CUDA プログラミング CUDAで「Hello World」

CUDA 10.1をインストールし、サンプルプログラムをビルドしてみました。
CUDA プログラミング CUDA 10.1のインストールと付属サンプルのビルド&実行

実際にプログラムを作成してみます。
こちらを参考に「Hello World」を表示してみます。
Tutorial 01: Say Hello to CUDA


nvcc



コンパイルには「nvcc」というコマンドを実行します。
実体は「/usr/local/cuda-10.1/bin/nvcc」
パスが通っていなかったので設定しておきました。


$ vi ~/.bash_profile



内容は
https://docs.nvidia.com/cuda/cuda-quick-start-guide/index.html
上記の記載に従います。


export PATH=/usr/local/cuda-10.1/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-10.1/lib64\${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}



内容を反映。


$ source ~/.bash_profile



これで準備完了です。



Hello World



チュートリアルに、C言語との比較が掲載されています。

・C言語


  1. void c_hello(){
  2.     printf("Hello World!\n");
  3. }
  4. int main() {
  5.     c_hello();
  6.     return 0;
  7. }



・CUDA


  1. __global__ void cuda_hello(){
  2.     printf("Hello World from GPU!\n");
  3. }
  4. int main() {
  5.     cuda_hello<<<1,1>>>();
  6.     return 0;
  7. }



「__global__」はGPU呼び出し。
「<<<...>>>」の部分は次節で説明とのことなので、そういうものだと思っておきます。
「main」がkernelsと呼ばれる部分での実行になるとのこと。

https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html
ここの解説を見ると、kernelsからGPU関数の呼び出しが行われるようです。

CUDA Tutorial
こちらを見ると、kernelsはhost(≒CPU)で実行されるという理解で良いのかもしれません。

CUDAのサンプルプログラムを「hello.cu」という名前で保存します。

・hello.cu


  1. #include <stdio.h>
  2. __global__ void cuda_hello(){
  3.     printf("Hello World from GPU!\n");
  4. }
  5. int main() {
  6.     cuda_hello<<<1,1>>>();
  7.     return 0;
  8. }



nvccでコンパイル。


$ nvcc hello.cu -o hello



実行してみます。


$ ./hello



...が何も表示されず。
特にエラーも表示されないので、これが正しい実行結果なのでしょうか。
とりあえずチュートリアルを先に進めます。



GPUによる演算



同じ長さの2つの配列の中身を加算し、1つの配列にまとめる処理をサンプルとします。

・vector_add.c


  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define N 10000000
  4. void vector_add(float *out, float *a, float *b, int n) {
  5.     for(int i = 0; i < n; i++){
  6.         out[i] = a[i] + b[i];
  7.     }
  8. }
  9. int main(){
  10.     float *a, *b, *out;
  11.     // Allocate memory
  12.     a = (float*)malloc(sizeof(float) * N);
  13.     b = (float*)malloc(sizeof(float) * N);
  14.     out = (float*)malloc(sizeof(float) * N);
  15.     // Initialize array
  16.     for(int i = 0; i < N; i++){
  17.         a[i] = 1.0f; b[i] = 2.0f;
  18.     }
  19.     // Main function
  20.     vector_add(out, a, b, N);
  21.     printf("%f\n", out[0]);
  22. }



ビルドして実行


$ g++ vector_add.c -o vector_add_c
$ ./vector_add_c
3.000000



このプログラムをGPU版に変更してみます。

・vector_add.cu


  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <cuda.h>
  4. #include <cuda_runtime.h>
  5. #define N 10000000
  6. __global__ void vector_add(float *out, float *a, float *b, int n) {
  7.     for(int i = 0; i < n; i++){
  8.         out[i] = a[i] + b[i];
  9.     }
  10. }
  11. int main(){
  12.     float *a, *b, *out;
  13.     float *d_a, *d_b, *d_out;
  14.     // Allocate memory
  15.     a = (float*)malloc(sizeof(float) * N);
  16.     b = (float*)malloc(sizeof(float) * N);
  17.     out = (float*)malloc(sizeof(float) * N);
  18.     // Initialize array
  19.     for(int i = 0; i < N; i++){
  20.         a[i] = 1.0f; b[i] = 2.0f;
  21.     }
  22.     
  23.     // Allocate device memory
  24.     cudaMalloc((void**)&d_a, sizeof(float) * N);
  25.     cudaMalloc((void**)&d_b, sizeof(float) * N);
  26.     cudaMalloc((void**)&d_out, sizeof(float) * N);
  27.     // Transfer data from host to device memory
  28.     cudaMemcpy(d_a, a, sizeof(float) * N, cudaMemcpyHostToDevice);
  29.     cudaMemcpy(d_b, b, sizeof(float) * N, cudaMemcpyHostToDevice);
  30.     // Executing kernel
  31.     vector_add<<<1,1>>>(d_out, d_a, d_b, N);
  32.     // Transfer data back to host memory
  33.     cudaMemcpy(out, d_out, sizeof(float) * N, cudaMemcpyDeviceToHost);
  34.     printf("%f\n", out[0]);
  35.     // Deallocate device memory
  36.     cudaFree(d_a);
  37.     cudaFree(d_b);
  38.     cudaFree(d_out);
  39.     // Deallocate host memory
  40.     free(a);
  41.     free(b);
  42.     free(out);
  43. }



ビルドと実行


$ nvcc vector_add.cu -o vector_add_cuda
$ ./vector_add_cuda
3.000000



あまり意味のない計算ですが、動いてくれたようです。


なんとなく理解したことは
・devide(GUP)で演算に使用するメモリは「cudaMalloc」で確保する。
・メモリの開放は「cudaFree」
・cudaMemcpyで、kernel領域のメモリをdevice領域にコピーして利用する。
・deviceの演算結果はcudaMemcpyでdevice領域からkernel領域にコピーして利用する。

次回はもう少し実用的な演算を試してみたいと思います。



【参考URL】
Tutorial 01: Say Hello to CUDA
CUDA Tutorial
https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html
https://docs.nvidia.com/cuda/cuda-samples/index.html
https://docs.nvidia.com/cuda/cuda-quick-start-guide/index.html

関連記事

コメント

非公開コメント

プロフィール

Author:symfo
blog形式だと探しにくいので、まとめサイト作成中です。
Symfoware まとめ

PR




検索フォーム

月別アーカイブ