More Related Content Similar to 2015年度GPGPU実践プログラミング 第12回 偏微分方程式の差分計算 (20) 2015年度GPGPU実践プログラミング 第12回 偏微分方程式の差分計算11. 差分法(理論的なお話)
空間打ち切り誤差
定義とテイラー展開の比較
テイラー展開を有限項で打ち切ったことによる誤差
3
33
2
22
!3!2
1)()(
dx
fdΔx
dx
fdΔx
ΔxΔx
xfΔxxf
dx
df
Δx
xfΔxxf
Δx
xfΔxxf
dx
df
Δx
)()()()(
lim
0
誤差
3
32
2
2
!3!2 dx
fdΔx
dx
fdΔx
GPGPU実践プログラミング12 2015/07/01
12. 2階微分の離散化
テイラー展開を応用
x方向にx離れた2点で展開
2式を足すと1階微分が消滅
3
33
2
22
!3!2
)()(
x
fΔx
x
fΔx
x
f
ΔxxfΔxxf
3
33
2
22
!3!2
)()(
x
fΔx
x
fΔx
x
f
ΔxxfΔxxf
2
22
!2
2)(2)()(
x
fΔx
xfΔxxfΔxxf
GPGPU実践プログラミング13 2015/07/01
19. #include<stdlib.h>
#include<math.h>/*‐lmオプションが必要*/
#define Lx (2.0*M_PI)
#define Nx (256)
#define dx (Lx/(Nx‐1))
#define Nbytes (Nx*sizeof(double))
#define dxdx (dx*dx)
void init(double *f){
int i;
for(i=0; i<Nx; i++){
f[i] = sin(i*dx);
}
}
void differentiate(double *f,
double *d2fdx2){
int i;
d2fdx2[0] = ( 2.0*f[0]
‐5.0*f[1]
+4.0*f[2]
‐ f[3])/dxdx;
for(i=1; i<Nx‐1; i++)
d2fdx2[i] = ( f[i+1]
‐2.0*f[i ]
+ f[i‐1])/dxdx;
d2fdx2[Nx‐1]=(‐ f[Nx‐4]
+4.0*f[Nx‐3]
‐5.0*f[Nx‐2]
+2.0*f[Nx‐1])/dxdx;
}
int main(void){
double *f,*d2fdx2;
f = (double *)malloc(Nbytes);
d2fdx2 = (double *)malloc(Nbytes);
init(f);
differentiate(f,d2fdx2);
return 0;
}
CPUプログラム
2015/07/01GPGPU実践プログラミング20
differentiate.c
23. #include<stdio.h>
#include<stdlib.h>
#include<math.h>/*‐lmオプションが必要*/
#define Lx (2.0*M_PI)
#define Nx (256)
#define dx (Lx/(Nx‐1))
#define Nbytes (Nx*sizeof(double))
#define dxdx (dx*dx)
#define NT (128)
#define NB (Nx/NT)
void init(double *f){
int i;
for(i=0; i<Nx; i++){
f[i] = sin(i*dx);
}
}
__global__ void differentiate
(double *f, double *d2fdx2){
int i = blockIdx.x*blockDim.x
+ threadIdx.x;
if(i==0)
d2fdx2[i] = ( 2.0*f[i ]
‐5.0*f[i+1]
+4.0*f[i+2]
‐ f[i+3])/dxdx;
if(0<i && i<Nx‐1)
d2fdx2[i] = ( f[i+1]
‐2.0*f[i ]
+ f[i‐1])/dxdx;
if(i==Nx‐1)
d2fdx2[i]=(‐ f[i‐3]
+4.0*f[i‐2]
‐5.0*f[i‐1]
+2.0*f[i ])/dxdx;
}
GPUプログラム
2015/07/01GPGPU実践プログラミング24
differentiate.cu
24. int main(void){
double *host_f,*host_d2fdx2;
double *f,*d2fdx2;
host_f =(double *)malloc(Nbytes);
cudaMalloc((void **)&f,Nbytes);
cudaMalloc((void **)&d2fdx2,Nbytes);
init(host_f);
cudaMemcpy(f, host_f, Nbytes, cudaMemcpyHostToDevice);
differentiate<<<NB, NT>>>(f, d2fdx2);
//host_d2fdx2=(double *)malloc(Nbytes);
//cudaMemcpy(host_d2fdx2, d2fdx2, Nbytes, cudaMemcpyDeviceToHost);
//for(int i=0; i<Nx; i++)printf("%f,%f,%f¥n",i*dx,host_f[i],host_d2fdx2[i]);
free(host_f);
free(host_d2fdx2);
cudaFree(f);
cudaFree(d2fdx2);
return 0;
}
GPUプログラム
2015/07/01GPGPU実践プログラミング25
differentiate.cu
26. 2次元への拡張
x方向2階偏微分
y方向を固定してx方向に偏微分
y方向2階偏微分
x方向を固定してy方向に偏微分
22
2
),(),(2),(
Δx
yΔxxfyxfyΔxxf
x
f
22
2
),(),(2),(
Δy
ΔyyxfyxfΔyyxf
y
f
GPGPU実践プログラミング27 2015/07/01
27. 時間積分
時間微分項の離散化
時間微分項を前進差分で離散化
右辺のt+tの項を移行
Δt
tyxfΔttyxf
t
f ),,(),,(
GPGPU実践プログラミング28 2015/07/01
f
t
f 2
t
f
ΔttyxfΔttyxf
),,(),,(
拡散方程式を代入
),,(),,(),,( 2
tyxfΔttyxfΔttyxf
f(x, y, t)の2階微分を計算できればf(x, y, t+t)が求められる
28. 離散化された方程式の記述
簡略化した表現
配列との対応をとるため下付き添字i, jを利用
時間は,上付き添字nを利用
jifyxf ,),(
jifyΔxxf ,1),(
1,),( jifΔyyxf
n
jiftyxf ,),,(
1
,),,(
n
jifΔttyxf
GPGPU実践プログラミング29 2015/07/01
29. 離散化された拡散方程式
連続系
離散系
t秒後の値
2
2
2
2
),,(),,(),,(
y
tyxf
x
tyxf
t
tyxf
2
1,,1,
2
,1,,1,
1
, 22
Δy
fff
Δx
fff
Δt
ff n
ji
n
ji
n
ji
n
ji
n
ji
n
ji
n
ji
n
ji
2
1,,1,
2
,1,,1
,
1
,
22
Δy
fff
Δx
fff
Δtff
n
ji
n
ji
n
ji
n
ji
n
ji
n
jin
ji
n
ji
GPGPU実践プログラミング30 2015/07/01
33. 計算手順
1. 計算条件の決定
計算領域の大きさLx, Ly
計算領域の分割数(離散点の個数)Nx, Ny
離散点同士の間隔(格子間隔)x, y
計算時間間隔t
2. 初期値の決定
fの初期分布の決定
3. 差分値の計算
fの分布からx, y方向の2階微分値を計算
境界条件に基づいて境界の値を決定
t秒後のfを計算
GPGPU実践プログラミング34 2015/07/01
34. CPUプログラム
計算条件の決定
計算領域の大きさLx, Ly
計算領域の分割数
(離散点の個数)Nx, Ny
離散点同士の間隔
(格子間隔)x, y
計算時間間隔t
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define Lx (1.0)
#define Ly (1.0)
#define Nx 128
#define Ny 128
#define dx (Lx/(Nx‐1))
#define dy (Ly/(Ny‐1))
#define dt 0.0001
GPGPU実践プログラミング35 2015/07/01
35. CPUプログラム
初期条件
境界条件
GPGPU実践プログラミング36 2015/07/01
)2.0(1
)2.0(0
r
r
f
1
1
x
y r
0
n
f
n
n
n: 外向き法線ベクトル
jiji
jiji
ff
Δx
ff
,1,1
,1,1
0
2
1,1,
1,1,
0
2
jiji
jiji
ff
Δy
ff
22
yxr
36. #include<stdlib.h>
#include<math.h>
#define Lx (1.0)
#define Ly (1.0)
#define Nx 128
#define Ny 128
#define dx (Lx/(Nx‐1))
#define dy (Ly/(Ny‐1))
#define dt 0.0001
#define endT (1.0)
#define Nt (int)(endT/dt)
#define DIFF (0.01)
#define dxdx (dx*dx)
#define dydy (dy*dy)
#define Nbytes (Nx*Ny*sizeof(double))
void init(double *, double *, double *);
void laplacian(double *, double *);
void integrate(double *, double *, double *);
void update(double *, double *);
int main(void){
double *f,*f_new,*f_lap,x,y;
int i,j,n;
f = (double *)malloc(Nx*Ny*sizeof(double));
f_new = (double *)malloc(Nx*Ny*sizeof(double));
f_lap = (double *)malloc(Nx*Ny*sizeof(double));
init(f, f_lap, f_new);
for(n=0;n<Nt;n++){
laplacian(f,f_lap);
integrate(f,f_lap,f_new);
update(f,f_new);
}
return 0;
}
CPUプログラム
2015/07/01GPGPU実践プログラミング37
diffusion.c
37. void init(double *f, double *f_lap, double *f_new){
int i,j,ij;
double x,y;
for(j=0;j<Ny;j++){
for(i=0;i<Nx;i++){
f [i+Nx*j] = 0.0;
f_lap[i+Nx*j] = 0.0;
f_new[i+Nx*j] = 0.0;
x=i*dx‐Lx/2.0;
y=j*dy‐Ly/2.0;
if(sqrt(x*x+y*y)<=0.2) f[i+Nx*j] = 1.0;
}
}
}
void laplacian(double *f, double *f_lap){
int i,j,ij,ip1j,im1j,ijp1,ijm1;
for(j=0;j<Ny;j++){
for(i=0;i<Nx;i++){
ij = i+Nx*j;
im1j=i‐1+Nx* j; if(i‐1< 0 )im1j=i+1+Nx*j;
ip1j=i+1+Nx* j; if(i+1>=Nx)ip1j=i‐1+Nx*j;
ijm1=i +Nx*(j‐1);if(j‐1< 0 )ijm1=i+Nx*(j+1);
ijp1=i +Nx*(j+1);if(j+1>=Ny)ijp1=i+Nx*(j‐1);
f_lap[ij] = (f[ip1j]‐2.0*f[ij]+f[im1j])/dxdx
+(f[ijp1]‐2.0*f[ij]+f[ijm1])/dydy;
}
}
}
void integrate
(double *f, double *f_lap, double *f_new){
int i,j,ij;
for(j=0;j<Ny;j++){
for(i=0;i<Nx;i++){
ij = i+Nx*j;
f_new[ij] = f[ij] + dt*DIFF*f_lap[ij];
}
}
}
void update(double *f, double *f_new){
int i,j,ij;
for(j=0;j<Ny;j++){
for(i=0;i<Nx;i++){
ij = i+Nx*j;
f[ij] = f_new[ij];
}
}
}
CPUプログラム
2015/07/01GPGPU実践プログラミング38
diffusion.c
38. CPUプログラム
差分計算
x方向,y方向偏微分を個別に計算して加算
void laplacian(double *f, double *f_lap){
int i,j,ij,ip1j,im1j,ijp1,ijm1;
for(j=0;j<Ny;j++){
for(i=0;i<Nx;i++){
ij =i +Nx* j;
im1j=i‐1+Nx* j; if(i‐1< 0 )im1j=i+1+Nx*j;
ip1j=i+1+Nx* j; if(i+1>=Nx)ip1j=i‐1+Nx*j;
ijm1=i +Nx*(j‐1);if(j‐1< 0 )ijm1=i+Nx*(j+1);
ijp1=i +Nx*(j+1);if(j+1>=Ny)ijp1=i+Nx*(j‐1);
f_lap[ij] = (f[ip1j]‐2.0*f[ij]+f[im1j])/dxdx
+(f[ijp1]‐2.0*f[ij]+f[ijm1])/dydy;
}
}
}
GPGPU実践プログラミング39 2015/07/01
44. CPUプログラム
境界条件
法線方向の1階微分が0
境界での2階の差分式
GPGPU実践プログラミング45 2015/07/01
f_lap[]
0
n
f
n: 外向き法線ベクトル
jiji
jiji
ff
Δx
ff
,1,1
,1,1
0
2
1,1,
1,1,
0
2
jiji
jiji
ff
Δy
ff
2
,,1
2
,,1
2
,1,,1 22
or
222
Δx
ff
Δx
ff
Δx
fff jijijijijijiji
2
,1,
2
,1,
2
1,,1, 22
or
222
Δy
ff
Δy
ff
Δy
fff jijijijijijiji
45. CPUプログラム
境界条件
参照する点が境界からはみ出して
いる場合は添字を修正
2階微分を計算する式は変更なし
右端ではi+1をi‐1に修正
左端ではi‐1をi+1に修正
上端ではj+1をj‐1に修正
下端ではj‐1をj+1に修正
GPGPU実践プログラミング46 2015/07/01
f_lap[]
if(i‐1< 0 )im1j=i+1+Nx*j;
if(i+1>=Nx)ip1j=i‐1+Nx*j;
if(j‐1< 0 )ijm1=i +Nx*(j+1);
if(j+1>=Ny)ijp1=i +Nx*(j‐1);
46. CPUプログラム
fの積分
void integrate(double *f, double *f_lap, double *f_new){
int i,j,ij;
for(j=0;j<Ny;j++){
for(i=0;i<Nx;i++){
ij = i+Nx*j;
f_new[ij] = f[ij] + dt*DIFF*f_lap[ij];
}
}
}
GPGPU実践プログラミング47 2015/07/01
2
1,,1,
2
,1,,1
,
1
,
22
Δy
fff
Δx
fff
Δtff
n
ji
n
ji
n
ji
n
ji
n
ji
n
jin
ji
n
ji
47. CPUプログラム
fの更新
fnからfn+1を計算
fn+1からfn+2を計算
今の時刻から次の時刻を求める
求められた次の時刻を今の時刻と見なし,次の時刻を求める
void update(double *f, double *f_new){
int i,j,ij;
for(j=0;j<Ny;j++){
for(i=0;i<Nx;i++){
ij = i+Nx*j;
f[ij] = f_new[ij];
}
}
}
GPGPU実践プログラミング48 2015/07/01
同じアルゴリズム
48. #include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define Lx (1.0)
#define Ly (1.0)
#define Nx 2048
#define Ny Nx
#define dx (Lx/(Nx‐1))
#define dy (Ly/(Ny‐1))
#define dt 0.000001
#define endT (1.0)
#define Nt (int)(endT/dt)
#define DIFF (0.01)
#define dxdx (dx*dx)
#define dydy (dy*dy)
#define Nbytes (Nx*Ny*sizeof(double))
#include "dif1.cu"
//#include "dif2.cu"
//#include "dif3.cu"
int main(void){
int n;
double *dev_f,*dev_f_new,*dev_f_lap;
dim3 Thread, Block;
if(DIFF*dt/dxdx > 0.5){
printf("configuration error¥n");
exit(1);
}
cudaMalloc( (void**)&dev_f , Nbytes );
cudaMalloc( (void**)&dev_f_new, Nbytes );
cudaMalloc( (void**)&dev_f_lap, Nbytes );
Thread = dim3(THREADX,THREADY,1);
Block = dim3(BLOCKX ,BLOCKY, 1);
init<<<Block, Thread>>>
(dev_f, dev_f_lap, dev_f_new);
for(n=1;n<=Nt;n++){
laplacian<<<Block, Thread>>>(dev_f,dev_f_lap);
integrate<<<Block, Thread>>>
(dev_f,dev_f_lap,dev_f_new);
update<<<Block, Thread>>>(dev_f,dev_f_new);
cudaDeviceSynchronize();
}
return 0;
}
GPUプログラム(CPU処理用共通部分)
2015/07/01GPGPU実践プログラミング49
diffusion.cu
49. #define THREADX 16
#define THREADY 16
#define BLOCKX (Nx/THREADX)
#define BLOCKY (Ny/THREADY)
__global__ void laplacian(double *f, double *f_lap){
int i,j,ij,im1j,ip1j,ijm1,ijp1;
i = blockIdx.x*blockDim.x + threadIdx.x;
j = blockIdx.y*blockDim.y + threadIdx.y;
ij =i +Nx* j;
im1j=i‐1+Nx* j; if(i‐1< 0 )im1j=i+1+Nx*j;
ip1j=i+1+Nx* j; if(i+1>=Nx)ip1j=i‐1+Nx*j;
ijm1=i +Nx*(j‐1);if(j‐1< 0 )ijm1=i +Nx*(j+1);
ijp1=i +Nx*(j+1);if(j+1>=Ny)ijp1=i +Nx*(j‐1);
f_lap[ij] = (f[ip1j]‐2.0*f[ij]+f[im1j])/dxdx
+(f[ijp1]‐2.0*f[ij]+f[ijm1])/dydy;
}
__global__ void integrate
(double *f, double *f_lap, double *f_new){
int i,j,ij;
i = blockIdx.x*blockDim.x + threadIdx.x;
j = blockIdx.y*blockDim.y + threadIdx.y;
ij = i+Nx*j;
f_new[ij] = f[ij] + dt*DIFF*f_lap[ij];
}
__global__ void update(double *f, double *f_new){
int i,j,ij;
i = blockIdx.x*blockDim.x + threadIdx.x;
j = blockIdx.y*blockDim.y + threadIdx.y;
ij = i+Nx*j;
f[ij] = f_new[ij];
}
__global__ void init
(double *f, double *f_lap, double *f_new){
int i,j,ij;
double x,y;
i = blockIdx.x*blockDim.x + threadIdx.x;
j = blockIdx.y*blockDim.y + threadIdx.y;
ij = i+Nx*j;
f [ij] = 0.0;
f_lap[ij] = 0.0;
f_new[ij] = 0.0;
x=i*dx‐Lx/2.0;
y=j*dy‐Ly/2.0;
if(sqrt(x*x+y*y)<=0.2) f[ij] = 1.0;
}
GPUプログラム(1スレッドが1点を計算)
2015/07/01GPGPU実践プログラミング50
dif1.cu
51. gnuplotによる結果の表示
2015/07/01GPGPU実践プログラミング52
表示可能なファイルフォーマット
データはスペース区切り
3次元表示では列(または行)の区切りとして空白行が必要
‐0.500000 ‐0.500000 0.000000e+00
‐0.499022 ‐0.500000 0.000000e+00
...
0.499022 ‐0.500000 0.000000e+00
0.500000 ‐0.500000 0.000000e+00
<‐改行
‐0.500000 ‐0.499022 0.000000e+00
‐0.499022 ‐0.499022 0.000000e+00
...
0.499022 ‐0.499022 0.000000e+00
0.500000 ‐0.499022 0.000000e+00
<‐改行
‐0.500000 ‐0.498045 0.000000e+00
‐0.499022 ‐0.498045 0.000000e+00
53. gnuplotによる結果の表示
2015/07/01GPGPU実践プログラミング54
スクリプトanim.gplの内容
set xrange [‐0.5:0.5] x軸の表示範囲を‐0.5~0.5に固定
set yrange [‐0.5:0.5] y軸の表示範囲を‐0.5~0.5に固定
set zrange [0:1] z軸の表示範囲を0~1に固定
set ticslevel 0 xy平面とz軸の最小値表示位置の差
set hidden3d 隠線消去をする
set view 45,30 視点をx方向45°,y方向30°に設定
set size 1,0.75 グラフの縦横比を1:0.75
unset contour 2次元等高線は表示しない
set surface 3次元で等値面を表示
unset pm3d カラー表示はしない
以下でファイルを読み込み,3次元表示
splot 'f0.00.txt' using 1:2:3 with line title "t=0.00s"
...
splot 'f1.00.txt' using 1:2:3 with line title "t=1.00s"
55. gnuplotによる結果の表示
2015/07/01GPGPU実践プログラミング56
スクリプトanim_color.gplの内容
set xrange [‐0.5:0.5] x軸の表示範囲を‐0.5~0.5に固定
set yrange [‐0.5:0.5] y軸の表示範囲を‐0.5~0.5に固定
set zrange [0:1] z軸の表示範囲を0~1に固定
set ticslevel 0 xy平面とz軸の最小値表示位置の差
set hidden3d 隠線消去をする
set view 45,30 視点をx方向45°,y方向30°に設定
set size 1,0.75 グラフの縦横比を1:0.75
unset contour 2次元等高線は表示しない
set surface 3次元で等値面を表示
set pm3d カラー表示する
set cbrange[0:1] カラーバーの表示範囲を0~1に固定
以下でファイルを読み込み,3次元表示
splot 'f0.00.txt' using 1:2:3 with line title "t=0.00s"
...
splot 'f1.00.txt' using 1:2:3 with line title "t=1.00s"
57. gnuplotによる結果の表示
2015/07/01GPGPU実践プログラミング58
スクリプトanim_2d.gplの内容
set xrange [‐0.5:0.5] x軸の表示範囲を‐0.5~0.5に固定
set yrange [‐0.5:0.5] y軸の表示範囲を‐0.5~0.5に固定
set zrange [0:1] z軸の表示範囲を0~1に固定
set view 0,0 真上から見下ろす
set size 1,1 グラフの縦横比を1:1
set contour 2次元等高線を表示
unset surface 3次元で等値面を表示しない
unset pm3d カラー表示しない
set cntrparam levels incremental 0,0.1,1
等高線を0から1まで0.1刻みに設定
以下でファイルを読み込み,3次元表示
splot 'f0.00.txt' using 1:2:3 with line title "t=0.00s"
...
splot 'f1.00.txt' using 1:2:3 with line title "t=1.00s"
65. 境界条件
2015/07/01GPGPU実践プログラミング66
2階微分が0
0
2
2
,1,,1
2
2
Δx
fff
x
f jijiji
jijiji fff ,1,,1 2
1,,1, 2 jijiji fff
0
2
2
1,,1,
2
2
Δy
fff
y
f jijiji
66. 境界条件
2015/07/01GPGPU実践プログラミング67
2階微分を片側差分で計算
3211 464 jjjjj fffff
2
11
2
321
2
2
2
452
Δy
fff
Δy
ffff
y
f
jjj
jjjj
2
11
2
321
2
2
2
452
Δx
fff
Δx
ffff
x
f
iii
iiii
3211 464 iiiii fffff
境界での2階微分の差分近似
境界外側の値
境界外側の値
71. GPUプログラム(ラプラシアンカーネル)
2015/07/01GPGPU実践プログラミング72
袖領域の設定
if(blockIdx.x == 0 && threadIdx.x == 0 ) sf[tx‐1][ty] = sf[tx+1][ty];
if(blockIdx.x != 0 && threadIdx.x == 0 ) sf[tx‐1][ty] = f[i‐1+Nx*j];
if(blockIdx.x == gridDim.x‐1 && threadIdx.x == blockDim.x‐1) sf[tx+1][ty] = sf[tx‐1][ty];
if(blockIdx.x != gridDim.x‐1 && threadIdx.x == blockDim.x‐1) sf[tx+1][ty] = f[i+1+Nx*j];
if(blockIdx.y == 0 && threadIdx.y == 0 ) sf[tx][ty‐1] = sf[tx][ty+1];
if(blockIdx.y != 0 && threadIdx.y == 0 ) sf[tx][ty‐1] = f[i+Nx*(j‐1)];
if(blockIdx.y == gridDim.y‐1 && threadIdx.y == blockDim.y‐1) sf[tx][ty+1] = sf[tx][ty‐1];
if(blockIdx.y != gridDim.y‐1 && threadIdx.y == blockDim.y‐1) sf[tx][ty+1] = f[i+Nx*(j+1)];
__syncthreads();
グローバルメモリにデータがある箇所はグロー
バルメモリから読み込み
グローバルメモリにデータがない箇所は2階微
分が0になるように袖領域の値を決定
ブロックが境界に接しているか否かで処理を切替
74. 2次元的な配列アクセスの優先方向
2015/07/01GPGPU実践プログラミング75
共有メモリの宣言の変更
1次元目と2次元目を入れ替え
sf[1+THREADX+1][1+THREADY+1]
sf[1+THREADY+1][1+THREADX+1]
配列参照
1次元目の添字はthreadIdx.yを利用
2次元目の添字はthreadIdx.xを利用
threadIdx.xのスレッド群(threadIdx.y
が一定)が連続なメモリアドレスにアクセス
sf[THREADX+2][THREADY+2]
threadIdx.x
threadIdx.y
THREADX+2
THREADY+2
sf[THREADY+2][THREADX+2]
threadIdx.y
threadIdx.x
THREADY+2
THREADX+2
79. GPUプログラムの評価
2015/07/01GPGPU実践プログラミング80
if分岐の書き方による実行速度の変化
ブロックとスレッドの条件を同時に記述
Warp内のスレッドが分岐すると実行速度が著しく低下
GPUはブロック単位で分岐,Warp単位で分岐しても実行速度
の低下は抑えられる
上の書き方は最適ではない?
ブロック単位の分岐とスレッド単位の分岐を同時に記述
if(blockIdx.x == 0 && threadIdx.x == 0 ) sf[ty][tx‐1] = sf[ty][tx+1];
if(blockIdx.x != 0 && threadIdx.x == 0 ) sf[ty][tx‐1] = f[i‐1+Nx*j];
if(blockIdx.x == gridDim.x‐1 && threadIdx.x == blockDim.x‐1) sf[ty][tx+1] = sf[ty][tx‐1];
if(blockIdx.x != gridDim.x‐1 && threadIdx.x == blockDim.x‐1) sf[ty][tx+1] = f[i+1+Nx*j];
if(blockIdx.y == 0 && threadIdx.y == 0 ) sf[ty‐1][tx] = sf[ty+1][tx];
if(blockIdx.y != 0 && threadIdx.y == 0 ) sf[ty‐1][tx] = f[i+Nx*(j‐1)];
if(blockIdx.y == gridDim.y‐1 && threadIdx.y == blockDim.y‐1) sf[ty+1][tx] = sf[ty‐1][tx];
if(blockIdx.y != gridDim.y‐1 && threadIdx.y == blockDim.y‐1) sf[ty+1][tx] = f[i+Nx*(j+1)];
80. GPUプログラムの評価
2015/07/01GPGPU実践プログラミング81
if分岐の書き方による実行速度の変化
ブロックとスレッドの条件を分け,ifを入れ子に
ブロック単位で分岐した後,スレッド単位で分岐
ブロック単位での分岐による速度低下が抑えられそうな気がする
ブロック単位の分岐を記述する8個のifは2個一組(同じ色の
行)で排他的に分岐
blockIdx.x==0が成立すると,blockIdx!=0は絶対に成立しない
if(blockIdx.x == 0 )if(threadIdx.x == 0 ) sf[ty][tx‐1] = sf[ty][tx+1];
if(blockIdx.x != 0 )if(threadIdx.x == 0 ) sf[ty][tx‐1] = f[i‐1+Nx*j];
if(blockIdx.x == gridDim.x‐1)if(threadIdx.x == blockDim.x‐1) sf[ty][tx+1] = sf[ty][tx‐1];
if(blockIdx.x != gridDim.x‐1)if(threadIdx.x == blockDim.x‐1) sf[ty][tx+1] = f[i+1+Nx*j];
if(blockIdx.y == 0 )if(threadIdx.y == 0 ) sf[ty‐1][tx] = sf[ty+1][tx];
if(blockIdx.y != 0 )if(threadIdx.y == 0 ) sf[ty‐1][tx] = f[i+Nx*(j‐1)];
if(blockIdx.y == gridDim.y‐1)if(threadIdx.y == blockDim.y‐1) sf[ty+1][tx] = sf[ty‐1][tx];
if(blockIdx.y != gridDim.y‐1)if(threadIdx.y == blockDim.y‐1) sf[ty+1][tx] = f[i+Nx*(j+1)];
81. GPUプログラムの評価
2015/07/01GPGPU実践プログラミング82
if分岐の書き方による実行速度の変化
2個一組のifの片方をelseに書き換え
2個一組のifにおける条件判断は1回限り
2回ifの条件判断を行うよりは速いような気がする
if(blockIdx.x == 0 ){if(threadIdx.x == 0 ) sf[ty][tx‐1] = sf[ty][tx+1];}
else {if(threadIdx.x == 0 ) sf[ty][tx‐1] = f[i‐1+Nx*j];}
if(blockIdx.x == gridDim.x‐1){if(threadIdx.x == blockDim.x‐1) sf[ty][tx+1] = sf[ty][tx‐1];}
else {if(threadIdx.x == blockDim.x‐1) sf[ty][tx+1] = f[i+1+Nx*j];}
if(blockIdx.y == 0 ){if(threadIdx.y == 0 ) sf[ty‐1][tx] = sf[ty+1][tx];}
else {if(threadIdx.y == 0 ) sf[ty‐1][tx] = f[i+Nx*(j‐1)];}
if(blockIdx.y == gridDim.y‐1){if(threadIdx.y == blockDim.y‐1) sf[ty+1][tx] = sf[ty‐1][tx];}
else {if(threadIdx.y == blockDim.y‐1) sf[ty+1][tx] = f[i+Nx*(j+1)];}