[Updated 5/17/2012 for Visual Studio 11 Beta]
In Visual Studio 11 Beta, C++ AMP enables you to accelerate your applications using heterogeneous hardware such as GPUs.
If you are a .NET developer, you can still use C++ AMP in your applications. You’ll write most of your code in C#, the pieces to execute on the GPU in C++ AMP, and then use your favorite interop mechanism to bridge the gap. This blog post explains how to do just that, using P/invoke.
However, before attempting to call C++ AMP from C#, make sure that you have C++ AMP working on your machine. For example, please verify that you can run the C++ AMP “Hello, World” example.
The short story
Once you have C++ AMP working on your machine, the easiest way to start using it from C# is to open this sample project in Visual Studio 11 Ultimate and begin experimenting with the code.
The long story
If you have an existing application that you’d like to modify to use C++ AMP – or you’d like to understand how the sample is set up – you can follow the steps below. In summary, you need to take the following steps:
- Step 1: Open or create a C# project in Visual Studio 11.
- Choose the platform target as X86 (if you plan to write 32-bit C++ AMP code).
- Allow unsafe code in the project.
- Step 2: Add a C++ project to the solution.
- Create a Win32 DLL that will contain the C++ AMP code.
- Step 3: Add a build step to copy the Win32 DLL to the binaries of the managed project.
- Step 4: Add a dependency between the projects.
- Step 5: Write the C++ AMP code and the C# code.
- The C# code will P/invoke into the C++ AMP code.
The steps are explained in detail below.
Step 1: Open or create a C# project in Visual Studio 11
First, you need to open or create a C# project. The rest of the article assumes that the project is named HelloWorldCSharp and is created from the Visual C# Console Application template.
Note that you need Visual Studio 11 Ultimate to create a Console Application project because Visual Studio 11 Express can only create Metro style applications.
You may need to adjust two project settings:
- Assuming that the C++ AMP code we’ll write will be 32-bit, the platform target of the project should be set to X86 and not AnyCPU.
- The managed project must allow unsafe code (right-click HelloWorldCSharp project in Solution Explorer, click on the “Build” tab, and check “Allow unsafe code”).
Step 2: Add a C++ project to the solution
You also need to add a Win32 DLL that will contain the C++ AMP code. In this walkthrough, we’ll create a “Win32 Console Application” and name it “HelloWorldLib”:
After clicking Next, choose the “DLL” application type:
Step 3: Add a build step to copy the Win32 DLL to the managed project binaries
If you build the solution we have so far, both projects should build. However, HelloWorldLib.dll will not be copied to the HelloWorldCSharp binaries. We need HelloWorldLib.dll copied here, so that HelloWorldCSharp.exe can find it at runtime.
To get HelloWorldLib.dll to copy each time you build, you need to add a build step into the HelloWorldCSharp project. First, unload the HelloWorldCSharp project in Solution Explorer:
Then, you can edit HelloWorldCSharp.csproj in Visual Studio by right-clicking the project in Solution Explorer and selecting “Edit HelloWorldSharp.csproj”:
Insert the following XML just above the line that imports “Microsoft.CSharp.targets”:
<ItemGroup> <Content Include="..$(Configuration)HelloWorldLib.dll"> <Link>HelloWorldLib.dll</Link> </Content> </ItemGroup>
After reloading HelloWorldLib (the same way as you unloaded it), HelloWorldLib.dll should show up in the Solution Explorer.
In the Properties window, make sure “Copy to Output Directory” is set to “Copy if newer”, and “Build Action” is set to “Content”:
Step 4: Add a dependency between the projects
For better usability of the solution, it is nice to set the HelloWorldLib project as a dependency for the HelloWorldCSharp project. Then, whenever you build HelloWorldCSharp, HelloWorldLib should build as well.
Right-click HelloWorldCSharp in Solution Explorer and click Project Dependencies:
And then add a dependency on HelloWorldLib:
To verify that everything is building correctly, you can try to rebuild the solution. After rebuilding, the binaries folder for HelloWorldCSharp (e.g., HelloWorldCSharpHelloWorldCSharpbinDebug) should contain both HelloWorldCSharp.exe and HelloWorldLib.dll.
Step 5: Write the C++ AMP code and the C# code
Now, we should be ready to call into the C++ AMP code from C#. Modify HelloWorldLib.cpp as follows:
#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.extent, [=] (index<1> idx) restrict(amp) { dataView[idx] = dataView[idx] * dataView[idx]; }); // Copy data from GPU to CPU dataView.synchronize(); }
We just added a simple square_array function that squares an array using C++ AMP. Also, we decorated the function to export it from the DLL.
Now, let’s edit the managed HelloWorldCSharp application to call into the C++ AMP code. Modify Program.cs in HelloWorldCSharp project as follows:
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); } } }
… and that’s it! Now, you should be able to run the HelloWorldCSharp project and see your application call into C++ AMP code.
Note that this is a very simple example that demonstrates how to call a C++ AMP function from C#. The example is too naïve to demonstrate speedup – it contains too little work per data element and in total to benefit from GPU acceleration. An example of a workload that does demonstrate speedup is matrix multiplication, and here is a link to C++ AMP code for Matrix Multiplication.
0 comments