4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonAdvent Calendar 2024

Day 10

PythonからCUDAを利用してベクトル加算を行う方法

Last updated at Posted at 2024-12-09

TL;DR

本記事では、PythonからCUDAを利用して大規模なベクトル加算を行う方法について解説します。

CUDAのコードはC++と似ているため、C++の基礎知識があると比較的理解しやすいです。PythonからCUDAを利用するためには、ctypesを使って共有ライブラリをロードし、データを渡す必要があります。

コードはこちらです:makinzm/cuda-from-python

1. CUDAコードの解説(vector_add.cu

まず、CUDAで実装されたベクトル加算のコードを見ていきましょう。

ヘッダファイルのインクルード

#include <cuda_runtime.h>

extern "C"による関数のエクスポート

extern "C" void vector_add(float *a, float *b, float *c, int n);
  • extern "C":

  • void vector_add(float *a, float *b, float *c, int n);:

    • ベクトル加算を行う関数のプロトタイプ宣言です。
    • 引数:
      • float *a: ベクトルAのデータ
      • float *b: ベクトルBのデータ
      • float *c: 結果を格納するベクトルC
      • int n: ベクトルの要素数
    • *は配列の先頭を指すポインタを表します。

CUDAカーネル関数の定義

__global__ void vector_add_kernel(float *a, float *b, float *c, int n) {
    int idx = threadIdx.x + blockDim.x * blockIdx.x;
    if (idx < n) {
        c[idx] = a[idx] + b[idx];
    }
}
  • __global__:

  • void vector_add_kernel(float *a, float *b, float *c, int n):

    • GPU上で実行されるベクトル加算カーネル関数です。
  • 変数の解説:

  • ロジック:

    • 各スレッドは一つの要素を処理します。
    • if (idx < n)で、範囲外アクセスを防ぎます。
    • c[idx] = a[idx] + b[idx];で対応する要素の加算を行います。

ホスト関数の実装

void vector_add(float *a, float *b, float *c, int n) {
    float *d_a, *d_b, *d_c;
    size_t size = n * sizeof(float);

    // デバイスメモリの割り当て
    cudaMalloc((void**)&d_a, size);
    cudaMalloc((void**)&d_b, size);
    cudaMalloc((void**)&d_c, size);

    // ホストからデバイスへデータを転送
    cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);

    // カーネルの起動
    int threadsPerBlock = 256;
    int blocksPerGrid = (n + threadsPerBlock - 1) / threadsPerBlock;
    vector_add_kernel<<<blocksPerGrid, threadsPerBlock>>>(d_a, d_b, d_c, n);

    // デバイスからホストへ結果を転送
    cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);

    // デバイスメモリの解放
    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_c);
}

2. CUDAコードのコンパイル方法

nvcc --shared -Xcompiler -fPIC -o libvector_add.so vector_add.cu
  • nvcc:

  • オプションの解説:

    • --shared:
      • 共有ライブラリ(.soファイル)を生成します。
    • -Xcompiler -fPIC:
      • コンパイラにポジション独立コード(PIC)を生成するよう指示します。共有ライブラリを作成する際に必要です。
    • -o libvector_add.so:
      • 出力ファイル名を指定します。
    • vector_add.cu:
      • コンパイル対象のCUDAソースファイルです。

Ref: C++ユーザーの為のリンクの話2 #C++ - Qiita

3. Pythonコードの解説(main.py

PythonからCUDAで作成した共有ライブラリを呼び出すコードを見ていきましょう。

共有ライブラリのロード

import numpy as np
import ctypes

# 共有ライブラリをロード
lib = ctypes.cdll.LoadLibrary('./libvector_add.so')
  • ctypesモジュール:

    • PythonからC言語で書かれた共有ライブラリを呼び出すための標準ライブラリです。
  • LoadLibrary関数:

    • 指定した共有ライブラリをロードします。

関数の引数と戻り値の型指定

lib.vector_add.argtypes = [
    np.ctypeslib.ndpointer(dtype=np.float32, flags='C_CONTIGUOUS'),
    np.ctypeslib.ndpointer(dtype=np.float32, flags='C_CONTIGUOUS'),
    np.ctypeslib.ndpointer(dtype=np.float32, flags='C_CONTIGUOUS'),
    ctypes.c_int
]
lib.vector_add.restype = None

データの準備と関数の呼び出し

# データの準備
N = 1000000
a = np.random.rand(N).astype(np.float32)
b = np.random.rand(N).astype(np.float32)
c = np.zeros(N, dtype=np.float32)

# CUDA関数の呼び出し
lib.vector_add(a, b, c, N)
  • データの準備:

    • N = 1000000:
      • 要素数100万のベクトルを生成します。
    • np.random.rand(N):
      • 0から1の間の乱数を生成します。
    • .astype(np.float32):
      • データ型を32ビット浮動小数点数に変換します。
  • 関数の呼び出し:

    • lib.vector_add(a, b, c, N):
      • 先ほど定義したCUDA関数を呼び出します。

結果の検証

# 結果の検証
if np.allclose(c, a + b):
    print("計算結果は正しいです。")
else:
    print("計算結果に誤りがあります。")

4. まとめ

本記事では、PythonからCUDAを利用してベクトル加算を行う方法について解説しました。CUDAコードでは、GPUメモリの管理やカーネル関数の起動方法など、CUDA特有の文法とロジックを説明しました。Pythonコードでは、ctypesを用いてCUDAで作成した共有ライブラリをロードし、データを渡して計算結果を取得する方法を示しました。

5. 感想

久々にCUDAを触りましたが、ポインタなどの低レベルな操作が必要なため、C++の基礎知識があると理解しやすいと感じました。また、Pythonとの連携も意外とかんたんにできることを知ったので、今後も活用していきたいです。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?