ホストプログラムではメモリオブジェクトを継承したバッファオブジェクトの生成を行なうBufferを前の項目で解説しました。
バッファオブジェクトはカーネルに処理をさせたいデータを渡したり、カーネルから処理済みのデータを受け取ることを可能とします。
つまり以下の2点のような処理が必要となります。
OpenCLではenqueue_read_buffer/enqueue_write_bufferを使いホストメモリ-バッファオブジェクト間のデータ読み込み、書き込みを行います。
まずバッファオブジェクトからの読み込みから見てみましょう。
詳しくは「表:clEnqueueReadBufferとclEnqueueWriteBuffer」(Table B.31, “表:clEnqueueReadBufferとclEnqueueWriteBuffer”)を参照ください。
pyopencl.enqueue_read_buffer( queue, #(1) mem, #(2) hostbuf, #(3) device_offset=0, #(4) wait_for=None, #(5) is_blocking=True) #(6)
このうち❹ はオフセットが必要な場合、5はコマンドの処理順序を整理させる場合でなければNoneを設定します。
❷は、pyopencl.Bufferで生成したメモリオブジェクトを指定して、読み取った結果をコピーするホストメモリの領域を指定するポインタ(メモリオブジェクトで割当てたデータ型と同型を使う)を使います。
この項目で説明するenqueue_write_bufferとenqueue_read_bufferの実装例では引数のエントリは、hostbufを除けば同じとします。
cl.enqueue_write_buffer( queue=queue, mem=output_mem, hostbuf=data) #(1)
dataは書き込むデータのホストメモリ領域を指すポインタです。 |
cl.enqueue_read_buffer( queue=queue, mem=output_mem, hostbuf=output) #(1)
outputは読み込んだデータを保存するメモリ領域を指すポインタです。 |
それでは空のバッファに対して書き込みをしてそれを読み込むサンプルソースコードを見てみましょう。
以下の例では、配列要素にnumpy.rangeで設定した領域のデータにenqueue_write_bufferを用いてバッファに書き込みます。その上で、enqueue_read_bufferを使い書き込んだデータを読み取ります。
import pyopencl as cl import numpy as np ctx = cl.create_some_context() devices = ctx.get_info(cl.context_info.DEVICES) queue = cl.CommandQueue(ctx) input = np.arange(16).astype(np.int32) input[0:16:4] = 100 cl.enqueue_write_buffer( queue=queue, mem=output_mem, hostbuf=input) output_map = cl.enqueue_map_buffer( queue=queue, buf=output_mem, offset=0, flags=cl.mem_flags.HOST_WRITE_ONLY, shape=(16,), dtype=np.int32) print(output_map[0])
出力.
[100 1 2 3 100 5 6 7 100 9 10 11 100 13 14 15]
enqueue_copy関数はバッファオブジェクトで識別されたsrc_bufferからdst_bufferへの複製を行うコマンドを挿入します。
詳しくは「表:clEnqueueCopyBuffer」(Table B.33, “表:clEnqueueCopyBuffer”)を参照ください。
pyopencl.enqueue_copy( queue, #(1) src, #(2) dst, #(3) byte_count=-1, #(4) src_offset=0, #(5) dst_offset=0, #(6) wait_for=None) #(7)
複製コマンドを挿入するコマンドキューを指定。command_queue、src_buffer、dst_buffer と関連付けられたOpenCLコンテキストは同じとなる必要がある。 | |
複製元のバッファオブジェクト。 | |
複製先のバッファオブジェクト。 | |
複製するデータのサイズをバイトで指定。 | |
src_bufferから複製を行う際に、読み込み開始位置をどれだけずらすかを指定。 | |
dst_bufferへ複製を行う際に、書き込み開始位置をどれだけずらすかを指定。 | |
このコマンドが実行される前に完了していなければならないイベントを指定。 |
以下の実装例ではnumpy.arangeに上書きしたinput配列をinput_memバッファとします。そしてinput_memを、データが空のoutput_memバッファにコピーします。
import pyopencl as cl import numpy as np ctx = cl.create_some_context() devices = ctx.get_info(cl.context_info.DEVICES) input = np.arange(16).astype(np.int32) input[0:16:2] = 0 mf = cl.mem_flags input_mem = cl.Buffer( ctx, mf.USE_HOST_PTR, hostbuf=input) input_check = input_mem.get_host_array( shape=(16,), dtype=np.int32) print(input_check) output_mem = cl.Buffer( ctx, mf.ALLOC_HOST_PTR, size=input.nbytes) queue = cl.CommandQueue(ctx) cl.enqueue_copy( queue=queue, src=input_mem, dest=output_mem) output_check = np.arange(16).astype(np.int32) cl.enqueue_read_buffer( queue=queue, mem=output_mem, hostbuf=output_check) print(output_check)
出力.
[ 0 1 0 3 0 5 0 7 0 9 0 11 0 13 0 15] [ 0 1 0 3 0 5 0 7 0 9 0 11 0 13 0 15]
clEnqueueFillBuffer関数は指定したパターンサイズのパターンで、バッファオブジェクトをフィル(埋める)するコマンドを挿入します。
詳しくは「表:clEnqueueFillBuffer」(Table B.35, “表:clEnqueueFillBuffer”)を参照ください。
pyopencl.enqueue_fill_buffer( queue, #(1) mem, #(2) pattern, #(3) offset, #(4) size, #(5) wait_for=None) #(6)
フィルコマンドが挿入されるコマンドキューを指定。コマンドキューとバッファに関連付けられたOpenCLコンテキストは同じとなる必要がある。 | |
有効なバッファオブジェクトを指定。 | |
データパターンを指す値。 | |
フィルする領域のオフセット。 | |
バッファ内でフィルされる領域の位置をバイトで表した値を指定。 | |
このコマンドが実行される前に完了していなければならないイベントを指定。 |
以下の実装例では、input_memバッファオブジェクトに数値1からなるパターンを書き込みます。
import pyopencl as cl import numpy as np ctx = cl.create_some_context() devices = ctx.get_info(cl.context_info.DEVICES) queue = cl.CommandQueue(ctx) input = np.arange(16).astype(np.int32) mf = cl.mem_flags input_mem = cl.Buffer( ctx, mf.ALLOC_HOST_PTR, size=input.nbytes) cl.enqueue_fill_buffer(queue, mem=input_mem, pattern=np.int32(1), offset=0, size=input.nbytes) output = input.copy() cl.enqueue_read_buffer( queue=queue, mem=input_mem, hostbuf=output) print(output)
出力.
[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
Copyright 2018-2019, by Masaki Komatsu