初探 C# GPU 通用计算技术
在安装包里,包含了几个例子程序,比如著名的 Life 游戏,不过,Life 游戏,相对于刚接触 GPU 运算的我,还是稍显复杂了。于是简化一下,只是进行一些简单的计算,发现,DX9Target.ToArray 如果返回参数是 int 数组的话,则会爆出“未支持的操作”的异常,想想也对,显卡确实是精于浮点运算的。
本来,我以为,GPU 运算是 DirectX 11 才有的功能,但是 Accelerator 支持的却是 DirectX 9,想来 DirectX 11 支持的运算能力更高、方式更简单吧。
为了简单比较一下 CPU 和 GPU 的速度,也写了一个 .net 4 的并行运算的程序,因为 DX9Target 不支持 int,所以这里的数组也用 float,如下:
而使用 Accelerator 的代码如下:
用我的笔记本(CPU 为 Core i5 430, 显卡为 ATI 5650)测试,对它们两个程序,都点击几次 Start 按钮,发现运行 3 次左右,图片框会变成全黑,这时,普通并行程序运算速度变慢,而 GPU 程序运行速度无明显变化,普通并行程序 4 次值为:96,89,277,291,而 GPU 程序 4 次值为:71,40,35,50。单就这个测试来说,在我的电脑上,使用 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”的异常,看来还需要等它完善之后才能使用吧。