PyOpenCL を用いた GPU 計算処理の構築と実行

導入

PyOpenCL は Python で OpenCL 環境を直接制御するためのライブラリです。ドキュメントの整備状況に限界があるため、実際の動作を理解するにはコードを実装しながら学習することが一般的です。ここでは、GPU 上でのベクトル演算を行うための基本的なプロトタイプと、その処理の流れについて解説します。

実装サンプル

以下のスクリプトは、CPU メモリ上の NumPy 配列データを GPU に転送し、カーネルで加算処理を行った後、結果を再度 Python 側に読み込む標準的な例です。

#!/usr/bin/env python3
import pyopencl as cl
import numpy as np

# データ準備:浮動小数点配列の生成
size = 10**7
lhs_array = np.random.rand(size).astype(np.float32)
rhs_array = np.random.rand(size).astype(np.float32)

# コンテキストとコマンドキューの初期化
# create_some_context は自動的に最適化されたコンテキストを選択します
ctx = cl.create_some_context()
cmd_q = cl.CommandQueue(ctx)

# メモリアロケートフラグ定義
mflags = cl.mem_flags

# ホスト側のデータをデバイス(GPU)側へコピー
buf_lhs = cl.Buffer(ctx, mflags.READ_ONLY | mflags.COPY_HOST_PTR, hostbuf=lhs_array)
buf_rhs = cl.Buffer(ctx, mflags.READ_ONLY | mflags.COPY_HOST_PTR, hostbuf=rhs_array)

# OpenCL プログムの定義とコンパイル
kernel_source = r"""
__kernel void compute_op(
    __global const float *lhs, __global const float *rhs, __global float *out)
{
  int gid = get_global_id(0);
  out[gid] = lhs[gid] + rhs[gid];
}
"""
prog = cl.Program(ctx, kernel_source).build()

# 出力用のバッファ確保
buf_out = cl.Buffer(ctx, mflags.WRITE_ONLY, lhs_array.nbytes)

# カーネルオブジェクトの取得
kernel_fn = prog.compute_op

# グローバルワーカーサイズを指定してカーネルを実行
kernel_fn(cmd_q, (size,), None, buf_lhs, buf_rhs, buf_out)

# 結果をホスト側に再取得
result_array = np.empty_like(lhs_array)
cl.enqueue_copy(cmd_q, result_array, buf_out)

# CPU 側での整合性確認(デバッグ目的)
assert np.allclose(result_array, lhs_array + rhs_array)

処理フローの分解

上記のスクリプトで示される OpenCL エコシステムの運用手順は、主に以下の 6 つの段階に分けられます。

  1. コンテキスト作成: cl.create_some_context() を使用して、実行環境を管理するセッションオブジェクトを作成します。
  2. キューの設定: 生成したコンテキストを使用して、命令を実行するキュー(CommandQueue)を確立します。
  3. メモリ転送: Python データ構造を、デバイスのメモリアドレス空間にマッピングされたバッファへ転送します。
  4. プログラムビルド: C 言語記述のクエリソースをコンパイルし、ビルド済みのプログラムオブジェクトを得ます。
  5. カーネル起動: ビルド済みオブジェクトから関数を取り出し、キューとパラメータを指定して実行します。
  6. 結果取得: 処理完了後、デバイスメモリ内の結果を再び Python 変数として読み込みます。

カーネル実行引数の詳細

実際にカーネル(例:kernel_fn)を呼び出す際、必要な引数は以下の順序となります。

  1. コマンドキュー: 実行のトリガーとなるキューオブジェクト。
  2. ワーカサイズ: タスクの実行規模を示すタプル(例:(100,))。多次元の場合は (dim_x, dim_y, ...) 形式です。この値は実用的なインデックス範囲を超えて設定することはできません。
  3. オフセット: ここでは通常 None が指定されます。

注意点として、get_global_id(0) で参照するインデックスがバッファ領域からはみ出ないよう、ワークサイズは正しく設定する必要があります。オーバーランすると実行時にエラーが発生します。

常见环境报错与对策

特に Linux サーバー等、ハードウェア環境やドライバの状態によっては、初期化段階で以下のような例外が発生することがあります。

pyopencl._cl.LogicError: clGetPlatformIDs failed: PLATFORM_NOT_FOUND_KHR

これは、システム上で動作可能な OpenCL プラットフォーム(GPU ドライバ等)が検出されなかったことを意味します。開発者環境では仮想化ツールの使用を試みるか、ベンダー提供の公式ドライバー(Intel や NVIDIA など)のインストールが必要になるケースがあります。

タグ: pyopencl OpenCL gpu-programming NumPy

7月8日 20:33 投稿