WINDOWSで始めるCUDA
エヌビディアジャパン CUDAエンジニア 森野慎也
GTC Japan 2014
内容
GPUのハードウエア構造を理解する
— GPU Diagram
— Compute Capability
CUDAのプログラミングモデルを理解する
— Grid, Block, Warp, Thread
Visual Studio + Nsightで、例題を実行する
— 配列の和
GPUの構造
Giga Thread Engine
— 処理を、SMに割り振る
GPU
Giga Thread Engine
PCI Express
SM
— 「並列」プロセッサ
L2 Cache
— R/W可能な二次キャッシュ
SM
SM
SM
L2 Cache
DRAM
— すべてのSMとPCI Expressから
アクセス可能なメモリ
PCI Express
— PC(ホスト)との接続インターフェース
DRAM
SM
…
KEPLER GK110 ブロックダイアグラム
アーキテクチャ
最大 15 SMX ユニット
SMX = KeplerのSM
71億トランジスタ
1 TFLOP以上の
倍精度演算性能
1.5 MB L2 Cache
384-bit GDDR5
STREAMING MULTIPROCESSOR EXTREME
Kepler
192 CUDA cores / SMX
GPU内部の並列プロセッサ
— SMXの単位で処理を実行。
— CUDA coreは、単体で動作しない。
COMPUTE CAPABILITY
GPUコアアーキテクチャのバージョン
— CUDA GPUs : https://developer.nvidia.com/cuda-gpus
アーキテクチャは進化する
— 低消費電力
— 高効率の命令実行
SM VS COMPUTE CAPABILITY
Instruction Cache
Scheduler
Scheduler
Dispatch
Dispatch
Register File
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Load/Store Units x 16
Special Func Units x 4
Interconnect Network
64K Configurable
Cache/Shared Mem
Uniform Cache
Fermi
CC 2.0 : 32 cores / SM
Kepler
CC 3.5 : 192 cores / SMX
Maxwell (1st gen)
CC 5.0 : 128 cores / SMM
CUDAプログラミングモデル
並列処理のみ実行可能
— 100万スレッド以上での並列動作
— Massively Parallel
SMアーキテクチャによらず、同じプログラムが動作する
— GPUハードウエアの共通概念
— プログラミングモデルとの対応
装置構成
GPU
PC
CPU
(数コア)
GPU: CPUにつながった
外部演算装置
Giga Thread Engine
制御
PCIe
ホスト側DRAM
SMX
SMX SMX SMX
L2 Cache
DRAM
転送
…
典型的な実行例
プログラム
開始
GPUはCPUからの制御
で動作する。
CPU
データ
転送
CUDA
カーネル
実行
完了
待ち
GPU
GPUでの演算
データ
転送
入力データは
CPU→GPUへと転送。
結果は、
GPU→CPUと転送
GPU上に常駐する
プログラムはない。
2.1 カーネル実行の階層
GPU
CPU
Grid
データ
転送
Thread
Thread
Thread
Thread
Block1
Thread
Thread
Thread
Thread
Block2
Thread
Thread
Thread
Thread
Block
n
CPUから呼び出される。
Blockにより構成される。
Block
Threadにより構成される。
Thread数は、Grid内部で一定
…
…
CUDA
カーネル
実行依頼
Grid
Block0
Thread
Thread
Thread
Thread
Thread
最小の実行単位
BLOCK は、SMX上で実行
プログラミングモデル
Grid
Block0
GPU
Block1
Block2
SMX
Block3
Block4
SMX
※ Blockの実行順序は保証されない。
※ 特定のSMXへのBlock割り当てはできない。
SMX
Block5
Block6
SMX
…
Block N
BLOCK は SMX上で実行
Block ⇒ 1 SMX
— 複数のSMXにまたがらない。
(SMX中では、複数Blockが実行される場合もある。)
— Block内部では、SMXのリソースを活用可能
各々のBlockは、独立に処理を実行する。
— 実行順序の保証はない。
— Block間の通信・同期は行わない。
STREAMING MULTIPROCESSOR EXTREME (略図)
192 Cores/SMX
Compute
Capability 3.5
SMX (簡略化しています)
レジスタ 64 K個 (256 KB)
Core
Core
Core
0
Core
1
2
3
Core
Core
Core
0
Core
1
2
3
Core
SFU LD/ST DP
Core Core Core Core
Core
Core
Core
Core
0
0
0
0
Core
Core
Core
Core
1
1
1
1
2
2
2
2
3
3
3
3
共有メモリ
L1 Cache
64 KB
テクスチャ
キャッシュ
48 KB
Core Core
15
15
Core
15
SFU
15
LD/ST
DP
15
15
SFU
Special Function Unit
LD/ST
Load/Store
DP
倍精度演算ユニット
WARP : 命令発行の単位
1命令を Warp が、32並列で処理
— SIMT (Single Instruction Multiple Thread)
Thread
Core
Core
Core
Core
Core
CUDA cores
Thread
Thread
Thread
Thread
Thread
SMX
…
32 GPU Thread
Warp
…
Warp
Block
Warp
SW
1命令を
32並列実行
CUDAプログラム実行の概要
SM(X)
CPU
Grid
Block
Block
Block
Warp (32 Thread)
Warp (32 Thread)
Warp (32 Thread)
Grid
カーネル全体、全てのBlockを含む
Block
SMX内部で、実行される。
Blockサイズは、Grid内で、一定。
Warp
命令発行の単位。32並列で実行。SIMT
Thread
GPUスレッド。ソースコードに対応。
SM
GPUの並列プロセッサ
プログラミングの基礎
ホストプログラミング
— メモリ転送、カーネルの実行
カーネルプログラミング
— GPU上の関数の実装
CUDA ホストAPI (抜粋)
機能
CUDA Runtime API
メモリ確保・解放 cudaError_t cudaMalloc(void ∗∗ devPtr, size_t size)
cudaError_t cudaFree(void *)
メモリ転送
cudaError_t
cudaMemcpy (void ∗ dst, const void ∗ src, size_t count,
enum cudaMemcpyKind kind)
同期
cudaError_t cudaDeviceSynchronize(void)
エラーチェック
cudaError_t
const char∗ cudaGetErrorString (cudaError_t error)
CUDA C/C++ カーネル
__global__
void myKernel(int a, float *pb, …) {
/* device code */
}
ホストから呼び出し可能なデバイス側の関数
— __global__を修飾子として持つ
— 戻り値は、voidでなければならない。
並列度に対応する回数、カーネルが呼び出されるイメージ。
2.3 プログラム例
配列の和
c[i] = a[i] + b[i]
メモリの取り扱い
基本的なカーネルの実装
装置構成
GPU
PC
CPU
(数コア)
GPU: CPUにつながった
外部演算装置
Giga Thread Engine
制御
PCIe
SMX
SMX SMX SMX
L2 Cache
DRAM
(Global Memory)
ホスト側DRAM
転送
…
配列の和:メモリの扱い
GPU
ホスト
float *a, *b, *c をアロケート
float *da, *db, *dc をアロケート (デバイスメモリ)
*a, *bに値を設定
ホスト->デバイス転送 a-> da, b->db
カーネル実行依頼
カーネル
dc[i] = da[i] + db[i]
ホスト <- デバイス転送 c <- dc
結果表示・検証
float *da, *db, *dc を開放 (デバイスメモリ)
float *a, *b, *c を開放
並列化(カーネル設計)
複数のブロックに配分して、和をとる。
— 図は、1 ブロックあたり、4スレッドとした場合
Block[0]
a[i]
b[i]
c[i]
0
1
2
Block[1]
3
4
5
6
Block[2]
7
8
9
Block[3]
10 11
12 13
14 15
+ + + +
+ + + +
+ + + +
+ + + +
15 14
11 10
7
3
13 12
9
8
6
5
4
2
1
0
GLOBAL ID
— Global ID
Grid内部でのスレッド番号
Grid内で一意
blockDim.x * blockIdx.x + threadIdx.x
— blockIdx
Block番号
Grid内で一意
— threadIdx
Block内のスレッド番号
Block内で一意
Global ID
0
1
2
3
blockIdx
0
blockIdx
4
5
6
7
1
threadIdx
0
1
2
3
Thread
Thread
Thread
Thread
threadIdx
0
1
2
3
Thread
Thread
Thread
Thread
カーネル実装例
__global__
void addArrayKernel(float *dc, const float *da, const float *db, int size) {
/* Global IDを算出 */
int globalID = blockDim.x * blockIdx.x + threadIdx.x;
if (globalID < size) { /* 範囲チェック */
/* 自スレッド担当の要素のみ、処理 */
dc[globalID] = da[globalID] + db[globalID];
}
}
ブロック数の指定
カーネルはブロック数でスケールする
— ブロックごとのスレッド数は一定
/* gridDim * blockDim個のスレッドを起動する */
int blockDim = 256;
int gridDim = (size + blockDim – 1) / blockDim;
addArrayKernel<<<gridDim, blockDim>>>(dc, da, db, size);
動かしてみる
まとめ
GPUのハードウエア
— Giga Thread Engine, SMX, SIMT
CUDAプログラミングモデル
— Grid, Block, Warp, Thread
基本的なプログラミング
— ホストプログラミング : メモリ転送、カーネル実行
— カーネルプログラミング : Global ID
© Copyright 2026