[ C++で開発 ]
主にGraphics Processing Unit(GPU)を演算処理に用い、GPUの持つ大量の演算器*1を活用して並列実行することで性能を向上させるフレームワークの1つがOpenCLです。
*1) 2010年代のATI RADEON5800系では、32bit浮動小数点演算器を1600個保有しています。
OpenCLは、演算処理プロセッサで実行するコードを記述するOpenCL C言語(C言語の並列拡張)と、アプリケーションプログラムから演算処理を依頼し結果を受け取る制御用のOpenCLランタイムAPIから構成されます。
使用するGPUの製造元が提供する開発キットを入手し、C/C++コンパイラと組み合わせて構築します。
OpenCLは共通仕様ですが、ここでは、Windows 7 (64bit版)上で、GPUはRADEON HD5770、開発キットにATI stream SDK v.2.2を使っています。
最初のサンプルコードは、CL/cl.hppにコメントで記載していたものです。
コード全体は以下です。
#define __CL_ENABLE_EXCEPTIONS #include <CL/cl.hpp> #include <iostream> const char* helloStr = "__kernel void hello(void) { }"; int main() { cl_int err = CL_SUCCESS; try { std::vector<cl::Platform> platforms; cl::Platform::get(&platforms); if (platforms.size() == 0) { std::cerr << "Platform size 0" << std::endl; return -1; } cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0}; cl::Context context(CL_DEVICE_TYPE_GPU, properties); std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>(); cl::Program::Sources source(1, std::make_pair(helloStr, strlen(helloStr))); cl::Program program = cl::Program(context, source); program.build(devices); cl::Kernel kernel(program, "hello", &err); cl::Event event; cl::CommandQueue queue(context, devices[0], 0, &err); queue.enqueueNDRangeKernel( kernel, cl::NullRange, cl::NDRange(4, 4), cl::NullRange, NULL, &event ); event.wait(); } catch (cl::Error err) { std::cerr << "ERROR: " << err.what() << "(" << err.err() << ")" << std::endl; } return EXIT_SUCCESS; }
#define __CL_ENABLE_EXCEPTIONS #include <CL/cl.hpp>
OpenCLライブラリで例外を有効にするため、1行目のデファイン文を記述します。
OpenCLのC++ APIを使用するため、2行目のインクルード文を記述します。
なお、この記述はWindows/Linuxの場合で、MacOSの場合は<OpenCL/cl.hpp>となります。
const char* helloStr = "__kernel void hello(void) { }";
演算用プロセッサで実行するコードをOpenCL C言語のソースで記述しchar* で参照します。
ここではインラインで記述していますが、通常は別ファイルに記述しておいて、ファイルを読み込んでchar*で参照するでしょう。
std::vector<cl::Platform> platforms; cl::Platform::get(&platforms);
OpenCLが実行できるプラットフォームの一覧を取得します。
cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0}; cl::Context context(CL_DEVICE_TYPE_GPU, properties);
先に特定したプラットフォーム上での実行環境OpenCLコンテクストを作成します。コンテクストには、使用したいデバイス種類を指定します。
std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
先に作成したコンテクストからデバイスを取得します。
cl::Program::Sources source(1, std::make_pair(helloStr, strlen(helloStr))); cl::Program program = cl::Program(context, source); program.build(devices);
OpenCLデバイスで実行するプログラムを作成します。プログラム(ソースコード)を文字列(char*)で参照し、実行するコンテキストと合わせてプログラムオブジェクトを生成します。そして、コンパイルします。
cl::Kernel kernel(program, "hello", &err);
1つのカーネルは、1つの関数に対応します。第2引数はカーネル関数名を指定します。
cl::CommandQueue queue(context, devices[0], 0, &err);
アプリケーション側からデバイスに制御コマンドを送るために使用するコマンドキューを作成します。
queue.enqueueNDRangeKernel( kernel, cl::NullRange, cl::NDRange(4, 4), cl::NullRange, NULL, &event );
カーネル関数をコマンドキューに投入します。