opencl
OpenCLの基本設定
サーチ…
前書き
OpenCLを利用する前に、OpenCLを使用するためのコードを設定する必要があります。このトピックでは、プロジェクトでopenclを起動して実行する方法と、基本的なカーネルを実行する方法について説明します。例はC#ラッパーのOpenCL.NETに基づいていますが、ラッパーはOpenCLに抽象化を追加しないため、C / C ++でもほとんど変わりません。
C#での呼び出しは、次のようになります。 'Cl.GetPlatformIDs'。 CスタイルのOpenCL APIの場合は、 'clGetPlatformIDs'を呼び出し、C ++スタイルの場合は 'cl :: GetPlatformIDs'
備考
NVidia、AMD、IntelはOpenCLの実装が若干異なりますが、括弧の要件と暗黙のキャストに限定されています。 NVidiaは、メソッドの正しいオーバーロードを把握しようとしているときに、カーネルをクラッシュさせることがあります。この場合、GPUを支援するための明示的なキャストを提供するのに役立ちます。この問題は、ランタイムコンパイルされたカーネルで観察されました。
このトピックで使用されているコールの詳細については、Googleの「OpenCL」とそれに続く関数名で十分です。 Khronosグループには、Webサイトで利用可能なすべてのパラメータとデータ型に関する完全なドキュメントがあります。
ターゲットデバイスの初期化
OpenCLカーネルは、GPUまたはCPUで実行できます。これにより、顧客が非常に古くなったシステムを持つ代替ソリューションが可能になります。プログラマは、その機能をCPUまたはGPUに制限することもできます。
OpenCLを使い始めるには、 'Context'と 'Device'が必要です。どちらもOpenCL API(cl :: ContextまたはclContext&〜Deviceとも呼ばれます)によって定義された構造であり、使用されているターゲットプロセッサを定義します。
デバイスとコンテキストを取得するには、それぞれが複数のデバイスをホストできる使用可能なプラットフォームのリストを照会する必要があります。プラットフォームは物理的なGPUとCPUを表し、デバイスは含まれているコンピューティングユニットをさらに区別することができます。 GPUの場合、ほとんどのプラットフォームには1つのデバイスしかありません。しかし、CPUはそのCPU能力のほかに追加の統合GPUを提供するかもしれない。
コンテキストは、メモリ、コマンドキュー、異なるカーネルおよびプログラムを管理します。コンテキストは、単一のデバイスに限定することも、複数のデバイスを参照することもできます。
コーディングを開始する前の簡単なAPIノート:OpenCLを呼び出すたびに、戻り値またはref-value(Cのポインタ)のいずれかのエラー値が返されます。今すぐ始めましょう。
ErrorCode err;
var platforms = Cl.GetPlatformIDs(out err);
if(!CheckError(err, "Cl.GetPlatformIDs")) return;
foreach (var platform in platforms) {
foreach (var device in Cl.GetDeviceIDs(platform, DeviceType.Gpu, out err)) {
if(!CheckError(err, "Cl.GetDeviceIDs")) continue;
[...]
}
}
このコードスニペットは、システム上の利用可能なすべてのGPUデバイスを照会します。これで、リストに追加したり、最初の一致でコンテキストを直接開始することができます。 'CheckError(...)'関数は、エラーコードが成功したかどうか、または異なる値を持つかどうかをチェックし、いくつかのログを提供できる単純なユーティリティです。別の関数やマクロを使用することをお勧めします。なぜなら、それをたくさん呼び出すからです。
ErrorCodeは、C#のデータ型cl_intの単なる列挙型です.C / C ++では、int値と、あらかじめ定義されているエラー定数を比較できます(https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/)。 xhtml / errors.html
また、デバイスが必要な機能をすべてサポートしているかどうかを確認したい場合は、実行時にカーネルがクラッシュする可能性があります。あなたは、
Cl.GetDeviceInfo(_device, DeviceInfo.ImageSupport, out err)
この例では、イメージ関数を実行できるかどうかをデバイスに問い合わせます。次の最後のステップでは、収集されたデバイスからコンテキストを構築する必要があります。
_context = Cl.CreateContext(null, 1, new[] { _device }, ContextNotify, IntPtr.Zero, out err);
いくつかのものはここで起こっている。 C / C ++の場合、IntPtrはC#のポインタアドレスです。私はここで重要な部分に集中します。
- 2番目のパラメータは、使用するデバイスの数を定義します
- 3番目のパラメータは、これらのデバイスの配列(またはC / C ++のポインタ)です。
- 3番目のパラメータは、コールバック関数の関数ポインタです。この関数は、コンテキスト内でエラーが発生するたびに使用されます。
さらに使用するには、使用しているデバイスやコンテキストをどこかに保存する必要があります。
すべてのOpenCLインタラクションを終了したら、あなたは再びコンテキストをリリースする必要があります
Cl.ReleaseContext(_context);
カーネルのコンパイル
カーネルは実行時にターゲットデバイス上でコンパイルできます。そうするには、
- カーネルソースコード
- コンパイルするターゲットデバイス
- ターゲットデバイスで構築されたコンテキスト
クイック用語更新:プログラムには、一連のカーネルが含まれています。カーネルはそのファイルの異なる関数メンバーですが、プログラムは完全なC / C ++ / C#ソースファイルと考えることができます。
まず、ソースコードからプログラムを作成する必要があります。
var program = Cl.CreateProgramWithSource(_context, 1, new[] { source }, null, out err);
複数のソースファイルを1つのプログラムにまとめてコンパイルすることができます。これにより、さまざまなファイルにカーネルを持ち、一度にコンパイルすることができます。
次のステップでは、ターゲットデバイス上でプログラムをコンパイルする必要があります。
err = Cl.BuildProgram(program, 1, new[] { _device }, string.Empty, null, IntPtr.Zero);
エラーコードは、関数呼び出し自体が成功したかどうかを示していますが、コードが実際にコンパイルされたかどうかはわかりません。それを確認するには、いくつかの追加情報を照会する必要があります
BuildStatus status;
status = Cl.GetProgramBuildInfo(program, _device, ProgramBuildInfo.Status, out err).CastTo<BuildStatus>();
if (status != BuildStatus.Success) {
var log = Cl.GetProgramBuildInfo(program, _device, ProgramBuildInfo.Log, out err);
}
C / C ++の人々は最後にキャストを無視して、返された整数を対応する定数と比較することができます。
最初の呼び出しは、ビルドが実際に成功したかどうかをチェックします。そうでない場合は、ログを取得して、状況が間違っている場所を正確に確認することができます。さまざまなプラットフォームに関するいくつかの一般的な落とし穴の発言を参照してください。
プログラムがビルドされたら、コンパイルされたプログラムから異なるカーネルを抽出する必要があります。これを行うには、
_kernel = Cl.CreateKernel(_program, kernel, out err);
ここで 'kernel'はカーネル名の文字列です。あなたがカーネルを使い終わったら、あなたはそれをリリースする必要があります
Cl.ReleaseKernel(_kernel);
コマンドキューの作成
デバイス上で操作を開始するには、デバイスごとにコマンドキューが必要です。キューは、ターゲットデバイスに行ったさまざまなコールを追跡し、順番に保ちます。ほとんどのコマンドは、ブロックモードまたは非ブロックモードで実行することもできます。
キューを作成するのはかなり簡単です。
_queue = Cl.CreateCommandQueue(_context, _device, CommandQueueProperties.None, out err);
コマンドキューとの基本的なやりとりは、実行するさまざまな操作(たとえば、デバイスとの間でデータのコピーやカーネルの起動など)をエンキューすることです。
コマンドキューの使用が終了したら、キューを解放する必要があります。
Cl.ReleaseCommandQueue(_queue);
カーネルの実行
だから今、私たちは本当のものに来て、あなたのカーネルをパラレルデバイスで実行します。カーネルのディスパッチを完全に理解するために、ハードウェアの基本についてお読みください。
まずカーネルを実際に呼び出す前にカーネルの引数を設定する必要があります。これは経由で行われます
err = Cl.SetKernelArg(_kernel, $argumentIndex, $argument);
カーネルを起動する前にすべての引数を設定しないと、カーネルは失敗します。
実際にカーネルを起動する前に、「グローバルワークサイズ」と「ローカルワークサイズ」を計算する必要があります。
グローバルワークサイズは、GPUで起動されるスレッドの総数です。ローカルワークサイズは、各スレッドブロック内のスレッド数です。カーネルが特別な要件を必要としない場合は、ローカルワークサイズを省略することができます。しかし、現地の仕事の規模が与えられれば、グローバルな仕事の規模は現地の仕事の規模の倍数でなければならない。
ワークサイズは、1次元、2次元または3次元のいずれかであり得る。あなたが望む次元数の選択は、あなた次第です。あなたのアルゴリズムに最も適したものを選ぶことができます。
作業の規模を決めたので、カーネルと呼ぶことができます。
Event clevent;
err = Cl.EnqueueNDRangeKernel(_queue, _kernel, $dimensions, null, $globalWorkSize, $localWorkSize, 0, null, out clevent);
$ dimensionは希望する次元数を定義します。$ globalWorkSizeは、グローバルWork Sizeを持つサイズ$ dimensionの配列で、$ localWorkSizeの配列と同じものです。最後の引数は、現在実行中のカーネルを表すオブジェクトを与えます。