最もシンプルなOpenCLによる演算の実行 - 1
About
C#(.Net) から OpenCL を利用して演算を行う、最もシンプルなパターンを解説します。最もシンプルなパターンとは言え、OpenCL は元々 C/C++ から利用することを想定していることから、それなりの手順を踏む必要があります。少々長くなるので、この内容はいくつかの記事に分割します。
OpenCLSharp_SimpleOpenCLTask.zip
- VisualStudio 2012
- .NET Framework 4.5
- OpenCL 1.2
処理の内容
ここでは乗算処理を取り扱うことにします。FHD の解像度(1920*1080)のデータを処理することを想定して、次のような標準的なサンプルプログラムを用意します。ここではこのサンプルプログラムの処理を、OpenCL によって( GPU で)処理を実行させることを目標とします。
※このサンプルプログラムのプロジェクトも、配布するサンプルの中に含まれています。
class Program { static void Main() { float[] xDataArray = new float[1920]; float[] yDataArray = new float[1080]; float[] rDataArray = new float[1920 * 1080]; for (int i = 0; i < xDataArray.Length; i++) xDataArray[i] = 3.1415f; for (int i = 0; i < yDataArray.Length; i++) yDataArray[i] = 3.1415f; for (int y = 0; y < yDataArray.Length; y++) for (int x = 0; x < xDataArray.Length; x++) { rDataArray[x + y * xDataArray.Length] = xDataArray[x] * yDataArray[y]; } //計算結果の出力 for (int i = 0; i < 100; i++) Console.WriteLine(rDataArray[i]); Console.ReadLine(); } }
OpenCLによる処理全体の流れ
OpenCL による演算の実行について、ソースコードだけでは全体の流れを把握しにくいため、簡単な図を用意しました。適宜参照しながらソースコードを読むと、流れが把握しやすいかと思います。 |
OpenCLプラットフォームとデバイスの取得
まずは演算するデータを初期化して用意しておきます。次いで、OpenCL による準備に取り掛かります。まずは利用したいプラットフォームとそのデバイスを取得します。特定のプラットフォームとデバイスを取得するサンプルは「特定のOpenCLプラットフォームやデバイスを取得する」のページにも用意しています。
//演算するデータの用意 float[] xDataArray = new float[1920]; float[] yDataArray = new float[1080]; float[] rDataArray = new float[1920 * 1080]; for (int i = 0; i < xDataArray.Length; i++) xDataArray[i] = 3.1415f; for (int i = 0; i < yDataArray.Length; i++) yDataArray[i] = 3.1415f; int errcode; //プラットフォームの取得 IntPtr[] platforms = new IntPtr[1]; uint platformCount; errcode = clGetPlatformIDs(1, platforms, out platformCount); if (errcode != CL_SUCCESS) throw new Exception("Error at clGetPlatformIDs : " + errcode); //デバイスの取得 IntPtr[] devices = new IntPtr[1]; uint deviceCount; errcode = clGetDeviceIDs (platforms[0], CL_DEVICE_TYPE_DEFAULT, 1, devices, out deviceCount); if (errcode != CL_SUCCESS) throw new Exception("Error at clGetDeviceIDs : " + errcode);
OpenCLコンテキストの生成
次に OpenCL による処理の実行に必要な情報を管理する、OpenCL コンテキストを生成します。OpenCL コンテキストの生成には、clCreateContext 関数を利用します。
- 引数やその他の詳細などはサンプルプロジェクトないし公式から確認してください。
//コンテキストの生成 IntPtr context = clCreateContext (null, 1, devices, null, IntPtr.Zero, out errcode); if (errcode != CL_SUCCESS) throw new Exception("Error at clCreateContext : " + errcode);
'コンテキスト'の概念が分かりにくい場合、用意しなければならないものとして覚えてしまっても、プログラムの開発に当たっては大きな問題にならないと思います。
実行可能な OpenCL プログラムの生成
次に実行したい処理内容を OpenCL プログラムオブジェクトとして生成します。ふつう、OpenCL のプログラムは、OpenCLC という C 言語を OpenCL 向けにした言語を利用して記述します。ここではそのソースコードを文字列として string[] sourceCode に用意しました。
用意した OpenCL(C) のソースコードは、プログラムから呼び出して実行するためにビルドされる必要があります。ここでは OpenCL のソースコードを読み込むために clCreateProgramWithSource 関数を利用し、読み込んだプログラムをビルドするために clBuildProgram 関数を利用します。
- 引数やその他の詳細などはサンプルプロジェクトないし公式から確認してください。
//OpenCLCによって記述されるソースコード string[] sourceCode = new string[] { "__kernel void\n"+ "mul(__global const float xDataArray[],\n"+ " __global const float yDataArray[],\n"+ " __global float rDataArray[])\n"+ "{\n"+ " for(int y = 0; y < 1080; y++)\n" + " for(int x = 0; x < 1920; x++)\n"+ " {\n"+ " rDataArray[x + y * 1920] = xDataArray[x] * yDataArray[y];\n"+ " }\n"+ "}\n" }; //プログラムの読み込み IntPtr program = clCreateProgramWithSource (context, (uint)sourceCode.Length, sourceCode, null, out errcode); if (errcode != CL_SUCCESS) throw new Exception("Error at clCreateProgramWithSource : " + errcode); //プログラムのビルド errcode = clBuildProgram(program, 1, devices, null, null, IntPtr.Zero); if (errcode != CL_SUCCESS) throw new Exception("Error at clBuildProgram : " + errcode);
実戦的な OpenCL を利用したプログラムを実装する際には、ソースコードは文字列として用意するのではなく、別のファイルなどに専用に用意して読み込むことになります。
ここまでのまとめ
- OpenCL を利用して並列処理を実行するための、プラットフォームとデバイスを用意する。
- OpenCL コンテキストを用意する。
- OpenCLC で書かれたプログラムを用意して、読み込んで、ビルドする。