C# GPU 初探

初探 C# GPU 通用计算技术


首先感谢未经允许的转载!


GPU 的并行计算能力高于 CPU,所以最近也有很多利用 GPU 的项目出现在我们的视野中,在 InfoQ 上看到这篇介绍  Accelerator-V2  的文章,它是微软研究院的研究项目,需要注册后才能下载,感觉作为我接触 GPU 通用运算的第一步还不错,于是去下载了回来。

在安装包里,包含了几个例子程序,比如著名的 Life 游戏,不过,Life 游戏,相对于刚接触 GPU 运算的我,还是稍显复杂了。于是简化一下,只是进行一些简单的计算,发现,DX9Target.ToArray 如果返回参数是 int 数组的话,则会爆出“未支持的操作”的异常,想想也对,显卡确实是精于浮点运算的。

本来,我以为,GPU 运算是 DirectX 11 才有的功能,但是 Accelerator 支持的却是 DirectX 9,想来 DirectX 11 支持的运算能力更高、方式更简单吧。

为了简单比较一下 CPU 和 GPU 的速度,也写了一个 .net 4 的并行运算的程序,因为 DX9Target 不支持 int,所以这里的数组也用 float,如下:

01 private const int GridSize = 1024;
02 private float[] _map;
03  
04 public Form1()
05 {
06     InitializeComponent();
07     _map = new float[GridSize * GridSize];
08     for (int y = 0; y < GridSize; y++)
09     {
10         for (int x = 0; x < GridSize; x++)
11         {
12             _map[x * GridSize + y] = x * y;
13         }
14     }
15     Render();
16 }
17  
18 private void Start_Click(object sender, EventArgs e)
19 {
20     var stopwatch = new Stopwatch();
21     stopwatch.Start();
22     _map = _map.AsParallel().Select(p => p * p * p / 4 + 194).ToArray();
23     var time = stopwatch.ElapsedMilliseconds;
24     this.Text = time.ToString();
25     Render();
26 }
27  
28 private void Render()
29 {
30     var workingBitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
31  
32     for (int y = 0; y < pictureBox1.Height; y++)
33     {
34         for (int x = 0; x < pictureBox1.Width; x++)
35         {
36             workingBitmap.SetPixel(x, y, Color.FromArgb(-0x1000000 | (int)_map[x * 2 * GridSize + y * 2]));
37         }
38     }
39     pictureBox1.Image = workingBitmap;
40 }


而使用 Accelerator 的代码如下:

01 private const int GridSize = 1024;
02 private readonly DX9Target _target;
03 private float[,] _map;
04  
05 public Form1()
06 {
07     InitializeComponent();
08     _target = new DX9Target();
09     _map = new float[GridSize, GridSize];
10     for (int y = 0; y < GridSize; y++)
11     {
12         for (int x = 0; x < GridSize; x++)
13         {
14             _map[x, y] = x * y;
15         }
16     }
17     Render();
18 }
19  
20 private void Start_Click(object sender, EventArgs e)
21 {
22     var stopwatch = new Stopwatch();
23     stopwatch.Start();
24  
25     var p = new FloatParallelArray(_map);
26     p = p * p * p / 4 + 194;
27     _target.ToArray(p, out _map);
28  
29     var time = stopwatch.ElapsedMilliseconds;
30     this.Text = time.ToString();
31     Render();
32 }
33  
34 private void Render()
35 {
36     var workingBitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
37  
38     for (int y = 0; y < pictureBox1.Height; y++)
39     {
40         for (int x = 0; x < pictureBox1.Width; x++)
41         {
42             workingBitmap.SetPixel(x, y, Color.FromArgb(-0x1000000 | (int)_map[x * 2, y * 2]));
43         }
44     }
45     pictureBox1.Image = workingBitmap;
46 }


用我的笔记本(CPU 为 Core i5 430, 显卡为 ATI 5650)测试,对它们两个程序,都点击几次 Start 按钮,发现运行 3 次左右,图片框会变成全黑,这时,普通并行程序运算速度变慢,而 GPU 程序运行速度无明显变化,普通并行程序 4 次值为:96,89,277,291,而 GPU 程序 4 次值为:71,40,35,50。单就这个测试来说,在我的电脑上,使用 GPU 的程序,大概比普通并行程序快一倍左右吧。这个测试本身,其实不见得很公平,结果仅供参考。

不过,在 Accelerator 中的并行编程,明显感觉受到的约束很大,平常很容易的代码,要改成这种并行模式,需要花费很多力气,有些逻辑甚至无法实现。相对于 Accelerator,Brahma的代码写起来就容易得多,也更易于阅读,其 Life 游戏的例子程序读起来简单而清晰,可惜我编译了 Brahma v0.1 和 v0.4,在我的电脑上,DirectX 的例子程序没有效果,而 OpenGL 的例子程序则会报一个“The generated GLSL was invalid”的异常,看来还需要等它完善之后才能使用吧。


转自:http://llf.hanzify.org/studio/article/show/csharp_gpgpu

如果您想利用GPU来加速C#代码的执行,可以考虑使用CUDA(Compute Unified Device Architecture)平台。CUDA是NVIDIA开发的用于GPU加速计算的平台和编程模型,可以在C#中使用CUDA.NET库来访问CUDA功能。 要使用CUDA.NET库,您需要先安装CUDA开发工具包和CUDA.NET库。然后,您可以在C#中编写使用CUDA加速的代码。例如,您可以使用CUDA.NET库来实现矩阵乘法算法,从而加速计算。 以下是一个简单的示例代码,使用CUDA.NET库来计算两个矩阵的乘积: ``` using ManagedCuda; using ManagedCuda.BasicTypes; using ManagedCuda.VectorTypes; int N = 1024; float[,] a = new float[N, N]; float[,] b = new float[N, N]; float[,] c = new float[N, N]; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { a[i, j] = i * N + j; b[i, j] = j * N + i; c[i, j] = 0; } } using (CudaContext ctx = new CudaContext()) { // Allocate device memory CudaDeviceVariable<float> dev_a = a; CudaDeviceVariable<float> dev_b = b; CudaDeviceVariable<float> dev_c = c; // Define kernel parameters dim3 threadsPerBlock = new dim3(16, 16); dim3 numBlocks = new dim3(N / 16, N / 16); // Launch kernel CudaKernel kernel = ctx.LoadKernel("matrixMul", "matrixMul.cu", new dim3(16, 16), null); kernel.BlockDimensions = threadsPerBlock; kernel.GridDimensions = numBlocks; kernel.Run(dev_a.DevicePointer, dev_b.DevicePointer, dev_c.DevicePointer, N, N, N); // Copy result back to host memory dev_c.CopyToHost(c); } ``` 在上面的代码中,我们首先定义了两个矩阵a和b,然后使用CUDA.NET库来分配设备内存并将数据复制到设备内存中。接下来,我们定义了一个CUDA核函数,该函数将计算两个矩阵的乘积。最后,我们将结果从设备内存复制回主机内存。 请注意,上面的示例代码只是一个简单的示例,实际的CUDA加速代码可能需要更复杂的实现。但是,使用CUDA.NET库可以简化与CUDA交互的过程,并且可以帮助您更轻松地编写使用CUDA加速的C#代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值