Pull to refresh

Как использовать C++ AMP из C#

Reading time5 min
Views12K
Original author: Igor Ostrovsky

В 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-оберткой.
Tags:
Hubs:
+23
Comments8

Articles

Change theme settings