В Visual Studio 11 Developer Preview, C++ AMP позволяет ускорить Ваши приложения, используя гетерогенное железо, такое как GPU.
Если Вы являетесь .NET-разработчиком, то все равно сможете использовать C++ AMP в Ваших приложениях. Большинство кода будет писаться на C#, лишь некоторые участки с помощью C++ AMP для его выполнения на GPU, затем использоваться любимый interop-механизм для связывания. Данный пост объяснит, как это сделать через P/invoke.
Однако перед попыткой вызвать C++ AMP из C#, убедитесь, что C++ AMP работает на Вашей машине. Пост VS 11 Developer Preview вместе с C++ AMP от Daniel Moth объясняет, как это сделать. Замечу, что C++ AMP может быть использован как из Visual Studio 11 Express, так и Visual Studio 11 Ultimate, впрочем, в данной статье мы будем использовать последний вариант.
Короткий путь
Как только C++ AMP заработает на Вашей системе, наиболее простым способом начать использовать его будет открытие примера проекта в Visual Studio 11 Ultimate и начать экспериментировать с ним.
Длинный путь
Если у Вас существует готовое приложение, которое хотите изменить для возможности использовать C++ AMP, или же хотите понять, как вышеприведенный пример был создан, тогда следуйте описанным ниже шагам. Вкратце, потребуется выполнить следующие действия:
- Шаг 1: Откройте или создайте C# проект в Visual Studio 11.
- Выберите целевую платформу как X86 (если планируется писать 32-битный C++ AMP код).
- Позвольте использование unsafe code в проекте.
- Шаг 2: Добавьте C++ проект к решению.
- Создайте Win32 DLL, который и будет содержать C++ AMP код.
- Шаг 3: Добавьте шаг сборки проекта для копирования Win32 DLL к бинарникам managed-проекта.
- Шаг 4: Добавьте зависимости между проектами.
- Шаг 5: Пишем C++ AMP и C# код.
- C# код будет использовать P/invoke для the C++ AMP кода.
Итак, перейдем к более подробному описанию необходимых шагов.
Шаг 1: Откройте или создайте C# проект в Visual Studio 11
Прежде всего, Вам необходимо открыть или создать C# проект. В остальной части статьи будет подразумеваться, что он назван как HelloWorldCSharp, и создано на основе шаблона Visual C# Console Application.
Замечу, что потребуется Visual Studio 11 Ultimate для создания консольного приложения, т.к. в Visual Studio 11 Express можно создавать только Metro-приложения.
Возможно, потребуется уточнить два параметра:
- Зная, что C++ AMP код будет 32-битным, целевая платформа проекта должна быть задана как X86, но никак не AnyCPU.
- Managed-проект должен позволять небезопасный код (правый клик на проекте HelloWorldCSharp в обозревателе решения, клик на вкладке Build, ставим выбор на “Allow unsafe code”).
Шаг 2: Добавьте C++ проект к решению
Нам также потребуется Win32 DLL, содержащий C++ AMP код. В данном примере, мы создадим “Win32 Console Application” и назовем как “HelloWorldLib”:
После нажатия Next, выберем тип приложения как “DLL”:
Шаг 3: Добавьте шаг сборки проекта для копирования Win32 DLL к бинарникам managed-проекта
При сборке решения хотелось бы, чтобы оба проекта собирались, не так ли? Однако, HelloWorldLib.dll не будет скопирован в папку с бинарниками HelloWorldCSharp. Нам необходимо так это сделать, что HelloWorldCSharp.exe смог бы найти во время выполнения.
Чтобы заставить HelloWorldLib.dll быть правильно скопированным каждый раз при сборке, необходимо добавить шаг сборки в проект HelloWorldCSharp. Сначала, выгрузите HelloWorldCSharp в обозревателе решений:
Далее, Вам потребуется отредактировать HelloWorldCSharp.csproj в Visual Studio ( правый клик на проекте, нажимаем “Edit HelloWorldSharp.csproj”):
Вставьте следующий XML прямо перед секцией “Microsoft.CSharp.targets”:
<ItemGroup>
<Content Include="..\$(Configuration)\HelloWorldLib.dll">
<Link>HelloWorldLib.dll</Link>
</Content>
</ItemGroup>
После повторной загрузки HelloWorldLib, HelloWorldLib.dll должна появится в обозревателе решений.
В окне свойств, убедитесь что значение “Copy to Output Directory” выбрано как “Copy if newer”, а также “Build Action” установлено как “Content”:
Шаг 4: Добавьте зависимости между проектами
Для большего удобства, зададим проект HelloWorldLib как зависимость для проекта HelloWorldCSharp. Потом, при сборке HelloWorldCSharp, произойдет сборки и HelloWorldLib.
Правый клик на HelloWorldCSharp и выберем Project Dependencies:
И далее добавим зависимость от HelloWorldLib:
Чтобы удостовериться в правильности сборки решения, произведите повторную сборку проекта, после этого папка бинарников HelloWorldCSharp (т.е., HelloWorldCSharp\HelloWorldCSharp\bin\Debug) должна содержать и HelloWorldCSharp.exe, и HelloWorldLib.dll.
Шаг 5: Пишем C++ AMP и C# код
Теперь мы готовы для вызова C++ AMP кода из C#. Изменим HelloWorldLib.cpp как показано ниже:
#include "stdafx.h"
#include "amp.h"
using namespace concurrency;
extern "C" __declspec ( dllexport ) void _stdcall square_array(float* arr, int n)
{
// Create a view over the data on the CPU
array_view<float,1> dataView(n, &arr[0]);
// Run code on the GPU
parallel_for_each(dataView.grid, [=] (index<1> idx) mutable restrict(direct3d)
{
dataView[idx] = dataView[idx] * dataView[idx];
});
// Copy data from GPU to CPU
dataView.synchronize();
}
Мы просто добавили функцию square_array, которая возводит в квадрат элементы массива, используя C++ AMP. Также, мы задекорировали функцию, чтобы экспортировать ее из DLL.
Теперь, отредактируем приложение HelloWorldCSharp для вызова C++ AMP кода. Изменим Program.cs в проекте HelloWorldCSharp как показано ниже:
using System;
using System.Runtime.InteropServices;
class Program
{
/// <summary>
/// Function defined in HelloWorldLib.dll to square an array using C++ AMP
/// </summary>
[DllImport("HelloWorldLib", CallingConvention = CallingConvention.StdCall)]
extern unsafe static void square_array(float* array, int length);
static unsafe void Main()
{
// Allocate an array
float[] arr = new[] { 1.0f, 2.0f, 3.0f, 4.0f };
// Square the array elements using C++ AMP
fixed (float* arrPt = &arr[0])
{
square_array(arrPt, arr.Length);
}
// Enumerate the results
foreach (var x in arr)
{
Console.WriteLine(x);
}
}
}
… вот и все! Сейчас Вы сможете запустить проект HelloWorldCSharp и увидеть работающий C++ AMP код через C#.
Замечу, что это весьма простой пример, демонстрирующий возможность вызова C++ AMP функции из C#. Пример слишком “наивный” для демонстрации скорости выполнения – он производит слишком мало действий над элементами данных с использованием возможностей GPU-акселерации. Пост C++ AMP code for Matrix Multiplication является примером, демонстрирующим ускорение при перемножении матриц.
От переводчика
Кроме C++ AMP существует еще один проект от Microsoft (его подразделения Research) – Accelerator, предоставляющий возможность параллельных вычислений как на многоядерных CPU, так и на GPU, поддерживающим DirectX 9 и выше.
Accelerator v2 представляет собой нативную C++ библиотеку с managed-оберткой.