バッファオブジェクトのマップは、ホストメモリとデバイス(カーネルインスタンスからアクセス可能なメモリ)のデータ同期を行ないます。
外部(PCIe等)ボード上のGPUデバイスでマップをするのは負荷が高いため、GeForce/Tesla/Radeon/FuryといったNVIDIAやAMDのデバイスでは通常は行なわない処理です。
反面Intelの内蔵GPUボード、AMDのAPUでは、CPUとGPUがホストメモリを共有するため、バッファをコピーするより負荷を軽減する効果を持ちます。いわゆるゼロコピーなため、効率的な実行処理が可能となります。
enqueue_map_buffer関数はバッファオブジェクトの領域からホストアドレス空間へのマップを行いマップされた領域へのポインタを戻すコマンドを挿入します。マップされた領域は、バッファオブジェクトの領域と、ホストメモリで1対1で対応するコピーを保持します。
詳しくは「表:clEnqueueMapBuffer」(Table B.36, “表:clEnqueueMapBuffer”)と「表:cl_map_flags」(Table B.50, “表:cl_map_flags”)を参照ください。
関数の定義は以下のようになります。
pyopencl.enqueue_map_buffer( queue, #(1) buf, #(2) flags, #(3) offset, #(4) shape, #(5) dtype, #(6) order="C", strides=None, wait_for=None, #(7) is_blocking=True)
マップコマンドを挿入するコマンドキューを指定。 | |
有効なバッファオブジェクトを指定。command_queueとbuffer は同じOpenCLコンテキスト上で作成されている必要がある。 | |
マップの設定を行うビットフィールド。 | |
バッファオブジェクトのマップを行う際に、マップの開始位置をどれだけずらすかをバイトで指定。 | |
マップする領域のshapeを指定。 | |
マップするデータ型を指定。 | |
このコマンドが実行される前に完了していなければならないイベントを指定。 |
map_flagsはホスト、デバイスアドレスのマップに関してのビットフィールドとなり、以下のように定義されます。
class pyopencl.map_flags
ビットフィールドの種類には以下の3つがあります。
READ WRITE WRITE_INVALIDATE_REGION
enqueue_map_bufferは内蔵型GPUを使う場合は推奨設定となります。つまりCopyやReadと名前のつく関数よりも効率的なバッファへのアクセスが可能となります。
マップコマンドは同期を行いますが、いつ同期が行なわれるかタイミングの面で保証がありません。そのためカーネルコマンド実行後にマップをするのが一般的な用例となります。
この項目ではカーネルの実行をする例はありませんが、マップコマンドのキューへの挿入は、カーネル実行後に行なうようにしてください。マップはパフォーマンス的にボトルネックとなりえるので、不用意に使用しないでください。
以下がコード例となります。
input_map = cl.enqueue_map_buffer( queue, buf=out_mem, flags=cl.map_flags.WRITE, offset=0, shape=(16,), dtype=np.int32)
mapする変数(output)は手動でメモリ領域を確保できませんので、clEnqueueMapBufferが自動で領域を割り当てます。
それではclEnqueueMapBufferを実装したサンプルソースコードを見てみましょう。
output = np.arange(16).astype(np.int32) out_mem = cl.Buffer(ctx, mf.USE_HOST_PTR, hostbuf=output) input_map = cl.enqueue_map_buffer( queue, buf=out_mem, flags=cl.map_flags.WRITE, offset=0, shape=(16,), dtype=np.int32) print(input_map)
上記のプログラムの出力は以下のようになります。
(array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], dtype=int32), <pyopencl.cffi_cl.Event object at 0x103a6e748>)
Copyright 2018-2019, by Masaki Komatsu