Windowsで始めるCUDA入門 - NVIDIA · 3.0 192 cores/SMX GTX680, Tesla K10 2012/3 3.5 Dynamic...
Transcript of Windowsで始めるCUDA入門 - NVIDIA · 3.0 192 cores/SMX GTX680, Tesla K10 2012/3 3.5 Dynamic...
GTC 2013 チュートリアル エヌビディアジャパン CUDAエンジニア 森野慎也
Windowsで始めるCUDA入門
1. GPUコンピューティング
GPUコンピューティング
— GPUによる、汎用コンピューティング
— GPU = Graphics Processing Unit
CUDA
— Compute Unified Device Architecture
— NVIDIAのGPUコンピューティング環境 Linux・Windows・MacOS Xにて動作
Compute Capability
ハードウエアアーキテクチャのバージョン
—「Fermi」(2.0以降) がターゲットです
機能 対応するデバイス 発表
1.0 初期バージョン 2006/11
1.1 globalメモリ上の32-bit atomic演算 GeForce 9XXX系 2007/10
1.2 メモリアクセスパターンの改善
実行スレッド数の増加、など。
GeForce GT240など
1.3 倍精度演算 GTX285, Tesla C1060など 2008/6
2.0 32 cores/SM、L1キャッシュなど GTX580、Tesla C2070など 2009/10
2.1 48 cores/SM GTX460、GTX560Ti など
3.0 192 cores/SMX GTX680, Tesla K10 2012/3
3.5 Dynamic Parallelism, 64 DP/SMX Tesla K20(X) 2012/11
最近のCUDAデバイス
デバイス名 コア数 ピーク演算性能
単精度/倍精度(FLOPS)
メモリバンド幅
GB/sec
Compute Capability 3.0
Quadro K5000 1536 2.1 T / 90 G 173
Tesla K10 1536 x 2 4.58 T / 0.19 T 320
GeForce GTX770 1536 3.21 T / 134 G 224.3
Compute Capability 3.5
Tesla K20X 2688 3.95 T / 1.31 T 250
Tesla K20 2496 3.52 T / 1.17 T 208
GeForce GTX Titan 2688 4.50 T / 1.31 T 288.4
1. Nsight Visual Studio Edition
Visual StudioでのCUDA開発
— ビルド・デバッグ・プロファイル
— CUDA Toolkitに含まれる。 (CUDA 5.5から)
開発者登録が 不要になりました。
1. 装置構成
CPU (数コア)
マザーボード
DRAM
チップセット
GPU
GPU: CPUにつながった
外部演算装置 プロセッサ(~数千コア)
DRAM
PCIe
1. 典型的な実行例
GPUはCPUからの制御で動作する。
入力データはCPU→GPUへと転送。
結果は、 GPU→CPUと転送
CPU
GPU
GPU プログラム 実行依頼
GPUでの演算
データ 転送
完了 待ち
データ 転送
プログラム 開始
1. CUDAカーネル
カーネル = GPU上のプログラム
— GPU向け言語(C++ベース)にて記述される。
— 特別なコンパイラ(NVCC)でコンパイル
100万スレッドオーダーでの並列動作
— Massively parallel
並列度の階層構造
— GPUのアーキテクチャに密接に関係
1. カーネル実行の階層
Grid
CPUからの呼び出し単位。
Blockに分解される。
Block
一定数のThread を持つ。
GPU上の並列プロセッサ(SMX) 内部でで実行される。
Thread
最小の実行単位
CPU GPU
GPU プログラム 実行依頼
データ 転送
Grid
Block0
Block1
Block2
Block n …
Thread Thread Thread Thread
Thread Thread Thread Thread
Thread Thread Thread Thread
…
Thread Thread Thread Thread
1. Warp
Warp
— 32 GPU-threads
— HW上の実行単位
Block
…
Thread Thread Thread Thread
Thread Thread Thread Thread
Thread Thread Thread Thread
Thread Thread Thread Thread
Warp0
Warp1
Warp2
Warp n
1. Streaming Multiprocessor eXtreme
192 Cores/SMX
Compute
Capability 3.5
SFU Special Function
Unit
LD/ST Load/Store
DP 倍精度演算ユニット
SMX (簡略化しています)
レジスタ
64 K個 (256 KB)
共有メモリ
L1 Cache
64 KB
テクスチャ
キャッシュ
48 KB
Core
3
Core
2
Core
1
Core
0
Core
15
Core
3
Core
2
Core
1
Core
0
Core
15
Core
3
Core
2
Core
1
Core
0
Core
15
Core
3
Core
2
Core
1
SFU
0
SFU
15
Core
3
Core
2
Core
1
LD/ST
0
LD/ST
15
Core
3
Core
2
Core
1
DP
0
DP
15
1. Streaming Multiprocessor eXtreme
GPU内部の「並列プロセッサ」
— 本質的に並列 (しか実行できない)
Blockは、SMX内部で動作
— GPUは、SMXの個数でスケールする。
高性能なGPU ⇒ 数多くのSMXを搭載
旧世代(Fermi以前):
Streaming Multiprocessor (SM)と呼びます
1. Grid・Block・Warp・Thread
Grid
— カーネル全体、全てのBlockを含む
Block
— 「カーネル設計」時に、重要な粒度
— Blockのサイズはカーネル内で一定。実行個数は、変更可能
Warp
— 「高速なプログラムを書く」時に重要な粒度
— HWに密接に関連。分岐処理、メモリアクセスの粒度
Thread
— 個々のGPUスレッド
— カーネルは、スレッド単位の視点で書く
SM(X)
1. CUDAプログラム実行の概要
Grid CPUからの呼び出し単位
Blockに分解
Block SM上の実行単位
Warpに分解
SM・共有メモリのスコープ
Warp CUDA固有の並列単位 32 GPU threads・条件分岐の粒度
SM(X) ハードウエア上の並列プロセッサ
CPU Grid
Block Block
Block
Warp Warp Warp
2. プログラミングの基礎
ホストプログラミング
— メモリ転送、カーネルの実行
カーネルプログラミング
— GPU上の関数の実装
2.1 CUDA ホストプログラミング
メモリのアロケーション、解放
— cudaMalloc()/cudaFree()
メモリコピー
— cudaMemcpy()
カーネルの呼び出し
— 特殊な構文
同期
— cudaDeviceSynchronize()
2.1 cudaMalloc() / cudaFree()
cudaError_t cudaMalloc(void ∗∗ devPtr, size_t size)
cudaError_t cudaFree(void *);
例:
float *devptr;
/* float型、1024個の要素分のデバイスメモリをアロケート */
cudaMalloc((void**)&devptr, sizeof(float) * 1024);
/* 解放 */
cudaFree(devptr);
2.1 cudaMemcpy()
cudaError_t
cudaMemcpy (void ∗ dst, const void ∗ src, size_t count,
enum cudaMemcpyKind kind)
例:
float src[1024] = {…..}
float *ddst;
cudaMalloc((void**)&ddst, sizeof(float) * 1024);
cudaMemcpy(ddst, src, sizeof(float) * 1024,
cudaMemcpyHostToDevice);
srcから ddstに、float型、1024個の要素をコピーする。
2.1 cudaMemcpy()
メモリは、「ホスト」「デバイス」の二種類
enum cudaMemcpyKind
cudaMemcpyHostToDevice
cudaMemcpyDeviceToHost
cudaMemcpyDeviceToDevice
cudaMemcpyHostToHost
(cudaMemcpyDefault : GPUdirect)
2.1 カーネル呼び出し
カーネル呼び出しの構文
kernelName<<<GridDim, BlockDim>>>(引数);
GridDim : グリッド中のブロック数
BlockDim : ブロックあたりのスレッド数
引数は、複数個指定可能
例:
sample<<<1, 256>>>(x, y, z);
2.1 cudaDeviceSynchronize()
cudaError_t cudaDeviceSynchronize (void)
例:
someKernel<<<xxx, yyy>>>(a, b, c);
cudaDeviceSynchronize();
カーネルが終了するまで待つ。
2.1 cudaError_t
エラーチェック
— 成功時は、cudaSuccessを返す。
エラーの場合、値を確認。
const char∗ cudaGetErrorString (cudaError_t error)
エラーを説明する文字列を返す。
2.1 CUDAカーネル
__global__
void myKernel(int a, float *pb, …) {
/* device code */
}
ホストから呼び出し可能なデバイス側の関数
— __global__を修飾子として持つ
— 戻り値は、voidでなければならない。
通常のC/C++の構文が使用可能。
2.2 プログラム例
配列の和
c[i] = a[i] + b[i]
メモリの取り扱い
基本的なカーネルの実装
2.2 デバイス・メモリ構成
CPU側 ホスト ホストメモリ
GPU側 デバイス グローバルメモリ
CPU
ホスト
ホストメモリ
GPU SM(X)
デバイスメモリ
(グローバルメモリ)
PCIe
2.2 配列の和:メモリの扱い
float *a, *b, *c をアロケート
ホスト GPU
カーネル dc[i] = da[i] + db[i]
*a, *bに値を設定
ホスト->デバイス転送 a-> da, b->db
ホスト <- デバイス転送 c <- dc
float *da, *db, *dc をアロケート (デバイスメモリ)
結果表示・検証
float *da, *db, *dc を開放 (デバイスメモリ)
float *a, *b, *c を開放
カーネル実行依頼
2.2 配列の和 : ホストコード
int main() {
static const int size= 256 * 100;
int memSize = sizeof(float) * size;
float *a, *b, *c, *da, *db, *dc; /* ホストもデバイスもメモリは同じポインタ型 */
/* ホスト側メモリの確保と値の初期化(略)*/
/* GPU側メモリをアロケート */
cudaMalloc(&da, memSize); cudaMalloc(&db,memSize); cudaMalloc(&dc, memSize);
cudaMemcpy(da, a, memSize, cudaMemcpyHostToDevice); /* メモリ転送(Host→Device) */
cudaMemcpy(db, b, memSize, cudaMemcpyHostToDevice);
/* カーネル(addArrayKernel)をここで呼ぶ */
cudaMemcpy(c, dc, memSize, cudaMemcpyDeviceToHost); /* メモリ転送(Host←Device) */
/* 表示などの処理 (略) */
cudaFree(da); cudaFree(db); cudaFree(dc);
free(a); free(b); free(c);
}
2.2 並列化(カーネル設計)
複数のブロックに配分して、和をとる。
— 図は、1 ブロックあたり、4スレッドとした場合
a[i]
b[i]
c[i]
Block[0]
0 1 2 3
+ + + + 15 14 13 12
Block[1]
4 5 6 7
+ + + + 11 10 9 8
Block[2]
8 9 10 11
+ + + + 7 6 5 4
Block[3]
12 13 14 15
+ + + + 3 2 1 0
2.2 Global ID / Local ID / Block ID
Global ID、Grid内で一意 blockDim.x * blockIdx.x + threadIdx.x
Local ID、Block内で一意 threadIdx.x
Block ID blockIdx.x
(OpenCLから概念を拝借)
0
1
2
3
Global ID
4
5
6
7
Local ID
(threadIdx)
0 Thread
1 Thread
2 Thread
3 Thread
0
Block ID
(blockIdx)
Local ID
0 Thread
1 Thread
2 Thread
3 Thread
1
Block ID
2.2 カーネル実装
__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];
}
}
2.2 ブロック数の指定
カーネルはブロック数でスケールする
— ブロックごとのスレッド数は一定
/* gridDim * blockDim個のスレッドを起動する */
int blockDim = 256;
int gridDim = (size + blockDim – 1) / blockDim;
addArrayKernel<<<gridDim, blockDim>>>(dc, da, db, size);
2.2 配列の和 : ホストコード
int main() {
static const int size= 256 * 100;
int memSize = sizeof(float) * size;
float *a, *b, *c, *da, *db, *dc; /* ホストもデバイスもメモリは同じポインタ型 */
/* ホスト側メモリの確保と値の初期化(略)*/
/* GPU側メモリをアロケート */
cudaMalloc(&da, memSize); cudaMalloc(&db,memSize); cudaMalloc(&dc, memSize);
cudaMemcpy(da, a, memSize, cudaMemcpyHostToDevice); /* メモリ転送(Host→Device) */
cudaMemcpy(db, b, memSize, cudaMemcpyHostToDevice);
int blockDim = 256; int gridDim = (size + blockDim – 1) / blockDim;
addArrayKernel<<<gridDim, blockDim>>>(dc, da, db, size);
// cudaDeviceSynchronize(); /* 同期。今回は、必須ではない。 */
cudaMemcpy(c, dc, memSize, cudaMemcpyDeviceToHost); /* メモリ転送(Host←Device) */
/* 表示などの処理 (略) */
cudaFree(da); cudaFree(db); cudaFree(dc);
free(a); free(b); free(c);
}
3. Visual Studio 2010 によるビルド
ビルドルールの追加
3. Visual Studio 2010 によるビルド
Compute Capability の設定
3. Visual Studio 2010 によるビルド
ライブラリ指定
NVIDIA Japan CUDA Monthly Seminar
NVIDIA Japanでは、毎月、
CUDAの無償セミナーを実施しています。
是非、ご参加ください。
— 申し込み :
http://www.nvidia.co.jp/object/event-calendar-jp.html
— 場所 : NVIDIA Japan 赤坂オフィス