Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
CUDAプログラミング入門株式会社フィックスターズリードエンジニア 飯塚拓郎
たったひとつでない、  GPUプログラミングの冴えたやり方        ready-to-useなライブラリを使う        CUBLAS, CUFFT, CURAND, NPP, etc...ディレクティブ+半自動並列化コンパイラを使う...
たったひとつでない、     GPUプログラミングの冴えたやり方              ready-to-useなライブラリを使う✓ CUDAプログラミングのメリット              CUBLAS, CUFFT, CURAND, ...
CUDA入門 基礎編
CPU vs GPU   CPU                GPU  ∼数十コア           ∼数百コア複雑なコントロール部     単純なコントロール部
CUDAプログラミングモデル    Host & Device   CUDAアプリケーション                    CUDA CUDA API                 C Language            通信  ...
CUDAプログラミングモデル      Kernel → Thread → CUDA Core                                CUDAカーネルには                               各ス...
CUDAプログラミングモデル                Thread Hierarchy✓ CUDAのスレッド空間は階層化されている                                    Grid - 全体を構成するのが G...
CUDAプログラミング          Basic Workflow    CPU                   GPU1.GPUメモリ確保2.入力データ転送3.カーネル呼び出し          4.カーネル実行5.出力データ転送6.G...
CUDAプログラミング                    Basic CUDA API✓ GPUメモリ確保/破棄 - cudaMalloc(void** devPtr, size_t size) - cudaFree(void* devPt...
CUDA プログラミング           Basic CUDA C Language✓ CUDA C Languageとは?  - CUDAのカーネルを書くための言語  - 文法はほぼC/C++、ただし標準Cライブラリ等は使用できない✓ 文...
配列の足し算
----左から続く----#include <iostream>#include <vector>                        // 2. 入力データ転送// 4. カーネル実行                        ...
コンパイル&実行✓ コンパイル環境 - NVIDIA CUDA Driver、 NVIDIA CUDA Toolkitをインストール - CUDAコンパイラ nvccを使う   - CUDAヘッダファイルのインクルードや必要なライブラリのリンク...
CUDA入門 応用編
GPUのアーキテクチャは進化するPrev : GT200   Now : Fermi   Next : Kepler
その時CUDAプログラマに           何が起こったか?✓ プログラミングモデルが変わった - メモリ空間の統合によって  メモリコピー操作が不要になった✓ プログラミングの難易度が変わった - キャッシュによってメモリ局所性を  意識...
つまり?✓GPUアーキテクチャと共に、CUDAプログラミングも進化する✓CUDAでそこそこの性能を出すことは、どんどん簡単になってゆく
つまり?✓GPUアーキテクチャと共に、CUDAプログラミングも進化する✓CUDAでそこそこの性能を出すことは、どんどん簡単になってゆく
Unified Virtual Address Space✓ Fermiアーキテクチャ+CUDA4.0ではCPUとGPUのメモリ空間が統合された                  これにより・・・✓ CPUとGPUのメモリ転送をプログラム中に書か...
配列の足し算(簡単編)
#include <iostream>__global__void vecadd(float *a, float *b, float *c){  c[threadIdx.x] = a[threadIdx.x] + b[threadIdx.x];}in...
でも、例えば・・・ CPU               GPUMemory            Kernel                           たくさん         明らかに遅い            ループ
こうするべき    CPU               GPU   Memory            Memory                     Kernel明示的にDevice Memoryにキャッシュ
アーキテクチャを知ることが        なぜ重要か?✓今時のCUDAはデータフローが隠 されていて簡単にプログラミングできる          Great! But...✓遅いプログラムも簡単にかけてしまう
つまり?✓GPUアーキテクチャと共に、CUDAプログラミングも進化する✓CUDAでそこそこの性能を出すことは、どんどん簡単になってゆく        というよりも・・・
つまり?✓GPUアーキテクチャと共に、CUDAプログラミングも進化する✓CUDAでそこそこの性能を出すことは、どんどん簡単になってゆく
FermiアーキテクチャStreaming Multiprocessor                   Streaming Multiprocessor      CUDA Cores                           ...
I/O性能✓ PCI Express 低速       Streaming Multiprocessor  Streaming Multiprocessor  -   レイテンシ:∼10us, スループット:∼8GB/s  - CPU、チップセ...
CacheとShared Memory✓ 違い  - L1/L2キャッシュによるキャッシュは暗黙的に行われる  - Shared Memoryはカーネル中で明示的に使う✓ Shared Memoryの使い方  - 変数修飾子__shared__...
行列の掛け算
愚直な一手  __global__  void matmul_naive(float *a, float *b, float *c, int matrix_size)  {    const unsigned int xidx = blockIdx....
2x2ブロックの4x4行列の計算に        単純化してみる                      A   B                      C   D        ×         =A   :   &     C  ...
Shared Memoryでキャッシュ               (Step1) 1.0 1.0              1.0 1.0 1.0 1.0              1.0 1.0                  ×1.0 ...
Shared Memoryでキャッシュ               (Step2)          1.0 1.0          1.0 1.0                    ×                        1....
スマートな一手__global__void matmul_shared(float *a, float *b, float *c, int matrix_size){  const unsigned int xidx = blockIdx.x * b...
まとめ✓CUDAプログラミングは簡単です - プログラミング言語的にはC + α程度、単純なプログラムなら  使用するAPIも10個以内ですむ - 「既存のCのコードをとりあえず動かだけ」すなら移植も楽✓最適化方法はアプリ(問題の性質)に依存す...
Thank you !
Upcoming SlideShare
Loading in …5
×

NVIDIA Japan Seminar 2012

1,928 views

Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

NVIDIA Japan Seminar 2012

  1. 1. CUDAプログラミング入門株式会社フィックスターズリードエンジニア 飯塚拓郎
  2. 2. たったひとつでない、 GPUプログラミングの冴えたやり方 ready-to-useなライブラリを使う CUBLAS, CUFFT, CURAND, NPP, etc...ディレクティブ+半自動並列化コンパイラを使う PGI Fortran, OpenACC CUDAプログラミングする
  3. 3. たったひとつでない、 GPUプログラミングの冴えたやり方 ready-to-useなライブラリを使う✓ CUDAプログラミングのメリット CUBLAS, CUFFT, CURAND, NPP, etc... - 柔軟:GPUのロジックを自由に記述でき、細かい制御もできる - 高速:ロジックを命令レベルで最適化可能、データ転送のタイミングも自由ディレクティブ+半自動並列化コンパイラを使う - 最新:GPUの最新のFeatureはまずCUDAに反映される PGI Fortran, OpenACC CUDAプログラミングしよう!
  4. 4. CUDA入門 基礎編
  5. 5. CPU vs GPU CPU GPU ∼数十コア ∼数百コア複雑なコントロール部 単純なコントロール部
  6. 6. CUDAプログラミングモデル Host & Device CUDAアプリケーション CUDA CUDA API C Language 通信 CPU GPU
  7. 7. CUDAプログラミングモデル Kernel → Thread → CUDA Core CUDAカーネルには 各スレッドの処理を記述 CUDAスレッド群は CUDAカーネルを実行論理層 CUDAコアは物理層 CUDAスレッドを順次実行∼数十万ものスレッドを数百のコアで効率良く実行
  8. 8. CUDAプログラミングモデル Thread Hierarchy✓ CUDAのスレッド空間は階層化されている Grid - 全体を構成するのが Grid Block - Grid の中に複数の Block Thread - Thread の中に複数の Thread✓ なぜ? - どんな構成のGPUでも 同等スケールの性能を達成するため - 同期機構を提供しつつ、 スケーラビリティを担保できる
  9. 9. CUDAプログラミング Basic Workflow CPU GPU1.GPUメモリ確保2.入力データ転送3.カーネル呼び出し 4.カーネル実行5.出力データ転送6.GPUメモリ破棄
  10. 10. CUDAプログラミング Basic CUDA API✓ GPUメモリ確保/破棄 - cudaMalloc(void** devPtr, size_t size) - cudaFree(void* devPtr)✓ データ転送 - cudaMemcpy(void* dst, void* src, size_t size, esize cudaMemcpyKind kind)✓ カーネル呼び出し - kernel_function<<<grid_size, block_size>>>(...)
  11. 11. CUDA プログラミング Basic CUDA C Language✓ CUDA C Languageとは? - CUDAのカーネルを書くための言語 - 文法はほぼC/C++、ただし標準Cライブラリ等は使用できない✓ 文法要素 - __global__ void func(...)でfuncがカーネルとしてコンパイルされる - __device__ void func(...)でfuncがデバイス関数 (カーネルから呼び出せる関数)としてコンパイルされる
  12. 12. 配列の足し算
  13. 13. ----左から続く----#include <iostream>#include <vector> // 2. 入力データ転送// 4. カーネル実行 cudaMemcpy(d_a, &a[0], size*sizeof(float), cudaMemcpyHostToDevice);__global__ cudaMemcpy(d_b, &b[0], size*sizeof(float),void vecadd(float *a, float *b, float *c) cudaMemcpyHostToDevice);{ c[threadIdx.x] = a[threadIdx.x] dim3 grid_size = dim3(1, 1, 1); + b[threadIdx.x]; dim3 block_size = dim3(size, 1, 1);} // 3. カーネル呼び出しint main(int argc, char *argv[]){ vecadd<<<grid_size, const int size = 16; block_size>>>(d_a, d_b, d_c); std::vector<float> a(size, 1); std::vector<float> b(size, 1); // 5. 出力データ転送 std::vector<float> c(size, 0); cudaMemcpy(&c[0], d_c, size*sizeof(float), cudaMemcpyDeviceToHost); float *d_a, *d_b, *d_c; // 6. GPUメモリ破棄 // 1. GPUメモリ確保 cudaFree(d_a); cudaMalloc(&d_a, size*sizeof(float)); cudaFree(d_b); cudaMalloc(&d_b, size*sizeof(float)); cudaFree(d_c); cudaMalloc(&d_c, size*sizeof(float)); for (int i=0; i<size; ++i) { ----右へ続く---- std::cout << c[i] << std::endl; }
  14. 14. コンパイル&実行✓ コンパイル環境 - NVIDIA CUDA Driver、 NVIDIA CUDA Toolkitをインストール - CUDAコンパイラ nvccを使う - CUDAヘッダファイルのインクルードや必要なライブラリのリンクは 自動的にやってくれる✓ 実行環境 - *nix環境:cudart.so/dylibへのパスを環境変数LD_LIBRARY_PATHに追加
  15. 15. CUDA入門 応用編
  16. 16. GPUのアーキテクチャは進化するPrev : GT200 Now : Fermi Next : Kepler
  17. 17. その時CUDAプログラマに 何が起こったか?✓ プログラミングモデルが変わった - メモリ空間の統合によって メモリコピー操作が不要になった✓ プログラミングの難易度が変わった - キャッシュによってメモリ局所性を 意識しなくてよくなった✓ 最適化方法が変わった
  18. 18. つまり?✓GPUアーキテクチャと共に、CUDAプログラミングも進化する✓CUDAでそこそこの性能を出すことは、どんどん簡単になってゆく
  19. 19. つまり?✓GPUアーキテクチャと共に、CUDAプログラミングも進化する✓CUDAでそこそこの性能を出すことは、どんどん簡単になってゆく
  20. 20. Unified Virtual Address Space✓ Fermiアーキテクチャ+CUDA4.0ではCPUとGPUのメモリ空間が統合された これにより・・・✓ CPUとGPUのメモリ転送をプログラム中に書かなくて良くなった!✓ 複数GPUを使用する際のメモリ転送をプログラム中に書かなくて良くなった! まさに いいことづくめ
  21. 21. 配列の足し算(簡単編)
  22. 22. #include <iostream>__global__void vecadd(float *a, float *b, float *c){ c[threadIdx.x] = a[threadIdx.x] + b[threadIdx.x];}int main(int argc, char *argv[]){ const int size = 16; float *a, *b, *c; cudaMallocHost(&a, size*sizeof(float)); cudaMallocHost(&b, size*sizeof(float)); for (int i=0; i<size; ++i) { a[i] = b[i] = 1; c[i] = 0; } cudaMallocHost(&c, size*sizeof(float)); dim3 grid_size = dim3(1, 1, 1); dim3 block_size = dim3(size, 1, 1); vecadd<<<grid_size, block_size>>>(a, b, c); for (int i=0; i<size; ++i) std::cout << c[i] << std::endl; 注意:GT200アーキテクチャ すごく簡単
  23. 23. でも、例えば・・・ CPU GPUMemory Kernel たくさん 明らかに遅い ループ
  24. 24. こうするべき CPU GPU Memory Memory Kernel明示的にDevice Memoryにキャッシュ
  25. 25. アーキテクチャを知ることが なぜ重要か?✓今時のCUDAはデータフローが隠 されていて簡単にプログラミングできる Great! But...✓遅いプログラムも簡単にかけてしまう
  26. 26. つまり?✓GPUアーキテクチャと共に、CUDAプログラミングも進化する✓CUDAでそこそこの性能を出すことは、どんどん簡単になってゆく というよりも・・・
  27. 27. つまり?✓GPUアーキテクチャと共に、CUDAプログラミングも進化する✓CUDAでそこそこの性能を出すことは、どんどん簡単になってゆく
  28. 28. FermiアーキテクチャStreaming Multiprocessor Streaming Multiprocessor CUDA Cores CUDA CoresL1 Cache Shared L1 Cache Shared Memory Memory L2 Cache L2 Cache Device Memory PCI Express
  29. 29. I/O性能✓ PCI Express 低速 Streaming Multiprocessor Streaming Multiprocessor - レイテンシ:∼10us, スループット:∼8GB/s - CPU、チップセット、メインメモリ、PCIバスに性能が左右される CUDA Cores CUDA Cores✓ Device Memory 中速 Shared Shared - GDDR5、オンボード/オフチップ L1 Cache Memory L1 Cache Memory - レイテンシ:∼500cycle, スループット:100~200GB/s L2 Cache L2 Cache✓ L2 Cache 中高速 Device Memory (on board, off chip) - 768KB - レイテンシ:∼200cycle PCI Express I/O
  30. 30. CacheとShared Memory✓ 違い - L1/L2キャッシュによるキャッシュは暗黙的に行われる - Shared Memoryはカーネル中で明示的に使う✓ Shared Memoryの使い方 - 変数修飾子__shared__をつける __global__ void kernel(float *ptr) { __shared__ float buf[16]; Shared Memoryの宣言とロード buf[16] = ptr[threadIdx.x]; ... __syncthreads(); 同期命令 ...
  31. 31. 行列の掛け算
  32. 32. 愚直な一手 __global__ void matmul_naive(float *a, float *b, float *c, int matrix_size) { const unsigned int xidx = blockIdx.x * blockDim.x + threadIdx.x; const unsigned int yidx = blockIdx.y * blockDim.y + threadIdx.y; float accumulator = 0.0; for (int i=0; i<matrix_size; ++i) { accumulator += a[yidx*matrix_size+i] * b[i*matrix_size+xidx]; } c[yidx*matrix_size+xidx] = accumulator;1. 計算結果の行列Cの要素ごとに1スレッドを割り当てる - 16x16のスレッドからなる、(matrix_size/16)x(matrix_size/16)のブロッ ク2. xidx, yidxは行列Cの要素の添字になる
  33. 33. 2x2ブロックの4x4行列の計算に 単純化してみる A B C D × =A : & C : &B : & D : & ブロック内でデータを共有できれば 計算に必要な領域は少なくてすむ
  34. 34. Shared Memoryでキャッシュ (Step1) 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 ×1.0 1.0 1.0 1.0 2.0 2.0 ×1.0 1.0 1.0 1.0 2.0 2.0 Shared Memory
  35. 35. Shared Memoryでキャッシュ (Step2) 1.0 1.0 1.0 1.0 × 1.0 1.0 1.0 1.01.0 1.0 1.0 1.0 4.0 4.0 ×1.0 1.0 1.0 1.0 4.0 4.0 Shared Memory
  36. 36. スマートな一手__global__void matmul_shared(float *a, float *b, float *c, int matrix_size){ const unsigned int xidx = blockIdx.x * blockDim.x + threadIdx.x; const unsigned int yidx = blockIdx.y * blockDim.y + threadIdx.y; float accumulator = 0.0; for (int i=0; i<matrix_size; i+=16) { Shared Memoryの宣言とロー __shared__ float sub_a[16][16]; __shared__ float sub_b[16][16]; sub_a[threadIdx.y][threadIdx.x] = a[yidx*matrix_size+(i+threadIdx.x)]; sub_b[threadIdx.y][threadIdx.x] = b[(i+threadIdx.y)*matrix_size+xidx]; Shared Memoryへの __syncthreads(); for (int j=0; j<16; ++j) { accumulator += sub_a[threadIdx.y][j] * sub_b[j][threadIdx.x]; } Shared Memoryからの __syncthreads(); }
  37. 37. まとめ✓CUDAプログラミングは簡単です - プログラミング言語的にはC + α程度、単純なプログラムなら 使用するAPIも10個以内ですむ - 「既存のCのコードをとりあえず動かだけ」すなら移植も楽✓最適化方法はアプリ(問題の性質)に依存する、まずGPUアーキテクチャを理解しよう - メモリI/Oのコストは一見隠されてはいるものの、
  38. 38. Thank you !

×