16.15. カーネルの実行

16.15.1. enqueue_nd_range_kernel

Note

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

pyopencl.enqueue_nd_range_kernel(
    queue, #(1)
    kernel, #(2)
    global_work_size, #(3)
    local_work_size, #(4)
    global_work_offset=None, #(5)
    wait_for=None, #(6)
    g_times_l=False) #(7)

(1)

有効なコマンドキューを指定。

(2)

有効なカーネルオブジェクトを指定。

(3)

要素数work_dimの配列で、カーネル関数を実行するwork_dim次元のグローバルワークアイテムの個数を指定。

(4)

要素数work_dimの配列で、カーネル関数を実行する各ワークグループを構成するワークアイテムの数(ワークグループのサイズとも呼ぶ)を指定。

(5)

要素数work_dimの配列でワークアイテムのグローバルIDを決定する際のオフセット値を指定。

(6)

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

(7)

global_work_sizeをlocal_work_sizeで乗じた値を使います。

enqueue_nd_range_kernel関数を実装する際に一番判断に迷うのは、以下の3つの引数です。

  • global_offset
  • global_work_size
  • local_work_size

オフセットは作業したいデータの位置や、アルゴリズムの設計により恣意的に開発者が決めるものですが、原則として(0,0,0)で支障はないかと思います。

グローバルワークサイズ引数についても同様ですが、一般にワークサイズは2の冪乗としてください。これはプロセッサの演算ユニットをなるべき多く使うための最も基本的な処方箋です。プロセッサを有効に使うためには、できるだけ多くのグローバルワークサイズ引数があったほうが良いです。

ローカルワークサイズ引数は、IntelのGPUであれば64〜128個とします。ローカルワークサイズを決定する際に覚えておきたい点は、ローカルワークサイズは、ワークグループ数を決めるということです。

例えば1024個のグローバルワークサイズに対して、128個のワークグループ数を考えてみましょう。この場合のワークグループ数は8個(1024/128)となります。しかしローカルワークサイズが大きいと、共有ローカルメモリのサイズが肥大化して、推奨されるメモリ使用量の閾値(Intelは4K)を超える可能性が高くなります。

Note

詳細は 「ワークグループ数とワークグループサイズの最適化」(Table 13.15, “ワークグループ数とワークグループサイズの最適化”) を参照ください。

実装例

clSample.py. 

import pyopencl as cl
import numpy as np

data = np.arange(16).astype(np.int32)

ctx = cl.Context([cl.get_platforms()[0].get_devices()[0]])
queue = cl.CommandQueue(ctx)
mf = cl.mem_flags
data_mem = cl.Buffer(ctx, mf.USE_HOST_PTR, hostbuf=data)

program = cl.Program(ctx, """
    __kernel void square(__global int* data) {
        size_t gid = get_global_id(0);
        data[gid] *= data[gid];
    }
    """).build()

program.square(queue, (16,), (4,), data_mem)
# program.square(queue, np.array([16,1,1]), np.array([1,1,1]) None)

cl.enqueue_copy(queue, data, data_mem)

print(data)

出力. 

[0   1   4   9  16  25  36  49  64  81 100 121 144 169 196 225]

16.15.2. enqueue_task

Note

詳しくは「表:clEnqueueTask」(Table B.89, “表:clEnqueueTask”)、(タスク並列プログラミング)を参照ください。

enqueue_taskはenqueue_nd_range_kernel関数がデータ並列プログラミングを指すのと対比して、異なるタスクを並列で処理させるために使うことができます。

通常のマルチスレッドプログラミングのOpenCLバージョンと考えて頂くと分かりやすいかと思います。

pyopencl.enqueue_task(
    queue, #(1)
    kernel, #(2)
    wait_for=None) #(3)

(1)

有効なコマンドキューを指定。カーネルはコマンドキューと関連付けられたデバイス上で実行のためにキューに入れられる。

(2)

有効なカーネルオブジェクト。

(3)

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

Copyright 2018-2019, by Masaki Komatsu