使用 OpenCL.Net 进行 C# GPU 并行编程
在 初探 C# GPU 通用計(jì)算技術(shù) 中,我使用 Accelerator 編寫了一個(gè)簡(jiǎn)單的 GPU 計(jì)算程序。也簡(jiǎn)單看了一些 Brahma 的代碼,從它的 SVN 最新代碼看,Brahma 要轉(zhuǎn)移到使用 OpenCL.Net 作為底層了,于是也去網(wǎng)上搜索了一下,發(fā)現(xiàn)了 OpenCL.Net 和另一個(gè)相關(guān)的項(xiàng)目 OpenCLTemplate。
?
看了一些它的代碼,頗像 DirectCompute 的風(fēng)格,其 GPU 程序是標(biāo)準(zhǔn) C 代碼,所以編寫和閱讀也容易一些,而 Host 程序是 C# 的,把 GPU 代碼字符串傳給編譯器進(jìn)行編譯,然后就可以在 C# 對(duì)它進(jìn)行調(diào)用,并且取回結(jié)果了。
?
安裝了 ati-stream-sdk-v2.1-vista-win7-64,折騰了一下它的例子程序 FirstOpenCLProgram,運(yùn)行時(shí)會(huì)拋出一個(gè) InvalidContext 的異常,到它的論壇去問,版主建議我安裝 ati 最新 driver 先,雖然覺得本本剛買沒幾天,應(yīng)該驅(qū)動(dòng)比較新,還是去安裝了最新的驅(qū)動(dòng),果然不再報(bào)異常了。只是如果直接引用 OpenCLTemplate 下的 OpenCL.NET.dll 和 OpenCLTemplate.dll 的話,在初始化的時(shí)候會(huì)報(bào)空指針;而引用 FirstOpenCLProgram 下的這兩個(gè) dll 的話,則初始化時(shí)會(huì)閃現(xiàn)好幾個(gè)控制臺(tái)窗口,但是后續(xù)都是正常的。
?
既然正常了,就還是以上次那個(gè)程序,來看看 OpenCL 的方式,會(huì)不會(huì)有更大的速度提升。
?
使用 OpenCL 的程序代碼如下:
?
?
代碼 private const int GridSize = 1024; private readonly float[] _map;private const string Code = @" __kernel void Test(__global float* v1) {int i = get_global_id(0);float p = v1[i];v1[i] = p * p * p / 4 + 194; }"; private readonly CLCalc.Program.Kernel _test; private readonly CLCalc.Program.Variable _vmap; private readonly CLCalc.Program.Variable[] _args; private readonly int[] _workers;public Form1() {InitializeComponent();_map = new float[GridSize * GridSize];for (int y = 0; y < GridSize; y++){for (int x = 0; x < GridSize; x++){_map[x * GridSize + y] = x * y;}}CLCalc.InitCL();CLCalc.Program.Compile(new[] { Code });_test = new CLCalc.Program.Kernel("Test");_vmap = new CLCalc.Program.Variable(_map);_args = new[] { _vmap };_workers = new[] { GridSize * GridSize };Render(); }private void Start_Click(object sender, EventArgs e) {var stopwatch = new Stopwatch();stopwatch.Start();_test.Execute(_args, _workers);_vmap.ReadFromDeviceTo(_map);var time = stopwatch.ElapsedMilliseconds;this.Text = time.ToString();Render(); }private void Render() {var workingBitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);for (int y = 0; y < pictureBox1.Height; y++){for (int x = 0; x < pictureBox1.Width; x++){workingBitmap.SetPixel(x, y, Color.FromArgb(-0x1000000 | (int)_map[x * 2 * GridSize + y * 2]));}}pictureBox1.Image = workingBitmap; }?
?
運(yùn)行程序,點(diǎn)擊 4 次按鈕,顯示圖形和前兩個(gè)程序相同,說明程序運(yùn)算正常,4 次時(shí)間為:8、8、7、8。比使用 Accelerator 的程序速度也快了 5 倍以上。
?
因?yàn)槭菢?biāo)準(zhǔn) C 程序,所以我們的自由度很大,我也在 OpenCL 下實(shí)現(xiàn)了一下 Life 游戲,C# 部分的代碼就不貼了,GPU 代碼如下:
?
?
代碼 #define width 512 #define length 262144int GetValue(__global int* v, int index) {if(index < 0 || index >= length){return 0;}return v[index] == 0 ? 0 : 1; };__kernel void Test(__global int* v) {int i = get_global_id(0);int topLeft = GetValue(v, i - width - 1);int top = GetValue(v, i - width);int topRight = GetValue(v, i - width + 1);int left = GetValue(v, i - 1);int current = GetValue(v, i);int right = GetValue(v, i + 1);int bottomLeft = GetValue(v, i + width - 1);int bottom = GetValue(v, i + width);int bottomRight = GetValue(v, i + width + 1);int liveNeighbors = topLeft + top + topRight + left + right + bottomLeft + bottom + bottomRight;if(current > 0){v[i] = ((liveNeighbors < 2) || (liveNeighbors > 3)) ? 0 : 255;}else{v[i] = (liveNeighbors == 3) ? 255 : 0;} };?
?
很長(zhǎng)時(shí)間不用 C,有很多像函數(shù)聲明順序等規(guī)則都忘了,好的一點(diǎn)是 OpenCL.Net 中還提供了 OpenCLCodeChecker,用來進(jìn)行代碼檢測(cè),同時(shí)也在側(cè)邊欄里提供了語言幫助,只是它的檢測(cè)稍嫌弱智,代碼稍微復(fù)雜,提示的錯(cuò)誤位置很奇怪,有時(shí)候告知編譯出錯(cuò),Log 里面卻沒有任何錯(cuò)誤信息。不過總體來說,幫助還是很大就是了。
?
這個(gè) Life 程序,有一個(gè)小 Bug,在于沒有判斷超出右邊界的代碼,所以如果左邊有生物,右邊雖然原來沒有生物,也會(huì)無中生有 :)
?
總的來說,用 OpenCL.Net 編程,感覺還是很愉快的。
總結(jié)
以上是生活随笔為你收集整理的使用 OpenCL.Net 进行 C# GPU 并行编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于swift5以上的SnapKit框架
- 下一篇: 初探 C# GPU 通用计算技术