格子 QCD における GPU 計算

27
格格 QCD 格格格格 GPU 格格 広広広 広広広広 広広広広広 広広広

description

格子 QCD における GPU 計算. 広大 理 尾崎裕介 共同研究者 石川健一. 1. Introduction. 格子 QCD 計算では HMC(Hybrid Monte-Carlo) 法がよく用いられる この手法において、差分方程式を解く計算が最も時間のかかる計算 この計算を GPU に計算させることによって加速 solver のアルゴリズムは Bi- CGStab 法 この際に作成した CUDA のコードに対して行った工夫等について紹介. Outline. Introduction GPU を使って倍精度の結果を得るために - PowerPoint PPT Presentation

Transcript of 格子 QCD における GPU 計算

Page 1: 格子 QCD における GPU 計算

格子 QCD における GPU 計算

広大理 尾崎裕介共同研究者 石川健一

Page 2: 格子 QCD における GPU 計算

2

1. Introduction

格子 QCD 計算では HMC(Hybrid Monte-Carlo)法がよく用いられる

この手法において、差分方程式を解く計算が最も時間のかかる計算

この計算を GPU に計算させることによって加速solver のアルゴリズムは Bi-CGStab 法

この際に作成した CUDA のコードに対して行った工夫等について紹介

2009/6/24

Page 3: 格子 QCD における GPU 計算

3

Outline

2009/6/24

1. Introduction

2. GPU を使って倍精度の結果を得るために

3. ホッピングの計算(行列 × ベクトル)

4. 内積の計算

5. Fortran + Cuda

6. おわりに

Page 4: 格子 QCD における GPU 計算

4

2.1 GPU で倍精度

差分方程式の解は倍精度の精度が必要 しかし GPU は単精度計算が高速

単精度:約 1TFlops倍精度: 86.4GFlops

単精度演算を行いながら、得られる結果が倍精度であるような手法があれば理想的

反復改良の手法によって、実は可能

2009/6/24

Page 5: 格子 QCD における GPU 計算

5

2.2 単精度倍精度混合 Solver

差分方程式 ( 連立 1 次方程式 )

前処理付き反復法による解法(倍精度)

単精度倍精度混合 Solver            (単精度)

2009/6/24

:残差ベクトル

M :前処理行列

Page 6: 格子 QCD における GPU 計算

6

2.3 単精度倍精度混合 Solver

2009/6/24

subroutine usual_solver(...) ⋮do i = 0,1,2,... ⋮p = M * r ! Preconditionq = A * px = x + αpr = r – αq ⋮enddo ⋮end subroutine

subroutine mixed_solver(...) ⋮As = A ! 倍→単 変換 ⋮do i = 0,1,2,... ⋮rs = r ! 倍→単 変換ps = (As)-1 * rs ! 単精度計算p = ps ! 単→倍 変換q = A * px = x + αpr = r – αq ⋮enddo ⋮end subroutineGPU の単精度高速計算!

Page 7: 格子 QCD における GPU 計算

7

2.4 収束の様子

2009/6/24

Page 8: 格子 QCD における GPU 計算

8

3.1 ホッピングの計算

単精度 Solver のアルゴリズムは Bi-CGStabWilson quark の場合によく用いられる。

このような反復法による計算では      のような行列とベクトルの積の計算が支配的

 

まずはこの計算を高速化

2009/6/24

Page 9: 格子 QCD における GPU 計算

9

3.2 の計算の様子

2009/6/24

quark : 3×4 ベクトル

hopping : 12×12 行列

Page 10: 格子 QCD における GPU 計算

10

3.3 Basic strategy

Nvidia Cuda Programming Guide より、 なるべく並列度を上げる

1 thread あたりの計算を 1 格子点の計算に assign(12×12 行列 ) × (12 次元ベクトル )

メモリアクセスの最適化global memory に対するアクセス速度が遅い

400 ~ 600 clockshared memory 、 texture cache 等の有効利用

4 clock計算によって生成できるデータは GPU 上で計算

memory access が遅いので計算した方が速い場合がある2009/6/24

Page 11: 格子 QCD における GPU 計算

11

3.4 データアクセス量の削減

2009/6/24

格子の辺に対応する行列は 12×12 しかし、 12×12 の行列を計算機上に保存しておく必要

はない各 μ ごとにコーディングを行えば 3×3 行列を 4 組保存しておけ

ば十分

さらに U はリー群 SU(3) の元であり、 3×3 も必要ない厳密には 8float今回は少し楽をして 6complex

Page 12: 格子 QCD における GPU 計算

12

3.5 効率的なメモリアクセス データ量とロード回数

2009/6/24

計 8 回のロード12×2 float = 96Byte

計 2 回のロード(6×2 float = 48Byte) ×

4set block 内では shared memory を使って thread 間の memory 共有ができるshared memory : 16KByteできるだけ多くの格子点を共有した

い ロード回数の多い格子点を

shared43×2=128 の格子点 = 12.6KByte リンクは texture を用いた

Page 13: 格子 QCD における GPU 計算

13

3.6 solver の性能

その他 Bi-CGStab 法で必要な演算(複素数の内積等)をSDK 等を参考に作成

これらを組み合わせて GPU を使った単精度 solver を作成even/odd preconditioning ← 格子サイズを半分にする手法clover term の計算 ← 格子間隔による誤差を減少させる

さらに前処理付き Bi-CGStab 倍精度 solver とマージ このようにしてできた solver を実際に走らせてみた

GPU : NVIDIA GeForce GTX 280 CPU : Intel Core i7 2.67GHz

結果約 20GFlops の性能

2009/6/24

この solver を以下のようにして高速化した これらの手法を紹介

Page 14: 格子 QCD における GPU 計算

14

3.7 coalesced access

2009/6/24

これまでのデータ構造は coalesced access の条件を満たしていなかった

block 0 block 1 …

site 0 site 1 site 2 … site 128 site 0 … …

block 0 block 1 …

0 1 … 128 0 1 … 0 1 … 0 1 … …

96Byte 96Byte 96Byte 96Byte 96Byte

16B 16B 16B

このようにデータを並び替えて coalesced access の条件を達成

Page 15: 格子 QCD における GPU 計算

15

3.8 divergent branch

ある格子点 (K) を計算する際、その隣の格子点 (S) にあるデータが必要

shared memory を用いる場合

2009/6/24

if (S は K と同じブロック ) → shared memory loadelse → global memory load

divergent branch !! 代わりに texture fetch を使う

任意の格子点 →  texture fetch load

multiprocessor あたりの texture cache 8KBshared memory は 16KB

No divergent branch !!

Page 16: 格子 QCD における GPU 計算

16

3.9 ここまでの結果

coalesced access and using No texture 40GFlops

coalesced access and using texture 50GFlops

coalesced access にすることで 速度は倍以上 !

texture fetch により、さらに速度 up!ただし、データは cache に乗りきらない

格子点 12.3KB + リンク 24.6KB > 8KBそれでも一部のデータは cache の恩恵を受けたた

め? 特にホッピングの計算部分の性能は 107GFlops

他の計算(内積等)が足を引っ張っている2009/6/24

Page 17: 格子 QCD における GPU 計算

17

4.1 内積の高速化

内積について高速化を行った 現在の内積は 17GFlops

bandwidthTest → 114GByte/s (GeForce GTX 280)

内積計算 : 2Byte/Flop理論性能 : 57GFlops

reduction のコードを kernel 5 → kernel 7NVIDIA_CUDA_SDK/projects/reduction/doc/

reduction.pdf要素数 2 冪以外に対応するように改造

2009/6/24

Page 18: 格子 QCD における GPU 計算

182009/6/24

template <unsigned int blockSize>__global__ void reduce6(int *g_idata, int *g_odata, unsigned int n){ extern __shared__ int sdata[]; unsigned int tid = threadIdx.x; unsigned int i = blockIdx.x* (blockSize*2) + tid; unsigned int gridSize = blockSize*2*gridDim.x; sdata[tid] = 0;

while (i < n) { sdata[tid] += g_idata[i] + g_idata[i+ blockSize]; i += gridSize; } __syncthreads();

if (blockSize >= 512) { if (tid < 256) { sdata[tid] += sdata[tid + 256]; } __syncthreads(); } if (blockSize >= 256) { if (tid < 128) { sdata[tid] += sdata[tid + 128]; } __syncthreads(); } if (blockSize >= 128) { if (tid < 64) { sdata[tid] += sdata[tid + 64]; } __syncthreads(); }

if (tid < 32) { if (blockSize >= 64) sdata[tid] += sdata[tid + 32]; if (blockSize >= 32) sdata[tid] += sdata[tid + 16]; if (blockSize >= 16) sdata[tid] += sdata[tid + 8]; if (blockSize >= 8) sdata[tid] += sdata[tid + 4]; if (blockSize >= 4) sdata[tid] += sdata[tid + 2]; if (blockSize >= 2) sdata[tid] += sdata[tid + 1]; } if (tid == 0) g_odata[blockIdx.x] = sdata[0];}

4.2 reduction : kernel 7

赤を消すと 2 冪以外にも対応可能

Page 19: 格子 QCD における GPU 計算

19

4.3 内積の高速化

reduction の部分は kernel 7 のコードを用いることにして、各成分同士の複素数 × 複素数の計算部分をコーディング

kernel 5 と kernel 7 は速度が倍違う → 倍速くなるだろう

2009/6/24

Page 20: 格子 QCD における GPU 計算

20

4.4 各成分の計算部分

2009/6/24

block 0 block 1 …

0 1 … 0 … 0 … 0 … 0 1 … …

float4 float4 float4

実部 虚部

x x

y y

z z

w w

実 虚a

x x

y y

z z

w w

実 虚b

a*b = c

x x

y y

z z

w w

実 虚c

=

float4 ar,ai,br,bi;

ar = (*a).b[blk].ri[0].c[site];ai = (*a).b[blk].ri[4].c[site];br = (*b).b[blk].ri[0].c[site];bi = (*b).b[blk].ri[4].c[site];

1thread 当りの計算

このやり方だと約20GFlops

Page 21: 格子 QCD における GPU 計算

21

4.5 各成分の計算部分の改良

2009/6/24

block 0

… …

float4 float4 float4

実部 虚部

x x

a

x x

b

a*b = c

x x

c=

float* xr,xi,yr,yi;float ar,ai,br,bi;

xr = &((*a).b[0].ri[0].c[0].x);xi = &((*a).b[0].ri[4].c[0].x);yr = &((*b).b[0].ri[0].c[0].x);yi = &((*b).b[0].ri[4].c[0].x);

ar = xr[i];ai = xi[i];br = yr[i];bi = yi[i];

1thread 当りの計算

強引に float アクセスに

y y

a

y y

b

y y

c=

1thread 当りの計算

32GFlops

Page 22: 格子 QCD における GPU 計算

22

4.6 内積の高速化

内積を計算する際の複素数 × 複素数の計算部分を float4 から float に変更見方を変えると並列度を上げたことに対応

結果、 20GFlops から 32GFlops に speed up!!元の 17GFlops から約 2 倍

全体の solver も 50GFlops → 55GFlops なぜ速くなったかはまだよくわかっていない

単純に float4 アクセスより float アクセスの方が速いのか?

並列度が上がった影響なのか?この結果は内積部分での話。他の演算ではどうか?texture経由での float4 アクセスはどうか?

2009/6/24

Page 23: 格子 QCD における GPU 計算

23

5. Fortran + Cuda

今回のプログラムは Fortan から cuda のコードを呼ぶように書いている

この書き方には標準がなく、プラットフォームやコンパイラによって異なる

今回は Linux 上の Intel コンパイラを使った時の話を紹介

2009/6/24

Page 24: 格子 QCD における GPU 計算

24

5.1 Fortran + cuda

基本的には Intel Fortran と C 間の場合と同じcuda 側の関数名の後ろにアンダースコア「_」を1つ付け加

えるcuda 側の関数宣言時、先頭に「 extern “C”」をつける引数を受け取る際、 cuda側ではポインタとして受け取る配列の順序コンパイル時に cudart 等へのリンク            

  等 ただし、 Fortran 上で GPU 上のメモリを扱うことが難

しかった (プログラムの先頭でメモリ確保し、後の呼び出しで引数として渡す )

今回はグローバル変数を用いて、 Fortran側にデバイスメモリが現れないようにコーディングを行った

2009/6/24

Page 25: 格子 QCD における GPU 計算

25

5.2 Fortran+cuda

2009/6/24

subroutine use_gpu_solver(...) ⋮As = A ! 倍→単 変換call new_gpu_solver(As,dA) ⋮do i = 0,1,2,... ⋮rs = r ! 倍→単 変換call s_bicgstab_gpu(rs,ps,dA,...)p = ps ! 単→倍 変換q = A * px = x + αpr = r – αq ⋮enddo

call delete_gpu_solver(dA) ⋮end subroutine

// s_bicgstab_gpu.cucuhgvfield dA;

extern “C”void new_gpu_solver_(cuhgvfield *As) { cudaMalloc((void**)&dA,...); cudaMemcpy(dA,As,...);}

extern ”C”void delete_gpu_solver_() { cudaFree((void *)dA);}

extern “C”void s_bicgstab_gpu_(hqfield *rs, hqfield *ps, .. ){ cuhqfield *dr; cudaMalloc((void**)&dr),...); ⋮ cudaMemcpy(ps,dp,...); cudaFree((void *)dr); ⋮}

Page 26: 格子 QCD における GPU 計算

26

5.3 Fortran+cuda (Makefile)

2009/6/24

Intel Fortran + nvcc

# Makefileall :: solver_main

GPULIB = GPUSolverLIB/libgpusolver.a

LIBDIR = -L$(CUDADIR)/lib \ –L$(CUDASDKDIR)/lib \ -L$(CUDASDKDIR)/common/libLIBS = $(LIBDIR) –lcudart –lcutil -lm

solver_main : $(OBJ) $(GPULIB) ifort $(LIBS) $(OBJ) $(GPULIB) \ –o $@

$(GPULIB) : (cd GPUSolverLIB; make)

# GPUSolverLIB/Makefileall :: libgpusolver.a

s_bicgstab_gpu.o : s_bicgstab_gpu.cu nvcc –c $< –o $@

libgpusolver.a : s_bicgstab_gpu.o ar cr $@ $<

cutil.h を include する場合

cuda を call する場合

Page 27: 格子 QCD における GPU 計算

27

6. おわりに

格子 QCD 計算の分野では大規模連立 1 次方程式を解く計算が大変

この計算を GPU によって加速 GPU で高速計算を実現するためにはメモリアク

セスにかなり気を配らないといけないCoalessed AccessDivergent WarpBank conflictfloat access ?

2009/6/24