7.1. デバイスメモリへのコピー

PyOpenCLではホストメモリから、デバイスメモリへのコピーを以下の2つの方法で行うことができます。

Arrayクラスのコンストラクタは以下のように定義されています。

class pyopencl.array.Array(
    cq,
    shape,
    dtype,
    order='C',
    allocator=None,
    data=None,
    offset=0,
    strides=None,
    events=None)

cqはpyopencl.CommandQueueまたはpyopencl.Contextのいずれかのインスタンスとなります。shapeとdtypeはnumpyと同じように操作が可能であり、他の引数はオプションで特殊な事例でなければ使う機会はないと考えます。

Warning

筆者が検証した限りPyOpenCLのArrayを使った実装ライブラリはnumpyの処理速度を超えることはありませんでした。PyOpenCLの並列アルゴリズムライブラリではいくらか緩和されますが、やはりnumpyの実行処理速度を超えませんでした。他方、pyopencl.BufferとOpenCLのバインディングライブラリ(ランタイムライブラリ)を用いた独自実装では、配列の規模が十分に大きければ(例:配列要素数 > 524288)常にnumpyの実行処理速度の数倍、数十倍以上のパフォーマンスを上げることができました。

下記のコードはArrayクラスのインスタンスを生成して、d_gpuが指すデバイスが管理するメモリ空間に10個の要素を持つ配列を作ります。

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr

d_gpu = clarr.Array(queue, (10,), dtype=np.uint32)
print((d_gpu+2).get())

出力は以下のようになります。

出力. 

[2 2 2 2 2 2 2 2 2 2]

符号なし32ビット整数(uint32)型の各要素に2を足しています。この加算はOpenCLデバイスで行われており、Deviceオブジェクトの種類ににGPUを選択すればGPU上で処理がなされます。

Arrayオブジェクトにデータを代入する場合はsetメソッドを使えばできます。以下のソースコードでは、空のインスタンスに整数のデータ配列を設定しています。

CLSetArrayTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a = np.arange(5).astype(np.uint32)

a_mem = clarr.Array(queue, (5,), dtype=np.uint32)
a_mem.set(a)

print(a_mem.get())

上記のプログラムの出力は以下のようになります。

出力. 

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLSetArrayTest.py
[0 1 2 3 4]

7.1.1. デバイスメモリ複製のユーティリティ関数

次にArrayクラスのコンストラクタを経由しないで、デバイスメモリにデータをコピーする、バッファのファクトリメソッドであるto_deviceもご案内したいと思います。to_deviceメソッドの定義は以下のようになります。

pyopencl.array.to_device(
    queue,
    ary,
    allocator=None,
    async=False,
    array_queue=<class 'pyopencl.array._same_as_transfer'>)

queueはpyopencl.CommandQueueのインスタンス、aryはnumpyで宣言した配列とします。

ではto_deviceメソッドを用いた実装例を見てみましょう。以下のCLAdditionTest.pyでは、numpyで生成したfloat32(32ビット浮動小数点型)の配列をデバイスメモリにコピーします。

CLAdditionTest.py. 

import numpy as np
import pyopencl as cl
import pyopencl.array as clarr
import warnings

import os
os.environ['PYOPENCL_COMPILER_OUTPUT'] = '1'

a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).astype(np.float32)

platforms = cl.get_platforms()
ctx = cl.Context([platforms[0].get_devices(cl.device_type.GPU)[0]])
queue = cl.CommandQueue(ctx)

a_gpu = clarr.to_device(queue, a) #(1)

result = (a_gpu + 10).get()
print(result)

(1)

a_gpuにデバイスにコピーしたデータを保管。a_gpuはGPUのメモリ空間にあることになります。

上記のプログラムの出力は以下のようになります。

/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 /Users/komatsu/PycharmProjects/MyPythonProject/CLAdditionTest.py
[ 11.  12.  13.  14.  15.  16.  17.  18.  19.  20.]

各要素に対して整数10を加算していることが確認できました。

注意すべき点としてto_deviceで生成したデバイスメモリと、Bufferで生成したデバイスメモリについては、用途が異なるため、そのままの状態でバッファを扱うことができません。Arrayクラスのインスタンス変数となるdata属性からBufferを抽出が可能です。

Copyright 2018-2019, by Masaki Komatsu