How to use C++ AMP from C#

How to use C++ AMP from C#

  • Comments 7

[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., HelloWorldCSharp\HelloWorldCSharp\bin\Debug) 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.

Attachment: HelloWorldCSharp.zip
Leave a Comment
  • Please add 1 and 2 and type the answer here:
  • Post
  • Thanks, great! Hower I have one problem. Debugging is not working. Do you know why?

  • Sorry, now I am just reading that GPU debugging is not supported on Win7.

  • See: social.msdn.microsoft.com/.../46da1c69-cf2f-4f24-8d83-531d67af1848

  • So, this line doesn't compile at all in Visual Studio 11 (Ultimate) Beta and Windows 7:

    parallel_for_each(dataView.grid, [=] (index<1> idx) mutable restrict(direct3d)

    error C2039: 'grid' : is not a member of 'Concurrency::array_view<_Value_type,_Rank>'

    error C3936: 'direct3d' : unrecognized restriction specifier

    What's the frequency, Kenneth?

  • Outdated sample, I guess, since the documentation would seem to agree with the compiler's assessment. Perhaps working samples could be provided? I'm sure we'd all appreciate that.

  • SB, I'll get in touch with Igor about updating the sample.

    In the meantime, please see changes to the Beta on our blog here:

    blogs.msdn.com/.../changes-in-vs-11-beta-for-c-amp.aspx

    As you can see there, you should change

    grid to extent,

    and change

    restrict(direct3d) to restrict(amp).

    You can probably delete the word mutable, that is rarelly needed.

  • SB, et al.  The attached sample and code within the post have been updated to use the Visual Studio 11 Beta syntax.

Page 1 of 1 (7 items)