46
3GPUプログラム構造の詳細 threadwarp長岡技術科学大学 電気電子情報工学専攻 出川智啓

2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

  • Upload
    -

  • View
    196

  • Download
    1

Embed Size (px)

Citation preview

Page 1: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

第3回 GPUプログラム構造の詳細(threadとwarp)

長岡技術科学大学 電気電子情報工学専攻 出川智啓

Page 2: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

今回の内容

2015/04/23先端GPGPUシミュレーション工学特論2

2次元的な並列処理

Warp

並列実行の制約

条件分岐

Page 3: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUによる1次元的な並列処理

ベクトル和C=A+B 配列要素に対して計算順序の依存性がなく, も単純に並列化可能

・・・

・・・

・・・c[i]

a[i]

b[i]

+ + + + + +

2015/04/23先端GPGPUシミュレーション工学特論3

Page 4: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

#include<stdlib.h>#define N (1024*1024)#define Nbytes (N*sizeof(float))

void init(float *a,float *b,float *c){int i;

for(i=0; i<N; i++){a[i] = 1.0;b[i] = 2.0;c[i] = 0.0;

}}

void add(float *a,float *b,float *c){int i;

for(i=0; i<N; i++)c[i] = a[i] + b[i];

}

int main(void){float *a,*b,*c;

a = (float *)malloc(Nbytes);b = (float *)malloc(Nbytes);c = (float *)malloc(Nbytes);

init(a,b,c);add(a,b,c);

free(a);free(b);free(c);return 0;

}

CPUプログラム(メモリの動的確保)

2015/04/23先端GPGPUシミュレーション工学特論4

vectoradd_malloc.c

Page 5: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUの構造とカーネルの書き方

プログラムからGPUで実行する関数を呼出

GPUで実行する関数という目印が必要

GPUはPCI‐Exバスを経由してホストと接続

GPUはホストと別に独立したメモリを持つ

関数の実行に必要なデータはGPUのメモリに置く

GPUはマルチスレッド(メニースレッド)で並列処理

関数には1スレッドが実行する処理を書く

関数を実行する際に並列処理の度合いを指定

2015/04/23先端GPGPUシミュレーション工学特論5

Page 6: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUの構造とカーネルの書き方

GPUで実行する関数(カーネル)という目印

修飾子__global__を付ける

GPUはPCI‐Exバスを経由してホストと接続

GPUはホストと別に独立したメモリを持つ

カーネルの返値をvoidにする

メモリの動的確保をmallocからcudaMallocに変更

GPUはマルチスレッド(メニースレッド)で並列処理

カーネルには1スレッドが実行する処理を書く

カーネル名と引数の間に<<<1,1>>>を付ける

2015/04/23先端GPGPUシミュレーション工学特論6

Page 7: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUで並列に処理を行うには

GPUは低性能の演算器を多数搭載

マルチスレッドで並列処理することで高い性能を達成 マルチスレッドで処理を行わないとCPUよりも遅い

GPUで並列計算を行う時の決まり事カーネルに1スレッドが行う処理を記述

カーネルを実行する際に並列処理の度合いを指定

カーネルの名前の後ろに付けていた<<<1,1>>>は,どのように並列実行するかの指定

2015/04/23先端GPGPUシミュレーション工学特論7

Page 8: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUの並列化の階層

グリッド-ブロック-スレッドの3階層

各階層の情報を参照できる変数 x,y,zをメンバにもつdim3型構造体

グリッド(Grid) gridDim グリッド内にあるブロックの数

ブロック(Block) blockIdx ブロックに割り当てられた番号

blockDim ブロック内にあるスレッドの数

スレッド(Thread) threadIdx スレッドに割り当てられた番号

2015/04/23先端GPGPUシミュレーション工学特論8

Page 9: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUの並列化の階層

CUDAでは並列化に階層がある 全体の領域(グリッド)をブロックに分割

ブロックの中をスレッドに分割

<<<2, 4>>>

ブロックの数 1ブロックあたりのスレッドの数

ブロックの数×1ブロックあたりのスレッドの数=総スレッド数2    × 4       = 8 

2015/04/23先端GPGPUシミュレーション工学特論9

Page 10: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

iを一意に決定する

N=8, <<<2, 4>>>で実行

c[i]

a[i]

b[i]

+ + + + + + + +

gridDim.x=2

blockIdx.x=0 blockIdx.x=1

blockDim.x=4blockDim.x=4threadIdx.x=

0    1   2    3 0    1   2    3

threadIdx.x=

i=   0    1   2    3   4    5   6    7=  blockIdx.x*blockDim.x + threadIdx.x

2015/04/23先端GPGPUシミュレーション工学特論10

Page 11: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

カーネルの書き換え

1スレッドが実行する処理になるよう変更 1スレッドがある添字 i の要素を担当

#define N (1024*1024)#define Nbytes (N*sizeof(float))

__global__ void init(float *a, float *b, float *c){int i=blockIdx.x*blockDim.x + threadIdx.x;a[i] = 1.0;b[i] = 2.0;c[i] = 0.0;

}__global__ void add(float *a, float *b, float *c){

int i=blockIdx.x*blockDim.x + threadIdx.x;c[i] = a[i] + b[i];

}

2015/04/23先端GPGPUシミュレーション工学特論11

Page 12: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUで並列に処理を行うには

GPUは低性能の演算器を多数搭載

マルチスレッドで並列処理することで高い性能を達成

GPUで並列計算を行う時の決まり事

カーネルに1スレッドが行う処理を記述

カーネルを実行する際に並列処理の度合いを指定

ブロック内で複数のスレッドが並列処理

複数のスレッドが異なる添字iにアクセスすることで並列に計算

添字iはスレッド数やブロック数を格納する変数を用いて決定

i = blockIdx.x*blockDim.x + threadIdx.x;

2015/04/23先端GPGPUシミュレーション工学特論12

Page 13: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

#define N (1024*1024)#define Nbytes (N*sizeof(float))

__global__ void init(float *a,float *b, float *c){

int i = blockIdx.x*blockDim.x+ threadIdx.x;

a[i] = 1.0;b[i] = 2.0;c[i] = 0.0;

}__global__ void add(float *a,

float *b, float *c){int i = blockIdx.x*blockDim.x

+ threadIdx.x;c[i] = a[i] + b[i];

}int main(void){

float *a,*b,*c;

cudaMalloc((void **)&a, Nbytes);cudaMalloc((void **)&b, Nbytes);cudaMalloc((void **)&c, Nbytes);

init<<< N/256, 256>>>(a,b,c);add<<< N/256, 256>>>(a,b,c);

cudaFree(a);cudaFree(b);cudaFree(c);

return 0;}

GPUで並列実行するプログラム

2015/04/23先端GPGPUシミュレーション工学特論13

vectoradd.cu

Page 14: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

階層の設定

無制限に設定できるわけではない GPUの世代によって設定できる範囲が変わる

確認にはdeviceQueryやpgaccelinfoを利用

$ pgaccelinfo

2015/04/23先端GPGPUシミュレーション工学特論14

Page 15: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

deviceQuery実行結果Device 0: "Tesla C2050"CUDA Driver Version / Runtime Version          5.5 / 5.5CUDA Capability Major/Minor version number:    2.0Total amount of global memory:                 2687 MBytes (2817982464 bytes)(14) Multiprocessors, ( 32) CUDA Cores/MP:     448 CUDA CoresGPU Clock rate:                                1147 MHz (1.15 GHz)Memory Clock rate:                             1500 MhzMemory Bus Width:                              384‐bitL2 Cache Size:                                 786432 bytesMaximum Texture Dimension Size (x,y,z)         1D=(65536), 2D=(65536, 65535), 3D=(2048, 2048, 2048)Maximum Layered 1D Texture Size, (num) layers  1D=(16384), 2048 layersMaximum Layered 2D Texture Size, (num) layers  2D=(16384, 16384), 2048 layersTotal amount of constant memory:               65536 bytesTotal amount of shared memory per block:       49152 bytesTotal number of registers available per block: 32768Warp size:                                     32Maximum number of threads per multiprocessor:  1536Maximum number of threads per block:           1024Max dimension size of a thread block (x,y,z): (1024, 1024, 64)Max dimension size of a grid size    (x,y,z): (65535, 65535, 65535)Maximum memory pitch:                          2147483647 bytesTexture alignment:                             512 bytesConcurrent copy and kernel execution:          Yes with 2 copy engine(s)Run time limit on kernels:                     NoIntegrated GPU sharing Host Memory:            NoSupport host page‐locked memory mapping:       YesAlignment requirement for Surfaces:            YesDevice has ECC support:                        EnabledDevice supports Unified Addressing (UVA):      YesDevice PCI Bus ID / PCI location ID:           2 / 0

2015/04/23先端GPGPUシミュレーション工学特論15

Page 16: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

pgaccelinfoの実行結果Device Number:                 0Device Name:                   Tesla M2050Device Revision Number:        2.0Global Memory Size:            2817982464Number of Multiprocessors:     14Number of Cores:               448Concurrent Copy and Execution: YesTotal Constant Memory:         65536Total Shared Memory per Block: 49152Registers per Block:           32768Warp Size:                     32Maximum Threads per Block:     1024Maximum Block Dimensions:      1024, 1024, 64Maximum Grid Dimensions:       65535 x 65535 x 65535Maximum Memory Pitch:          2147483647BTexture Alignment:             512BClock Rate:                    1147 MHzInitialization time:           4222411 microsecondsCurrent free memory:           2746736640Upload time (4MB):             2175 microseconds ( 829 ms pinned)Download time:                 2062 microseconds ( 774 ms pinned)Upload bandwidth:              1928 MB/sec (5059 MB/sec pinned)Download bandwidth:            2034 MB/sec (5418 MB/sec pinned)

2015/04/23先端GPGPUシミュレーション工学特論16

Page 17: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

pgaccelinfo実行結果 Revision Number:       2.0

Global Memory Size:            2817982464

Warp Size:                     32

Maximum Threads per Block:     1024

Maximum Block Dimensions:      1024, 1024, 64

Maximum Grid Dimensions:       65535 x 65535 x 65535

どのような機能を有しているか

実行時のパラメータ

選択の際に重要

各方向の 大値

1ブロックあたりのスレッド数は 大1024 (1024, 1, 1), (1, 1024, 1)

(32, 32, 1), (4, 4, 64)など

2015/04/23先端GPGPUシミュレーション工学特論17

Page 18: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUの階層の情報(dim3型構造体)

2015/04/23先端GPGPUシミュレーション工学特論18

gridDim(グリッド内にあるブロックの数) gridDim.x, gridDim.y, gridDim.z

blockIdx(ブロックに割り当てられた番号) blockIdx.x, blockIdx.y, blockIdx.z

blockDim(ブロック内にあるスレッドの数) blockDim.x, blockDim.y, blockDim.z

threadIdx(スレッドに割り当てられた番号) threadIdx.x, threadIdx.y, threadIdx.z

Page 19: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

2次元的な並列処理

2015/04/23先端GPGPUシミュレーション工学特論19

例えば画像処理のフィルタなど あるピクセルの周囲情報を利用して効果を計算

画像を矩形領域で分割した2次元的な並列処理が自然

元画像 輪郭抽出後

Page 20: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

2次元的な並列処理

2015/04/23先端GPGPUシミュレーション工学特論20

ブロックとスレッドを2次元的に設定 1スレッドが1ピクセル(配列の1要素)を処理

スレッドを2次元的に配置してブロックを構成

Page 21: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

想定されるカーネル

1スレッドが2次元配列の1要素を計算 1スレッドがある添字 i,j の要素を担当

#define Nx (1920)#define Ny (1080)

__global__ void filter(...){

int i = ; //1スレッドが担当する配列要素の添字を決定int j = ; //

picture[i][j] = ...;

}

2015/04/23先端GPGPUシミュレーション工学特論21

Page 22: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

i, jを一意に決定する

2015/04/23先端GPGPUシミュレーション工学特論22

Nx=8, Ny=8, x,y方向スレッド数4,ブロック数2

blockIdx.x=0 blockIdx.x=1

blockIdx.y=0

blockIdx.y=1

gridDim.x=2

gridDim.y=2

blockDim.x=4

blockDim.y=4

threadIdx.x=threadIdx.y=

Page 23: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

i, jを一意に決定する

2015/04/23先端GPGPUシミュレーション工学特論23

Nx=8, Ny=8, x,y方向スレッド数4,ブロック数2 gridDim.x=2, gridDim.y=2 blockDim.x=4,blockDim.y=4

(0,0)(1,0)(2,0)(3,0)(0,0)

(3,3) (3,3)

(0,1)(1,1)(2,1)(3,1)

(0,2)(1,2)(2,2)(3,2)

(0,3)(1,3)(2,3)(3,3) (3,3)

(0,0) (0,0)

block(0,0) block(1,0)

block(0,1) block(1,1)

thread

threadIdx.xthreadIdx.y

i= 0 1 2 3 4 5 6 7j=

01

23

45

67

Page 24: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

i, jを一意に決定する

2015/04/23先端GPGPUシミュレーション工学特論24

Nx=8, Ny=8, x,y方向スレッド数4,ブロック数2 i = blockIdx.x*blockDim.x + threadIdx.x j = blockIdx.y*blockDim.y + threadIdx.y

(0,0)(1,0)(2,0)(3,0)(0,0)

(3,3) (3,3)

(0,1)(1,1)(2,1)(3,1)

(0,2)(1,2)(2,2)(3,2)

(0,3)(1,3)(2,3)(3,3) (3,3)

(0,0) (0,0)

block(0,0) block(1,0)

block(0,1) block(1,1)

thread

threadIdx.xthreadIdx.y

i= 0 1 2 3 4 5 6 7j=

01

23

45

67

Page 25: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

1次元による配列で2次元配列の表現

2015/04/23先端GPGPUシミュレーション工学特論25

mallocやcudaMallocは1次元配列を宣言 2次元配列の宣言は不可能

1次元配列を宣言し,2次元配列的に参照

2次元配列の場合

1次元配列の場合

picutre[1][2] = 11

picture[_______] = 11

Nx=8

1*8 + 2

picture[i][j]  picture[i*Ny+j]

j

i12345

9101112

678

picture[Nx][Ny]

Ny=8

Page 26: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

想定されるカーネル

1スレッドが2次元配列の1要素を計算 1スレッドがある添字 i,j の要素を担当

#define Nx (1920)#define Ny (1080)

__global__ void filter(...){

int i = blockIdx.x*blockDim.x + threadIdx.x;int j = blockIdx.y*blockDim.y + threadIdx.y;

picture[i*Ny + j] = ...;

}

2015/04/23先端GPGPUシミュレーション工学特論26

picture[j*Nx + i] = ... の方が適切第4回GPUのメモリ階層の詳細(共有メモリ)を参照の事

Page 27: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

2次元的な並列度の指定

2015/04/23先端GPGPUシミュレーション工学特論27

<<<,>>>の中にどのように数字を書くか 1次元の場合は数字を書くことができた

2次元,3次元は数字を並べて書くことができない

dim3型変数を利用

int main(void){

dim3 block(2,2,1), thread(4,4,1);filter<<<block, thread>>>(...);

filter<<<dim3(2,2,1), dim3(4,4,1)>>>(...);

}

dim3型変数block, threadを利用

・・・

あるいは直接dim3型として記述・・・

Page 28: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

#define Nx (1024)#define Ny (1024)#define Nbytes (Nx*Ny*sizeof(float))#define NTx (16)#define NTy (16)#define NBx (Nx/NTx)#define NBy (Ny/NTy)

__global__ void init(float *a, float *b,float *c){

int i = blockIdx.x*blockDim.x + threadIdx.x;int j = blockIdx.y*blockDim.y + threadIdx.y;int ij = i*Ny + j;

a[ij] = 1.0;b[ij] = 2.0;c[ij] = 0.0;

}__global__ void add(float *a, float *b,

float *c){int i = blockIdx.x*blockDim.x + threadIdx.x;int j = blockIdx.y*blockDim.y + threadIdx.y;int ij = i*Ny + j;

c[ij] = a[ij] + b[ij];}

int main(void){float *a,*b,*c;dim3 thread(NTx, NTy, 1);dim3  block(NBx, NBy, 1);

cudaMalloc( (void **)&a, Nbytes);cudaMalloc( (void **)&b, Nbytes);cudaMalloc( (void **)&c, Nbytes);

init<<< block, thread >>>(a,b,c);add<<< block, thread >>>(a,b,c);

cudaFree(a);cudaFree(b);cudaFree(c);return 0;

}

ベクトル和(2次元並列版)

2015/04/23先端GPGPUシミュレーション工学特論28

vectoradd2d.cu

int ij=j*Nx+iの方が適切

int ij=j*Nx+iの方が適切

Page 29: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

一般的な形

グリッドは必ず1個 ブロックは3次元的に配置可能

スレッドは3次元的に配置可能

各方向の 大値はGPUの世代によって変化

Grid

Block(i,j,k)

Thread(i,j,k)

2015/04/23先端GPGPUシミュレーション工学特論29

Page 30: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

GPUの並列処理

GPUはどのようにスレッドを管理しているか?

Streaming Multiprocessor内のCUDA Core数 Tesla世代 8 Fermi世代 32 Kepler世代 192 Maxwell世代 128

Warp(ウォープ)という処理の単位の導入 32スレッドをまとめて1 Warp 1 Warpごとに処理を実行

2015/04/23先端GPGPUシミュレーション工学特論30

256スレッドの同時実行は不可能

Page 31: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

Warpによる並列実行

処理はWarp単位で実行 32スレッドごとに処理を実行

1ブロックあたりのスレッド数が256=Warp 8個

ある命令を発行するとWarp単位で同じ命令を実行 Warp内でCUDA CoreはSIMD的に動作

複数のまとまったスレッドが同じ演算を同時に実行

SIMT(Single Instruction Multiple Threads)

SIMD(Single Instruction Multiple Data streams) 複数のまとまったデータに対して同じ演算を同時に実行

命令は一つ,その命令が同時に多くのデータに対して適用されるアーキテクチャ

2015/04/23先端GPGPUシミュレーション工学特論31

Page 32: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

Warpによる並列実行の利点

パイプライン実行 あるWarpがグローバルメモリへアクセス,データ待ち

その他の実行可能なWarpが計算を実行

グローバルメモリへのアクセスのレイテンシを隠蔽

2015/04/23先端GPGPUシミュレーション工学特論36

計算 メモリアクセス 計算Warp0

Warp1

Warp2

Warp3

実行開始 処理時間

計算 メモリアクセス 計算

計算 メモリアクセス 計算

計算 メモリアクセス

・・・

・・・

Page 33: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

Warpによる並列実行の利点

レイテンシ 命令の発行から結果が得られるまでの(遅延)時間

簡単な命令でも数サイクル(クロック)必要

グローバルメモリへのアクセスは数百サイクル必要

CPUはプリフェッチやOut of Order実行でレイテンシを隠蔽

GPUでは各コアに対してCPUのような機能を実装できない

コアの数,面積,電力の問題

2015/04/23先端GPGPUシミュレーション工学特論37

Page 34: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

Warpによる並列実行の利点

複数のWarpを切り替えながら処理を実行することでメモリアクセスのレイテンシを隠蔽

1ブロックあたりのスレッド数が多いと,Warpの実行に必要な資源が不足

38 2015/04/23先端GPGPUシミュレーション工学特論

計算 メモリアクセス 計算Warp0

Warp1

Warp2

Warp3

実行開始 処理時間

計算 メモリアクセス 計算

計算 メモリアクセス 計算

計算 メモリアクセス

・・・

・・・

Page 35: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

Warpの同時実行

2015/04/23先端GPGPUシミュレーション工学特論39

一つのブロック内で使用している資源(レジスタ,共有メモリ)によって同時実行できるWarpの数が変化 同時実行可能なWarpが多いほどレイテンシ隠蔽が容易

同時に実行されるWarpの数 Active Warp Active Warpを多くすることが高速化に繋がる

占有率(Occupancy) SM内で並列実行されているスレッド数 と SMあたりの 大で実行できるスレッド数 の比

1に近いほどよいが,占有率が高い=高速 ではない

Page 36: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

並列実行に対する制約

2015/04/23先端GPGPUシミュレーション工学特論40

GPUの構成(資源の量,スケジューラ)に起因するハードウェア的・ソフトウェア的制約

Tesla世代 Fermi世代

Warpサイズ 32スレッド

1ブロックあたりのスレッド数 512 1024

1SMあたりの 大スレッド数 1024 1536

1SMあたりのWarp数 32 48

1SMあたりのブロック数 8 8

1SMあたりの共有メモリサイズ 16384 byte 49152 byte

1SMあたりの32bitレジスタ数 16384本 32768本

https://www.softek.co.jp/SPG/Pgi/TIPS/public/accel/gpu‐accel2.html

Page 37: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

並列実行に対する制約

2015/04/23先端GPGPUシミュレーション工学特論41

1ブロックあたりのスレッド数を256に設定 1ブロックあたりのWarp数

256thread/block / 32thread/Warp=8 Warp/block

1SMが処理するブロック数(1SMあたり 大48Warpの処理が可能)

48 Warp/SM / 8 Warp/block =  6 block/SM(<8) 1SMあたり6ブロックを並列処理すればよい( 大8ブロック)

同時実行されるWarp数は

8 Warp/block×6 block/SM = 48 Warp/SM 1SMあたり 大48 Warp同時実行できるので,占有率は

48 Warp/SM /48 Warp/SM = 1.00 (=100%)

太字:利用するGPUによって決定下線:ユーザの設定によって決定斜体:処理内容やコンパイラによって決定

Page 38: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

並列実行に対する制約

2015/04/23先端GPGPUシミュレーション工学特論42

1ブロックあたりのスレッド数を 64に設定 1ブロックあたりのWarp数

64thread/block / 32thread/Warp=2 Warp/block

1SMが処理するブロック数(1SMあたり 大48Warpの処理が可能)

48 Warp/SM / 2 Warp/block = 24 block/SM(>8) 1SMあたり8ブロックを並列処理可能(24ブロックの処理は不可能)

同時実行されるWarp数は

2 Warp/block×8 block/SM = 16 Warp/SM 1SMあたり 大48 Warp同時実行できるので,占有率は

16 Warp/SM /48 Warp/SM = 0.33 (=33%)

太字:利用するGPUによって決定下線:ユーザの設定によって決定斜体:処理内容やコンパイラによって決定

Page 39: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

並列実行に対する制約

2015/04/23先端GPGPUシミュレーション工学特論43

並列実行されるブロックの数 1ブロックあたりの共有メモリやレジスタの使用量で制限

1ブロックあたりのスレッド数がNt 1ブロックが共有メモリをS[byte]利用していると

49152/S [block/SM] 1スレッドがレジスタをR[本]利用していると

32768/(Nt×R) [block/SM]

並列実行されるブロック数

min{8, 48/(Nt/32), 49152/S, 32768/(Nt×R)}

太字:利用するGPUによって決定下線:ユーザの設定によって決定斜体:処理内容やコンパイラによって決定

Page 40: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

Warp内のスレッドの実行

2015/04/23先端GPGPUシミュレーション工学特論44

Warp内では32スレッドが同じ命令を実行 Warp内でCUDA CoreはSIMD的に動作(SIMT型)

プログラム的には異なる動作をさせることも可能 ブロック内のスレッド番号 threadIdx を基にif文を記述しても正しく実行(=厳密なSIMDではない)

__global__ void kernel(){if(threadIdx.x == 0){

処理1}else{

処理2}

}int main(void){

kernel<<<1,32>>>();}

1 Warpだけでカーネルを実行・・・

0番目のスレッドだけ処理1を実行・・・

残りの31スレッドは処理2を実行・・・

Page 41: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

ブロック内での条件分岐

2015/04/23先端GPGPUシミュレーション工学特論45

Warp内では32スレッドが同じ命令を実行 Warp内でCUDA CoreはSIMD的に動作(SIMT型)

条件分岐もWarp単位で判定

Warp内の全スレッドの分岐先が同じ Warp内の全スレッドが,分岐先のみの処理を実行

Warp内でスレッドの分岐先が異なる Warp内の全スレッドが,全ての分岐先の処理を実行

各スレッドで真になる条件の処理のみを採用

branch divergence, divergent branch, diverged branchなどと呼ばれる

Page 42: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

__global__ void no_diverge(){if(threadIdx.x < 32){

処理1}else{

処理2}

}

__global__ void diverge(){if(threadIdx.x % 8 == 0){

if(threadIdx.x & 1 == 1) a+=1;else                     a+=2;

}else{if(threadIdx.x & 1 == 1) a+=2;else                     a+=1;

}}

int main(void){no_diverge<<<1,64>>>();

diverge<<<1,64>>>();}

ブロック内での条件分岐

2015/04/23先端GPGPUシミュレーション工学特論46

2 Warpでカーネルを実行・・・

0~31スレッド(=Warp 0)は処理1を実行・・・

32~63スレッド(=Warp 1)は処理2を実行・・・divergeしない

Page 43: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

__global__ void no_diverge(){if(threadIdx.x < 32){

処理1}else{

処理2}

}

__global__ void diverge(){if(threadIdx.x % 8 == 0){

if(threadIdx.x & 1 == 1) a+=1;else                     a+=2;

}else{if(threadIdx.x & 1 == 1) a+=2;else                     a+=1;

}}

int main(void){no_diverge<<<1,64>>>();

diverge<<<1,64>>>();}

ブロック内での条件分岐

2015/04/23先端GPGPUシミュレーション工学特論47

diverge

divergeしない

スレッド番号が8の倍数(0も含む)・・・

スレッド番号が8の倍数以外・・・

2 Warpでカーネルを実行・・・

0~31スレッド(=Warp 0)は処理1を実行・・・

32~63スレッド(=Warp 1)は処理2を実行・・・

Page 44: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

__global__ void no_diverge(){if(threadIdx.x < 32){

処理1}else{

処理2}

}

__global__ void diverge(){if(threadIdx.x % 8 == 0){

if(threadIdx.x & 1 == 1) a+=1;else                     a+=2;

}else{if(threadIdx.x & 1 == 1) a+=2;else                     a+=1;

}}

int main(void){no_diverge<<<1,64>>>();

diverge<<<1,64>>>();}

ブロック内での条件分岐

2015/04/23先端GPGPUシミュレーション工学特論48

divergeしない

スレッド番号が奇数なら1を加算・・・スレッド番号が偶数なら2を加算・・・

スレッド番号が奇数なら2を加算・・・

スレッド番号が偶数なら1を加算・・・

diverge

diverge

スレッド番号が8の倍数(0も含む)・・・

スレッド番号が8の倍数以外・・・

2 Warpでカーネルを実行・・・

0~31スレッド(=Warp 0)は処理1を実行・・・

32~63スレッド(=Warp 1)は処理2を実行・・・

Page 45: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

__global__ void no_diverge(){if(threadIdx.x < 32){

処理1}else{

処理2}

}

__global__ void diverge(){if(threadIdx.x % 8 == 0){

if(threadIdx.x & 1 == 1) a+=1;else                     a+=2;

}else{if(threadIdx.x & 1 == 1) a+=2;else                     a+=1;

}}

int main(void){no_diverge<<<1,64>>>();

diverge<<<1,64>>>();}

ブロック内での条件分岐

2015/04/23先端GPGPUシミュレーション工学特論49

スレッド番号が奇数なら1を加算・・・スレッド番号が偶数なら2を加算・・・

スレッド番号が奇数なら2を加算・・・

スレッド番号が偶数なら1を加算・・・

diverge

diverge

スレッド番号が8の倍数(0も含む)・・・

スレッド番号が8の倍数以外・・・

全てのifがdivergeするので,合計4通りの計算が行われる

divergeしない

2 Warpでカーネルを実行・・・

0~31スレッド(=Warp 0)は処理1を実行・・・

32~63スレッド(=Warp 1)は処理2を実行・・・

Page 46: 2015年度先端GPGPUシミュレーション工学特論 第3回 GPUプログラム構造の詳細(threadとwarp)

ブロック間での分岐

2015/04/23先端GPGPUシミュレーション工学特論50

ブロック内の全スレッドが分岐先の処理を実行

divergent branchのような現象は発生しない 条件判断に要する時間だけで処理の切替が可能

GPUはifの処理が苦手なので,ifの使用は極力控える

__global__ void kernel(){if(blockIdx.x == 0){

処理1}else{

処理2}

}int main(void){

kernel<<<2,32>>>();}

ブロック数2でカーネルを実行・・・

0番目のブロックに属する全スレッドが処理1を実行・・・

それ以外のブロックに属する全スレッドが処理2を実行・・・