Main Content

cuBLAS の例

この例では、cuBLAS ライブラリを使用して 2 つの行列 AB を乗算します。汎用行列-行列乗算 (GEMM) の MATLAB® 実装は次のようになります。

function [C] = blas_gemm(A,B)

    C = zeros(size(A));
    C = A * B;
end

生成された CUDA コード

CUDA® コードを生成すると、GPU Coder™ は cuBLAS ライブラリを初期化する関数呼び出しを作成し、行列-行列演算を実行して、cuBLAS ライブラリで使用されたハードウェア リソースを解放します。以下は、生成された CUDA コードのスニペットです。

   cublasEnsureInitialization();
  blas_gemm_kernel1<<<dim3(2048U, 1U, 1U), dim3(512U, 1U, 1U)>>>(gpu_C);
  alpha1 = 1.0;
  beta1 = 0.0;
  cudaMemcpy((void *)gpu_alpha1, (void *)&alpha1, 8ULL, cudaMemcpyHostToDevice);
  cudaMemcpy((void *)gpu_A, (void *)A, 8388608ULL, cudaMemcpyHostToDevice);
  cudaMemcpy((void *)gpu_B, (void *)B, 8388608ULL, cudaMemcpyHostToDevice);  
  cudaMemcpy(gpu_beta1, &beta1, 8ULL, cudaMemcpyHostToDevice);
  cublasDgemm(cublasGlobalHandle, CUBLAS_OP_N, CUBLAS_OP_N, 1024, 1024, 1024,
             (double *)gpu_alpha1, (double *)&gpu_A[0], 1024, (double *)&gpu_B
              [0], 1024, (double *)gpu_beta1, (double *)&gpu_C[0], 1024);
  cublasEnsureDestruction();
  cudaMemcpy((void *)C, (void *)gpu_C, 8388608ULL, cudaMemcpyDeviceToHost);

cuBLAS ライブラリを初期化し、cuBLAS ライブラリ コンテキストのハンドルを作成するために、関数 cublasEnsureInitialization()cublasCreate() cuBLAS API を呼び出します。これにより、ホストとデバイスのハードウェア リソースが割り当てられます。

static void cublasEnsureInitialization(void)
{
  if (cublasGlobalHandle == NULL) {
    cublasCreate(&cublasGlobalHandle);
    cublasSetPointerMode(cublasGlobalHandle, CUBLAS_POINTER_MODE_DEVICE);
  }
}

blas_gemm_kernel1 は、結果の行列 C を 0 に初期化します。このカーネルが起動し、1 ブロックあたり 512 スレッドを含む 2048 個のブロックが用意されます。これらのブロックとスレッドの値は C のサイズに対応します。

static __global__ __launch_bounds__(512, 1) void blas_gemm_kernel1(real_T *C)
{
  int32_T threadIdX;
  threadIdX = (int32_T)(blockDim.x * blockIdx.x + threadIdx.x);
  if (!(threadIdX >= 1048576)) {
    C[threadIdX] = 0.0;
  }
}

cudaMemcpy の呼び出しは、行列 A および B をホストからデバイスに転送します。関数 cublasDgemm は、次の行列-行列乗算を実行するレベル 3 の Basic Linear Algebra Subprogram (BLAS3) です。

C = αAB + βC

ここで、α および β はスカラーです。また、A、B、および C は列優先形式で保存された行列です。CUBLAS_OP_N は入力行列に対する転置演算を制御します。

最後は、cublasEnsureDestruction() の呼び出しと、もう 1 つの cudaMemcpy の呼び出しです。cublasEnsureDestruction() は、cublasCreate() cuBLAS API を呼び出し、cuBLAS で使用されたハードウェア リソースを解放します。cudaMemcpy は結果の行列 C をデバイスからホストにコピーします。

static void cublasEnsureDestruction(void)
{
  if (cublasGlobalHandle != NULL) {
    cublasDestroy(cublasGlobalHandle);
    cublasGlobalHandle = NULL;
  }
}

カーネル作成用の blas_gemm の準備

GPU Coder は、ライブラリの呼び出しを生成するために特殊なプラグマを必要としません。CUDA カーネルを生成する方法は 2 つあります。coder.gpu.kernelfun および coder.gpu.kernel です。この例では、coder.gpu.kernelfun プラグマを利用して CUDA カーネルを生成します。変更した関数 blas_gemm は次のようになります。

function [C] = blas_gemm(A,B) %#codegen
    C = coder.nullcopy(zeros(size(A));

    coder.gpu.kernelfun;
    C = A * B;
end

メモ

算術演算子および関数を cuBLAS ライブラリ実装に置き換える場合、入力データには最小サイズ (128 要素) が必要です。