Yasuoka Laboratory

【3年生の皆様へ】

泰岡研究室はいつでも見学を歓迎しております。気軽に学生居室(25棟319室)までお越しください!

既に訪問したい時間帯が決まっている方は こちらのフォーム(要keio.jp認証)からアポイントメントを取ることも可能です。


3. CUDAプログラミングの第一歩

ここでは二つのベクトルの和の計算(C=A+B)を例にとってCUDAプログラミング例を示す.まず,CPUで実行される通常のC言語でのサンプルコードを以下に示す.


// main.c

#include <stdio.h>
#define NVEC (10000)

int main(){
  int a[NVEC], b[NVEC], c[NVEC], i; // declare all variables

  for (i = 0; i < NVEC; i++) a[i] = i * 2; // initialize A
  for (i = 0; i < NVEC; i++) b[i] = NVEC - i; // initialize B
  
  for (i = 0; i < NVEC; i++) c[i] = a[i] + b[i]; // calculate C = A + B
  return 0;
}

これと同じことをGPUで動かすためにCUDAでこれから書くわけだが,ここではC=A+Bを計算する部分を他のファイルに分離する.ここでは,gpu_addvecという関数をCUDAで書くことにする.



// followings are written in main.c

#include <stdio.h>
#define NVEC (10000)
#define GPUFLAG (1)

extern void gpu_addvec(int*, int*, int*);

int main(){
  int a[NVEC], b[NVEC], c[NVEC], i; // declare all variables

  for (i = 0; i < NVEC; i++) a[i] = i * 2; // initialize A
  for (i = 0; i < NVEC; i++) b[i] = NVEC - i; // initialize B
  
  if (GPUFLAG == 1){
    gpu_addvec(c,a,b);
  }else{  
      for (i = 0; i < NVEC; i++) c[i] = a[i] + b[i]; // calculate C = A + B
  }
  return 0;
}

// followings are written in addvec.cu

#include <stdio.h>
#define NVEC (10000)

extern "C" void gpu_addvec(int* c, int* a, int* b){
  
}

これからgpu_addvecの中身書いていく.まず必要なのは,ビデオメモリへの情報の転送である.ここでは,ビデオメモリ上に置く配列を宣言,アロケートし,メインメモリ-ビデオメモリ間の情報の転送を行い,最後にビデオメモリ上に置く配列を解放している.また,cutil.hというヘッダをインクルードしている.このファイルは
/usr/local/cuda/bin/nvcc --compile addvec.cu -I$CUDA_SDK_PATH/common/inc
という手続きでオブジェクトファイルに変換される.(64 bit Mac OS Xなどでは-m64を付ける必要あり)$CUDA_SDK_PATHはSDKがインストールされている場所のパスであり,環境変数に書いてもよい.また,/usr/local/cudaにtoolkitがインストールされているものとしている.そして実行ファイルは
gcc main.c addvec.o -L/usr/local/cuda/lib -lcuda -lcudart -lm -lstdc++
という手続きで生成される.



// followings are written in addvec.cu

#include <stdio.h>
#include <cutil.h>
#define NVEC (10000)

extern "C" void gpu_addvec(int* c, int* a, int* b){
  int *a_dev, *b_dev, *c_dev; // declare arrays for GPU computation
  unsigned int memsize = sizeof(int) *NVEC; // memory size of each array

  cudaMalloc((void**)&a_dev,memsize); // allocate arrays on video memory (global memory)
  cudaMalloc((void**)&b_dev,memsize);
  cudaMalloc((void**)&c_dev,memsize);

  cudaMemcpy(a_dev,a,memsize,cudaMemcpyHostToDevice); // transfer data to video memory
  cudaMemcpy(b_dev,b,memsize,cudaMemcpyHostToDevice);
  
  cudaMemcpy(c,c_dev,memsize,cudaMemcpyDeviceToHost); // transfer data from video memory

  cudaFree(a_dev);
  cudaFree(b_dev);
  cudaFree(c_dev);
}

次に,GPU上で走る各スレッドの実行する関数(カーネル)を考える.gpu_addvecにはそれを制御する命令を書かなければならないのだが,先にカーネルそのものの方を示す.__global__という修飾子はカーネルの関数につけられるものである.indexという変数の中身は次に触れるが,ここでは各スレッドごとに異なる値を持つものである.



// followings are written in addvec.cu

#define BLOCKSIZE (100)

__global__ void gpu_addvec_kernel(int *c_dev, int *a_dev, int* b_dev){
  int index = threadIdx.x + blockIdx.x * BLOCKSIZE;
  c_dev[index] = a_dev[index] + b_dev[index];
}

最後に,gpu_addvec_kernelの制御をgpu_addvecから行えば完成である.gpu_addvec_kernelの呼び出しの際に<<<grids,threads>>>というのが付いているが,これはスレッドの個数を指定するものである.CUDAではスレッドをブロックという単位で複数に束ねて実行するため,1ブロックあたりのスレッド数(ここではBLOCKSIZE)とブロックの総数(ここではNVEC/BLOCKSIZE)という情報が必要になる.gpu_addvec_kernelを呼び出す直前でこれらを指定している.また,gpu_addvec_kernel内でthreadIdx.xとblockIdx.xという変数を用いているが,これはそれぞれブロック内でのスレッドのIDおよびブロックのIDを示している.



// followings are written in addvec.cu

#include <stdio.h>
#define NVEC (10000)

extern "C" void gpu_addvec(int* c, int* a, int* b){
  int *a_dev, *b_dev, *c_dev; // declare arrays for GPU computation
  unsigned int memsize = sizeof(int) *NVEC; // memory size of each array

  cudaMalloc((void**)&a_dev,memsize); // allocate arrays on video memory (global memory)
  cudaMalloc((void**)&b_dev,memsize);
  cudaMalloc((void**)&c_dev,memsize);

  cudaMemcpy(a_dev,a,memsize,cudaMemcpyHostToDevice); // transfer data to video memory
  cudaMemcpy(b_dev,b,memsize,cudaMemcpyHostToDevice);

  dim3 threads(BLOCKSIZE);
  dim3 grids(NVEC/BLOCKSIZE);
  gpu_addvec_kernel<<<grids,threads>>>(c_dev,a_dev,b_dev);
  
  cudaMemcpy(c,c_dev,memsize,cudaMemcpyDeviceToHost); // transfer data from video memory

  cudaFree(a_dev);
  cudaFree(b_dev);
  cudaFree(c_dev);
}


コンテンツ

0. なぜGPUコンピューティングが注目を浴びているか
1. CUDAプログラミングを始める前に
2. インストールガイド
3. CUDAプログラミングの第一歩
ホーム