OpenCLのオブジェクトとOpenCLプログラムの構造
OpenCLでは以下のような「オブジェクト」が概念として定義されている。
デバイス(Device)はGPUやCPUのこと、コンテクスト(Context)はデバイスの集合のこと。コンテクストは、CPUやGPUといったデバイスの種類の違いに配慮して、その処理をどういうカーネルで処理するのか、メモリはどうするのかといったことを定義するものになる。
キュー(Queue)はデバイスに対してカーネルの実行、メモリ内容の転送、メモリオブジェクトの割り当て、演算結果の同期を促したり……と、様々な処理の実行を司る仕組みのこと。イメージ的にはコンテクストに届く指令書のようなもの。キューには実行順序を厳守する指令と、実行順序不問なものとがある。
バッファ(Buffer)はある一定量のメモリの固まりを指す。各カーネル(後述)はこのバッファへ自在なアクセスが許されており、配列としてアクセスしてもいいし、ポインタ経由でもいいし、構造体としてアクセスしてもいい。しかも、そのアクセスは読み込みでも書き出しでもいい。
イメージ(Image)は2Dや3Dのデータのことで、具体的には画像データやボリュームデータのようなものを指す。各カーネルはread_image()やwrite_image()のような専用命令経由でしかアクセスが出来ない。また、読み込み専用のイメージ、書き出し専用のイメージという定義がなされるため、読み書きは自由ではない。
プログラム(Program)はカーネルの集合を含み、そのプログラムで必要なデバイス・リスト、実効バイナリやソースをひとまとめにしたもの。
カーネル(Kernel)はプログラムにおける実行部隊に相当するものでデータに施す演算や処理の単位となる。カーネルの実際の実効そのものはデバイス側で行われる。
OpenCLは、データパラレルコンピューティングのためのプラットフォームだが、そのターゲットがGPUに限定されない。DSPであったり、マルチ/メニー・コアのCPUの場合もある。しかし、そのソフトウェア・アプリケーションのポータビリティは、なるべくならば身につけておきたい。
そこで、そのアプリケーションが、ターゲットプラットフォームで動作できるか、あるいはカーネルを駆動する単位をどうするかといった動的な動作計画を立てるための下調べのAPIが用意されている。
clGetDeviceIDs()は、その1つで、そのシステムでOpenCLから利用可能なデバイスの数、種別を得るもの。CPUか、GPUなのか、それともDSPなのか……といったことがこれで分かる。
clGetDeviceInfo()は、そのデバイスの演算リソースの使用を取得するもの。何コアなのか、ワークアイテム数は最大いくつ、ワークグループサイズはいくつに設定できるのか、各階層ごとのメモリ空間の容量はいくつなのか……具体的にはこうした情報が取得できる。こうした情報を元にアプリケーションの実行を臨機応変に変えられる設計にしてもいいし、ここで動作不可の判断をしてもいいことになる。
ここのプロセスはセットアップ(Setup)と名付けられていて、主にデバイスの確保、コンテクストの生成、キューの定義といった要素からなる。
OpenCLの場合、異種のプロセッサが混在する環境での実効を想定しているので(最も身近な例ではCPUとGPU)、プログラムのコンパイルは、動作対象のプロセッサごとに行われる。これも特徴的だ。
OpenCLでは、メモリ内容の一貫性を緩やかにした引き替えに並列性を高めるプログラミングモデルとなるために、並列動作させるカーネルの処理順序というものが重要になる。
イベント(event)は、そうした各カーネルの実行結果の取りまとめや、同期取りを行うものだ。
同一GPUコアに依存関係のあるカーネルを実行させる場合は先に実行されたカーネルの終了を待たなければならない。複数のデバイス(2つのGPU、CPUとGPUなど)で依存関係がある場合も同様だ。