16.3. バッファーの読み込み・書き込み・コピー

ホストプログラムではメモリオブジェクトを継承したバッファオブジェクトの生成を行なうBufferを前の項目で解説しました。

バッファオブジェクトはカーネルに処理をさせたいデータを渡したり、カーネルから処理済みのデータを受け取ることを可能とします。

つまり以下の2点のような処理が必要となります。

OpenCLではenqueue_read_buffer/enqueue_write_bufferを使いホストメモリ-バッファオブジェクト間のデータ読み込み、書き込みを行います。

16.3.1. enqueue_read_bufferとenqueue_write_buffer

まずバッファオブジェクトからの読み込みから見てみましょう。

Note

詳しくは「表: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)

(1)

コマンドキュー

(2)

読みとるバッファオブジェクト

(3)

読み取ったデータをコピーする先のホストポインタ

(4)

オフセット

(5)

待機イベントリスト

(6)

読みとりのブロックフラグ

このうち❹ はオフセットが必要な場合、5はコマンドの処理順序を整理させる場合でなければNoneを設定します。

❷は、pyopencl.Bufferで生成したメモリオブジェクトを指定して、読み取った結果をコピーするホストメモリの領域を指定するポインタ(メモリオブジェクトで割当てたデータ型と同型を使う)を使います。

実装例

この項目で説明するenqueue_write_bufferとenqueue_read_bufferの実装例では引数のエントリは、hostbufを除けば同じとします。

cl.enqueue_write_buffer(
    queue=queue,
    mem=output_mem,
    hostbuf=data) #(1)

(1)

dataは書き込むデータのホストメモリ領域を指すポインタです。

cl.enqueue_read_buffer(
    queue=queue,
    mem=output_mem,
    hostbuf=output) #(1)

(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]

16.3.2. enqueue_copy

enqueue_copy関数はバッファオブジェクトで識別されたsrc_bufferからdst_bufferへの複製を行うコマンドを挿入します。

Note

詳しくは「表: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)

(1)

複製コマンドを挿入するコマンドキューを指定。command_queue、src_buffer、dst_buffer と関連付けられたOpenCLコンテキストは同じとなる必要がある。

(2)

複製元のバッファオブジェクト。

(3)

複製先のバッファオブジェクト。

(4)

複製するデータのサイズをバイトで指定。

(5)

src_bufferから複製を行う際に、読み込み開始位置をどれだけずらすかを指定。

(6)

dst_bufferへ複製を行う際に、書き込み開始位置をどれだけずらすかを指定。

(7)

このコマンドが実行される前に完了していなければならないイベントを指定。

実装例

以下の実装例では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]

16.3.3. clEnqueueFillBuffer

clEnqueueFillBuffer関数は指定したパターンサイズのパターンで、バッファオブジェクトをフィル(埋める)するコマンドを挿入します。

Note

詳しくは「表:clEnqueueFillBuffer」(Table B.35, “表:clEnqueueFillBuffer”)を参照ください。

pyopencl.enqueue_fill_buffer(
    queue, #(1)
    mem, #(2)
    pattern, #(3)
    offset, #(4)
    size, #(5)
    wait_for=None) #(6)

(1)

フィルコマンドが挿入されるコマンドキューを指定。コマンドキューとバッファに関連付けられたOpenCLコンテキストは同じとなる必要がある。

(2)

有効なバッファオブジェクトを指定。

(3)

データパターンを指す値。

(4)

フィルする領域のオフセット。

(5)

バッファ内でフィルされる領域の位置をバイトで表した値を指定。

(6)

このコマンドが実行される前に完了していなければならないイベントを指定。

実装例

以下の実装例では、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