poisson方程式の求解 (線形連立一次方程式)»Š回の内容 675...
TRANSCRIPT
Poisson方程式の求解(線形連立一次方程式)
長岡技術科学大学 電気電子情報工学専攻 出川智啓
今回の内容
2015/06/18先端GPGPUシミュレーション工学特論675
偏微分方程式の分類
Laplace方程式とその解法 Jacobi法,Gauss‐Seidel法,SOR法
2次元Laplace方程式
共役勾配法
SOR法(RB‐SOR法)
偏微分方程式の簡単な分類
2015/06/18先端GPGPUシミュレーション工学特論676
2個の独立変数f(x,y)に対する2階線形偏微分方程式 A, B, C, D, E, F, Gは定数もしくはx, yの関数
高階(2階)微分の係数による分類
02
22
2
2
GFf
yfE
xfD
yfC
yxfB
xfA
B2‐4AC 型 物理的挙動
=0 放物型 時間発展
>0 双曲型 時間発展
<0 楕円型 空間平衡
放物型
2015/06/18先端GPGPUシミュレーション工学特論677
2
2
xf
xfc
tf
移流拡散方程式
−A=(>0), D=c, E=1, B=C=F=G=0 yをtと形式的に置換
移流項によってfが形を保ったまま速度cで移流
拡散項によってfの形状がなだらかに減衰
計算するためには初期値とt>0における境界値が必要
初期値・境界値問題
双曲型
2015/06/18先端GPGPUシミュレーション工学特論678
波動方程式
−A=c2, C=1, B=D=E=F=G=0 yをtと形式的に置換
時間的に振動する問題を記述
fは時刻t,位置xにおける振動の変位
cは振動の位相速度
計算するためには初期値とt>0における境界値が必要
初期値・境界値問題
2
22
2
2
xfc
tf
楕円型
2015/06/18先端GPGPUシミュレーション工学特論679
Laplace方程式
A=1, C=1, B=D=E=F=G=0 G≠0の場合はPoisson方程式
式中に時間微分項が存在しない
fの平衡状態(時間的変化が0の定常状態)を表す
計算するためには境界値が重要
境界値問題
02
2
2
2
yf
xf
Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論680
順問題 関数fの値・形が既知
2階微分を計算
逆問題 関数fのラプラシアンが0 関数fの形状を推定
?2
2
2
2
yf
xf
f
02
2
2
2
yf
xf
?f
Laplace方程式の離散化
x方向2階偏微分の離散化 y方向を固定してx方向に偏微分
y方向2階偏微分の離散化 x方向を固定してy方向に偏微分
2,1,,1
22
2 2),(),(2),(Δx
fffΔx
yΔxxfyxfyΔxxfxf jijiji
21,,1,
22
2 2),(),(2),(Δy
fffΔy
ΔyyxfyxfΔyyxfyf jijiji
先端GPGPUシミュレーション工学特論681 2015/06/18
Laplace方程式の離散化
Laplace方程式
離散化までは拡散方程式と同じ fが未知
離散化されたf同士の関係(差分式)と境界条件から推定
022
21,,1,
2,1,,1
2
2
2
2
Δyfff
Δxfff
yf
xf jijijijijiji
先端GPGPUシミュレーション工学特論682 2015/06/18
1次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論683
x方向のみを考慮
L=4を5個の離散点に分割 各位置iでの差分式(i=1,5では境界条件が必要)
02
02
02
2543
2432
2321
Δxfff
Δxfff
Δxfff
022
112
2
Δxfff
xf iii
1次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論684
境界条件(左の境界でf=0, 右の境界でf=1)を課す
f1~f5を未知数とした連立一次方程式
1
02
02
020
5
2543
2432
2321
1
fΔx
fffΔx
fffΔx
ffff
L
f
1次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論685
行列を用いた表記 係数行列Aの逆行列A−1を左からかければfが求められる
逆行列の計算は計算負荷が高く,多くの誤差が混入
連立一次方程式の求解法を利用してfを求める
10000
1121
121121
1
5
4
3
2
1
fffff
Ax=b
連立一次方程式の解法
2015/06/18先端GPGPUシミュレーション工学特論686
直接法
係数行列を単位行列(や上三角,下三角行列)に変形することで未知数を求める方法
所定の計算回数で解が得られる
計算量が多く,大規模な問題には適用できない
反復法
係数行列を変更せず,未知数に推定値を代入して所定の計算を行い,推定値が厳密解に十分近づくまで計算を繰り返す方法
よい推定値を選べば非常に高速に解が得られる
Jacobi法
2015/06/18先端GPGPUシミュレーション工学特論687
未知数=...の形に変形し近似値を繰り返し計算 初期の推定値f(0)を基にf(1), f(2), f(3),..., f(m)を計算
f(m)の誤差が十分小さくなればf(m)を解とする(反復が収束)
f(m)を元の方程式に代入して右辺と比較する 等
5.020
020020
5)0(
3)1(
4
)0(4
)0(2
)1(3
)0(31
)1(2
ffffff
fff
1
02
02
020
5
2543
2432
2321
1
fΔx
fffΔx
fffΔx
ffff
Jacobi法
2015/06/18先端GPGPUシミュレーション工学特論688
未知数=...の形に変形し近似値を繰り返し計算 初期の推定値f(0)を基にf(1), f(2), f(3),..., f(m)を計算
f(m)の誤差が十分小さくなればf(m)を解とする(反復が収束)
f(m)を元の方程式に代入して右辺と比較する 等
5.020
25.020020
5)1(
3)2(
4
)1(4
)1(2
)2(3
)1(31
)2(2
ffffff
fff
1
02
02
020
5
2543
2432
2321
1
fΔx
fffΔx
fffΔx
ffff
Jacobi法
2015/06/18先端GPGPUシミュレーション工学特論689
未知数=...の形に変形し近似値を繰り返し計算 初期の推定値f(0)を基にf(1), f(2), f(3),..., f(m)を計算
f(m)の誤差が十分小さくなればf(m)を解とする(反復が収束)
f(m)を元の方程式に代入して右辺と比較する 等
625.020
25.020125.020
5)2(
3)3(
4
)2(4
)2(2
)3(3
)2(31
)3(2
ffffff
fff
1
02
02
020
5
2543
2432
2321
1
fΔx
fffΔx
fffΔx
ffff
Jacobi法
2015/06/18先端GPGPUシミュレーション工学特論690
未知数=...の形に変形し近似値を繰り返し計算 初期の推定値f(0)を基にf(1), f(2), f(3),..., f(m)を計算
f(m)の誤差が十分小さくなればf(m)を解とする(反復が収束)
f(m)を元の方程式に代入して右辺と比較する 等
625.020
375.020125.020
5)4(
3)4(
4
)4(4
)4(2
)4(3
)4(31
)4(2
ffffff
fff
1
02
02
020
5
2543
2432
2321
1
fΔx
fffΔx
fffΔx
ffff
Gauss-Seidel法
2015/06/18先端GPGPUシミュレーション工学特論691
既に計算したf(m+1)の値を同一反復内で利用 更新した値を順次使用するため収束が速くなる
5.020
020020
5)1(
3)1(
4
)0(4
)1(2
)1(3
)0(31
)1(2
ffffff
fff
625.020
25.020020
5)2(
3)2(
4
)1(4
)2(2
)2(3
)1(31
)2(2
ffffff
fff
Gauss‐Seidel法Jacobi法
5.020
020020
5)0(
3)1(
4
)0(4
)0(2
)1(3
)0(31
)1(2
ffffff
fff
5.020
25.020020
5)1(
3)2(
4
)1(4
)1(2
)2(3
)1(31
)2(2
ffffff
fff
Gauss-Seidel法
2015/06/18先端GPGPUシミュレーション工学特論692
既に計算したf(m+1)の値を同一反復内で利用 更新した値を順次使用するため収束が速くなる
0.68820
0.37520125.020
5)3(
3)3(
4
)2(4
)3(2
)3(3
)2(31
)3(2
ffffff
fff
0.71920
0.438200.187520
5)4(
3)4(
4
)3(4
)4(2
)4(3
)3(31
)4(2
ffffff
fff
Gauss‐Seidel法Jacobi法
625.020
25.020125.020
5)2(
3)3(
4
)2(4
)2(2
)3(3
)2(31
)3(2
ffffff
fff
625.020
375.020125.020
5)3(
3)4(
4
)3(4
)3(2
)4(3
)3(31
)4(2
ffffff
fff
SOR法(逐次過緩和法, Successive 0ver-Relaxation)
2015/06/18先端GPGPUシミュレーション工学特論693
近似解の修正量に係数をかけて過剰に修正 加速係数が0<<2の範囲で収束
1<<2を取ると収束が加速
0<<1なら収束が減速
696.020
36.020020
)1(45
)2(3
)1(4
)2(4
)1(3
)1(4
)2(2
)1(3
)2(3
)1(2
)1(31
)1(2
)2(2
ffffffffff
fffff
6.020
020020
)0(45
)1(3
)0(4
)1(4
)0(3
)0(4
)1(2
)0(3
)1(3
)0(2
)0(31
)0(2
)1(2
ffffffffff
fffff
2.1
SOR法(逐次過緩和法, Successive 0ver-Relaxation)
2015/06/18先端GPGPUシミュレーション工学特論694
近似解の修正量に係数をかけて過剰に修正 加速係数が0<<2の範囲で収束
1<<2を取ると収束が加速
0<<1なら収束が減速
749.020
498.020242.020
)3(45
)4(3
)3(4
)4(4
)3(3
)3(4
)4(2
)3(3
)4(3
)3(2
)3(31
)3(2
)4(2
ffffffffff
fffff
746.020
475.020216.020
)2(45
)3(3
)2(4
)3(4
)2(3
)2(4
)3(2
)2(3
)3(3
)2(2
)2(31
)2(2
)3(2
ffffffffff
fffff
2.1
共役勾配法
2015/06/18先端GPGPUシミュレーション工学特論695
Jacobi法などとは異なる種類の反復解法
係数行列が対称である連立一次方程式が対象
丸め誤差が無ければ所定の回数で収束 丸め誤差に弱く,所定の回数で収束しないこともある
共役勾配法のアルゴリズム
2015/06/18先端GPGPUシミュレーション工学特論696
Compute r0=b−Ax0. Set p0−1=0,−1=0.
For m=0,1,…, until ||r||/||b|| < , Do
p(m) = r(m)+(m−1)p(m−1)
(m) = (r(m), r(m))/(p(m), Ap(m))
x(m+1) = x(m)+(m)p(m)
r(m+1) = r(m)−(m)Ap(m)
(m) = (r(m+1), r(m+1))/{(m)(p(m), Ap(m))}
EndDo Apは係数行列Aとベクトルpの積( , )はベクトル同士の内積||・||はl2−ノルム
2次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論697
1方向から加熱される金属板の温度分布 Laplace方程式
境界条件
1℃
0℃0℃
0℃
B2
B1
B3
B4
4
3
2
1
on 0on 0on 1on 0
BTBTBTBT
02
2
2
2
yT
xT
xy
2次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論698
1方向から加熱される金属板の温度分布 Laplace方程式
計算条件
計算領域 Lx=2, Ly=2 離散点の数 Nx=3, Ny=3 格子間隔 x=y=h
02
2
2
2
yT
xT
xy
0
i
1 20
1
2
j
2次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論699
1方向から加熱される金属板の温度分布 Laplace方程式
Laplace方程式の差分式
02
2
2
2
yT
xT
0
i
1 2
0
1
2
j
T[i][j]
022
21,,1,
2,1,,1
yTTT
xTTT jijijijijiji
04
21,,1,1,,1
hTTTTT jijijijiji
2次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論700
1方向から加熱される金属板の温度分布 Laplace方程式
Laplace方程式の差分式
0
i
1 2
0
1
2
j
T[]
0 1 2
3 4 5
6 7 8
0222
1472
345
yTTT
xTTT
042
13475
h
TTTTT
02
2
2
2
yT
xT
連立一次方程式
2015/06/18先端GPGPUシミュレーション工学特論701
Tに関する9元連立一次方程式を解く
111000000
11
11
114111
11
1
8
7
6
5
4
3
2
1
0
TTTTTTTTT
Ax=b
連立一次方程式
2015/06/18先端GPGPUシミュレーション工学特論702
行列Aを保持する必要は無い SOR法はT=… の形を作り,反復計算を行う
変形した式をプログラムに記述周囲が全て固定値の境界条件なので1回の反復で収束
1110
40000
7531
8
7
6
5
)(4
3
2
1
0
TTTT
TTTT
TTTTT
m
連立一次方程式
2015/06/18先端GPGPUシミュレーション工学特論703
行列Aを保持する必要は無い 共役勾配法は行列-ベクトル積Axの結果がわかればよい
行列-ベクトル積を計算して得られた式をプログラムに記述
8
7
6
5
75431
3
2
1
0
8
7
6
5
4
3
2
1
0
4
11
11
114111
11
1
TTTT
TTTTTTTTT
TTTTTTTTT
#include<stdio.h>#include<stdlib.h>#include<math.h>
#define Lx 2.0#define Ly Lx#define Nx 11#define Ny Ny#define dx (Lx/(Nx‐1))#define dy dx#define dxdx (dx*dx)#define dydy (dy*dy)#define Nbytes (Nx*Ny*sizeof(double))#define ERR_TOL 1e‐12#define Accel 1.0
void sor(double *T);void cg(double *T);void init(double *T){
for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){
T[i+Nx*j] = 0.0;}}
}
int main(void){
double *T;T = (double *)malloc(Nbytes);
init(T);sor(T)//cg(T);
return 0;}
CPUプログラム(メイン・初期化)
2015/06/18先端GPGPUシミュレーション工学特論704
laplace.c
void sor(double *T){int ite_SOR=0;double err_n,err_d,err_relative;double d_t;int ij,ip1j,im1j,ijm1,ijp1;//境界条件の適用for(int i=0;i<Nx;i++){ij = i+Nx*(Ny‐1); //j=Ny‐1T[i+Nx*j] = 1.0;
}
do{err_n = 0.0;err_d = 0.0;
for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );
im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);d_t =( (T[im1j]+T[ip1j])
+(T[ijm1]+T[ijp1]))/(‐4.0) ‐T[ij];
T[ij] += Accel*d_t;err_n += d_t*d_t;err_d += T[ij]*T[ij];
}}
if(err_d<1e‐20)err_d=1.0;err_relative = sqrt(err_n/err_d);ite_SOR++;
}while(err_relative > ERR_TOL);}
CPUプログラム(SOR法)
2015/06/18先端GPGPUシミュレーション工学特論705
laplace.c
void cg(double *T){double err;int ij,ip1j,im1j,ijm1,ijp1;double *p,*r,*Ap;double alph,beta,rr,pAp;//境界条件の適用for(int i=0;i<Nx;i++){ij=i+Nx*(Ny‐1);//j=Ny‐1T[i+Nx*j] = 1.0;
}
p = (double *)malloc(Nbytes);r = (double *)malloc(Nbytes);Ap = (double *)malloc(Nbytes);for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;p [ij] = 0.0;r [ij] = 0.0;
Ap[ij] = 0.0;}}alph=0.0;beta=0.0;
rr = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);r[ij] = ‐( (T[im1j]‐2*T[ij]+T[ip1j])/dxdx
+(T[ijm1]‐2*T[ij]+T[ijp1])/dydy);
rr += r[ij]*r[ij];}}
CPUプログラム(共役勾配法)
2015/06/18先端GPGPUシミュレーション工学特論706
laplace.c
do{for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;p [ij] = r[ij] + beta*p[ij];
}}
pAp = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);Ap[ij] = (p[im1j]‐2*p[ij]+p[ip1j])/dxdx
+(p[ijm1]‐2*p[ij]+p[ijp1])/dydy;
pAp += p[ij]*Ap[ij];
}}alph = rr/pAp;
rr = 0.0;for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;T[ij] = T[ij] + alph* p[ij];r[ij] = r[ij] ‐ alph*Ap[ij];rr += r[ij]*r[ij];
}}err = sqrt(rr);beta = rr/(alph*pAp);
}while(err > ERR_TOL);}
CPUプログラム(共役勾配法)
2015/06/18先端GPGPUシミュレーション工学特論707
laplace.c
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論708
誤差 真の値からのズレ
近似値 =真値r+誤差
誤差
絶対誤差
相対誤差
rr ˆ
r̂
rr ˆ
rrr ˆ
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論709
ベクトル量における誤差の評価 lp−ノルム(pは整数)
l1−ノルム
l2−ノルム
l∞−ノルム
pN
i
p
iip xxl ˆx2
x1
x
x̂
22111 ˆˆ xxxxl
2
22
2
112 ˆˆ xxxxl
2211 ˆ,ˆmax xxxxl
11 x̂x
22 x̂x
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論710
l2−ノルムを用いた相対誤差評価
m+1回目の反復の値が真値になれば誤差と一致
反復中に変化量が十分小さくなれば収束と判定
x2
y x
y x
N
j
N
i
mji
N
j
N
i
mji
mji
T
TT
2)1(,
2)(,
)1(,
err_n = 0.0;err_d = 0.0;
for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){
d_t =( (T[im1j]+T[ip1j])+(T[ijm1]+T[ijp1])
)/(‐4.0) ‐T[ij];T[ij] += Accel*d_t;err_n += d_t*d_t;err_d += T[ij]*T[ij];
}}
if(err_d<1e‐20)err_d=1.0;err_relative = sqrt(err_n/err_d);
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論711
残差 方程式の解に近似値を用いたことによって生じる不整合
残差rの定義
誤差と残差 誤差の計算には真の解が必要
残差は方程式の係数と右辺値がわかれば計算できる
xAbr ˆ
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論712
残差の2乗ノルムを用いた収束判定 右辺の2乗ノルムを用いた相対残差を計算 x2
y x
y x
N
j
N
iji
N
j
N
i
mji
b
r
2,
2)1(,
rr = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){
ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);r[ij] = ‐( (T[im1j]‐2*T[ij]+T[ip1j])/dxdx
+(T[ijm1]‐2*T[ij]+T[ijp1])/dydy);rr += r[ij]*r[ij];
}}・・・//右辺が0でなければ右辺のl2ノルムbbで//割った値の平方根sqrt(rr/bb)を評価err = sqrt(rr);
計算結果
2015/06/18先端GPGPUシミュレーション工学特論713
前回授業で用いたgnuplotのanim_2d.gplを流用して2次元分布を確認
set xrange [0:2] x軸の表示範囲を0~2に固定set yrange [0:2] y軸の表示範囲を0~2に固定set zrange [0:1] z軸の表示範囲を0~1に固定set view 0,0 真上から見下ろすset size 1,1 グラフの縦横比を1:1set contour 2次元等高線を表示unset surface 3次元で等値面を表示しないunset pm3d カラー表示しないset cntrparam levels incremental 0,0.1,1
等高線を0から1まで0.1刻みに設定以下でファイルを読み込み,3次元表示splot 'T.txt' with line
計算結果
2015/06/18先端GPGPUシミュレーション工学特論714
前回授業で用いたgnuplotのanim_2d.gplを流用して2次元分布を確認
set xrange [0:2] x軸の表示範囲を0~2に固定set yrange [0:2] y軸の表示範囲を0~2に固定set zrange [0:1] z軸の表示範囲を0~1に固定set view 0,0 真上から見下ろすset size 1,1 グラフの縦横比を1:1unset contour 2次元等高線を表示unset surface 3次元で等値面を表示しないset pm3d カラー表示しない以下でファイルを読み込み,3次元表示splot 'T.txt' with line
Poisson方程式への拡張
2015/06/18先端GPGPUシミュレーション工学特論715
Poisson方程式
離散化 右辺が0ではなく関数gの値
gyf
xf
2
2
2
2
jijijijijijiji g
Δyfff
Δxfff
yf
xf
.21,,1,
2,1,,1
2
2
2
2 22
SOR法で用いる式の拡張
2015/06/18先端GPGPUシミュレーション工学特論716
xとyが等しくない場合 差分式を変形してfi,j=… の形に変形
jijijijijijiji g
Δyfff
Δxfff
.21,,1,
2,1,,1 22
21,1,
2,1,1
.,22
112Δy
ffΔx
ffgf
ΔyΔxjijijiji
jiji
22
22
21,1,
2,1,1
.,2
ΔyΔxΔyΔx
Δyff
Δxff
gf jijijijijiji
#define dxdxdydy (dxdx*dydy)#define dxdy2 (2.0*(dxdx+dydy))
void sor(double *f, double *g){int ite_SOR=0;double err_n,err_d,err_relative;double d_f;int ij,ip1j,im1j,ijm1,ijp1;do{err_n = 0.0;err_d = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);d_f =( (f[im1j]+f[ip1j])/dxdx
+(f[ijm1]+f[ijp1])/dydy‐(g[ij]) //右辺の影響)*dxdxdydy/dxdy2 ‐f[ij];
f[ij] += Accel*d_f;err_n += d_f*d_f;err_d += f[ij]*f[ij];
}}
if(err_d<1e‐20)err_d=1.0;err_relative = sqrt(err_n/err_d);ite_SOR++;
}while(err_relative > ERR_TOL);
}
CPUプログラム(SOR法)
2015/06/18先端GPGPUシミュレーション工学特論717
poisson.c
#define dxdxdydy (dxdx*dydy)#define dxdy2 (2.0*(dxdx+dydy))
void sor(double *f, double *g){int ite_SOR=0;double err_n,err_d,err_relative;double d_f;int ij,ip1j,im1j,ijm1,ijp1;do{err_n = 0.0;err_d = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);d_f =( (f[im1j]+f[ip1j])/dxdx
+(f[ijm1]+f[ijp1])/dydy‐(g[ij]) //右辺の影響)*dxdxdydy/dxdy2 ‐f[ij];
f[ij] += Accel*d_f;err_n += d_f*d_f;err_d += f[ij]*f[ij];
}}
if(err_d<1e‐20)err_d=1.0;err_relative = sqrt(err_n/err_d);ite_SOR++;
}while(err_relative > ERR_TOL);
}
CPUプログラム(共役勾配法)
2015/06/18先端GPGPUシミュレーション工学特論718
poisson.c
do{for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;p [ij] = r[ij] + beta*p[ij];
}}
pAp = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);//右辺の影響は入らないAp[ij] = (p[im1j]‐2*p[ij]+p[ip1j])/dxdx
+(p[ijm1]‐2*p[ij]+p[ijp1])/dydy;
pAp += p[ij]*Ap[ij];}}alph = rr/pAp;
rr = 0.0;for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;f[ij] = f[ij] + alph* p[ij];r[ij] = r[ij] ‐ alph*Ap[ij];rr += r[ij]*r[ij];
}}err = sqrt(rr/bb);beta = rr/(alph*pAp);
}while(err > ERR_TOL);}
CPUプログラム(共役勾配法)
2015/06/18先端GPGPUシミュレーション工学特論719
poisson.c
CPUプログラム
右辺(ソース項)g
境界条件
for(i=0;i<Nx;i++){for(j=0;j<Ny;j++){
x = (double)i*dx;y = (double)j*dy;g[i*Ny+j] = 2.0*sin(2.0*M_PI*x/Lx)
*sin(2.0*M_PI*y/Ly);}}
先端GPGPUシミュレーション工学特論720 2015/06/18
yxg sinsin2
0sinsin22 yxf
2
2
g2−2
#include<stdio.h>#include<stdlib.h>#include<math.h>
#define Lx (2.0*M_PI)#define Ly Lx#define Nx 11#define Ny Ny#define dx (Lx/(Nx‐1))#define dy dx#define dxdx (dx*dx)#define dydy (dy*dy)#define dxdxdydy (dxdx*dydy)#define dxdy2 (2.0*(dxdx+dydy))#define Nbytes (Nx*Ny*sizeof(double))#define ERR_TOL 1e‐12#define Accel 1.0void sor(double *f,double *g);void cg(double *f,double *g);
void init(double *f,double *g){for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){g[i+Nx*j] = 2.0*sin(2.0*M_PI*x/Lx)
*sin(2.0*M_PI*y/Ly);f[i+Nx*j] = 0.0;
}}
}int main(void){
double *f, *g;f = (double *)malloc(Nbytes);g = (double *)malloc(Nbytes);
init(f,g);sor(f,g)//cg(f,g);return 0;
}
CPUプログラム(メイン・初期化)
2015/06/18先端GPGPUシミュレーション工学特論721
poisson.c
void sor(double *f, double *g){int ite_SOR=0;double err_n,err_d,err_relative;double d_f;int ij,ip1j,im1j,ijm1,ijp1;
//境界値は全て0固定なので何もしない
do{err_n = 0.0;err_d = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);
d_f =( (f[im1j]+f[ip1j])/dxdx+(f[ijm1]+f[ijp1])/dydy‐(g[ij]))*dxdxdydy/dxdy2 ‐f[ij];
f[ij] += Accel*d_f;err_n += d_f*d_f;err_d += f[ij]*f[ij];
}}
if(err_d<1e‐20)err_d=1.0;err_relative = sqrt(err_n/err_d);ite_SOR++;
}while(err_relative > ERR_TOL);
}
CPUプログラム(SOR法)
2015/06/18先端GPGPUシミュレーション工学特論722
poisson.c
void cg(double *f, double *g){double err;int ij,ip1j,im1j,ijm1,ijp1;double *p,*r,*Ap;double alph,beta,rr,pAp,bb;
p = (double *)malloc(Nbytes);r = (double *)malloc(Nbytes);Ap = (double *)malloc(Nbytes);for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;p [ij] = 0.0;r [ij] = 0.0;Ap[ij] = 0.0;
}}alph=0.0;beta=0.0;
//境界値は全て0固定なので何もしないbb = 0.0;rr = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);r[ij] = g[ij]‐((f[im1j]‐2*f[ij]+f[ip1j])/dxdx
+(f[ijm1]‐2*f[ij]+f[ijp1])/dydy);
rr += r[ij]*r[ij];bb += g[ij]*g[ij];
}}
CPUプログラム(共役勾配法)
2015/06/18先端GPGPUシミュレーション工学特論723
poisson.c
do{for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;p [ij] = r[ij] + beta*p[ij];
}}
pAp = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);
Ap[ij] = (p[im1j]‐2*p[ij]+p[ip1j])/dxdx
+(p[ijm1]‐2*p[ij]+p[ijp1])/dydy;
pAp += p[ij]*Ap[ij];}}alph = rr/pAp;
rr = 0.0;for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;f[ij] = f[ij] + alph* p[ij];r[ij] = r[ij] ‐ alph*Ap[ij];rr += r[ij]*r[ij];
}}err = sqrt(rr/bb);beta = rr/(alph*pAp);
}while(err > ERR_TOL);}
CPUプログラム(共役勾配法)
2015/06/18先端GPGPUシミュレーション工学特論724
poisson.c
計算結果
2015/06/18先端GPGPUシミュレーション工学特論725
前回授業で用いたgnuplotのanim_2d.gplを流用して2次元分布を確認
set xrange [0:2*pi] x軸の表示範囲を0~2に固定set yrange [0:2*pi] y軸の表示範囲を0~2に固定set zrange [‐1:1] z軸の表示範囲を‐1~1に固定set view 0,0 真上から見下ろすset size 1,1 グラフの縦横比を1:1set contour 2次元等高線を表示unset surface 3次元で等値面を表示しないunset pm3d カラー表示しないset cntrparam levels incremental ‐1,0.1,1
等高線を0から1まで0.1刻みに設定以下でファイルを読み込み,3次元表示splot 'f.txt' with line
計算結果
2015/06/18先端GPGPUシミュレーション工学特論726
前回授業で用いたgnuplotのanim_2d.gplを流用して2次元分布を確認
set xrange [0:2*pi] x軸の表示範囲を0~2に固定set yrange [0:2*pi] y軸の表示範囲を0~2に固定set zrange [‐1:1] z軸の表示範囲を‐1~1に固定set view 0,0 真上から見下ろすset size 1,1 グラフの縦横比を1:1unset contour 2次元等高線を表示unset surface 3次元で等値面を表示しないset pm3d カラー表示しない以下でファイルを読み込み,3次元表示splot 'f.txt' with line
GPUへの移植
2015/06/18先端GPGPUシミュレーション工学特論727
共役勾配法のGPU移植は比較的簡単 これまでの授業内容の知識で実装可能
第7回総和計算
第8回偏微分方程式の差分計算(拡散方程式)
SOR法のGPU移植は非常に困難 処理の過程で格子点のデータに依存性が存在
fi,j(m+1)の値を計算するためにfi−1,j
(m+1), fi,j−1(m+1)の値を利用
特殊な並列化方法を導入
void cg(double *f, double *g){double err;int ij,ip1j,im1j,ijm1,ijp1;double *p,*r,*Ap;double alph,beta,rr,pAp,bb;
p = (double *)malloc(Nbytes);r = (double *)malloc(Nbytes);Ap = (double *)malloc(Nbytes);for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;p [ij] = 0.0;r [ij] = 0.0;Ap[ij] = 0.0;
}}alph=0.0;beta=0.0;
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論728
1スレッドが配列の1要素に0を代入容易に並列化・GPU移植可能
bb = 0.0;rr = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);
r[ij] = g[ij]‐((f[im1j]‐2*f[ij]+f[ip1j])/dxdx
+(f[ijm1]‐2*f[ij]+f[ijp1])/dydy);
rr += r[ij]*r[ij];bb += g[ij]*g[ij];
}}
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論729
残差を計算するカーネルと内積を計算するカーネルに分離・残差の計算には拡散方程式のカーネル・内積の計算は総和計算のカーネルを流用
容易ではないが,これまでの知識で並列化・GPU移植可能
do{for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;p [ij] = r[ij] + beta*p[ij];
}}
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論730
ベクトル和と同様に1スレッドが配列の1要素の計算を実行容易に並列化・GPU移植可能
pAp = 0.0;for(int j=1;j<Ny‐1;j++){for(int i=1;i<Nx‐1;i++){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);
Ap[ij] = (p[im1j]‐2*p[ij]+p[ip1j])/dxdx+(p[ijm1]‐2*p[ij]+p[ijp1])/dydy;
pAp += p[ij]*Ap[ij];}}alph = rr/pAp;
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論731
残差を計算するカーネルと内積を計算するカーネルに分離・残差の計算には拡散方程式のカーネル・内積の計算は総和計算のカーネルを流用
容易ではないが,これまでの知識で並列化・GPU移植可能
rr = 0.0;for(int j=0;j<Ny;j++){for(int i=0;i<Nx;i++){ij = i+Nx*j;f[ij] = f[ij] + alph* p[ij];r[ij] = r[ij] ‐ alph*Ap[ij];rr += r[ij]*r[ij];
}}err = sqrt(rr/bb);beta = rr/(alph*pAp);
}while(err > ERR_TOL);
}
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論732
ベクトル和と同様に1スレッドが配列の1要素の計算を実行容易に並列化・GPU移植可能
//メモリの確保・解放は比較的負荷が高いので,mainで確保して関数に渡すvoid cg(double *f, double *g, double *r, double *p, double *Ap){double err;double alph,beta,rr,pAp,bb;
initAuxVectors<<<Block, Thread>>>(r,p,Ap); cudaThreadSynchronize();alph=0.0;beta=0.0;
bb = dot_product(g,g); cudaThreadSynchronize();computeResidual<<<Block, Thread>>>(f,g,r); cudaThreadSynchronize();rr = dot_product(r,r); cudaThreadSynchronize();
do{computeAuxVector<<<Block, Thread>>>(p,r,beta);cudaThreadSynchronize();
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論733
1台のGPUの全スレッドを同期(不要な箇所もある)
computeMxV<<<Block, Thread>>>(Ap,p); cudaThreadSynchronize();pAp = dot_product(p,Ap); cudaThreadSynchronize();alph = rr/pAp;
update<<<Block, Thread>>>(f,r); cudaThreadSynchronize();rr = dot_product(r,r); cudaThreadSynchronize();
err = sqrt(rr/bb);beta = rr/(alph*pAp);
}while(err > ERR_TOL);
}
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論734
SOR法の並列化
2015/06/18先端GPGPUシミュレーション工学特論735
SOR法の処理の手順 更新手順に依存性が存在
Jacobi法なら依存性問題は回避できるが収束が遅すぎる
並列化の影響で反復毎に更新順序が変化すると収束が保証されない
配列順序・計算順序の並び替え(オーダリング)を導入
f[]
i
j
22
22
2
)1(1,
)(1,
2
)1(,1
)(,1
.
)1(, 2
ΔyΔxΔyΔx
Δyff
Δxff
g
f
mji
mji
mji
mji
ji
mji
Red-Blackオーダリング
2015/06/18先端GPGPUシミュレーション工学特論736
更新順序を2段階に分離 赤色の要素を参照し,黒色の要素全てを更新
黒色の要素を参照し,赤色の要素全てを更新f[]
i
j
i
j
i
fr[] fb[]
Red-Blackオーダリング
2015/06/18先端GPGPUシミュレーション工学特論737
更新順序を2段階に分離 赤色の要素を参照し,黒色の要素全てを更新
黒色の要素を参照し,赤色の要素全てを更新f[]
i
j
fr[]
i
j
fb[]
i
Red-Black SOR法
2015/06/18先端GPGPUシミュレーション工学特論738
赤色もしくは黒色の要素は常にm+1の値を用いて計算 SOR法よりも速く収束
22
22
2
)(1,
)(1,
2
)(,1
)(,1
.)1(
,2
ΔyΔxΔyΔx
Δyff
Δxff
gfmji
mji
mji
mji
jimji
22
22
2
)1(1,
)1(1,
2
)1(,1
)1(,1
.)1(
,2
ΔyΔxΔyΔx
Δyff
Δxff
gfmji
mji
mji
mji
jimji
even is when ,...5,3,1odd is when ,...6,4,2
,1,2,...,2,1jiji
NNj yy
odd is when ,...5,3,1even is when ,...6,4,2
,1,2,...,2,1jiji
NNj yy
void rbsor(double *f, double *g){
int ite_SOR=0;double err_n,err_d,err_relative;double d_f;int ij,ip1j,im1j,ijm1,ijp1;
do{err_n = 0.0;err_d = 0.0;//赤色要素を計算for(int j=1;j<Ny‐1;j++){for(int i=1+j%2;i<Nx‐1;i+=2){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);d_f =( (f[im1j]+f[ip1j])/dxdx
+(f[ijm1]+f[ijp1])/dydy‐(g[ij]))*dxdxdydy/dxdy2 ‐f[ij];
f[ij] += Accel*d_f;err_n += d_f*d_f;err_d += f[ij]*f[ij];
}}
CPUプログラム(RB-SOR法)
2015/06/18先端GPGPUシミュレーション工学特論739
poisson.c
CPUプログラム(RB-SOR法)
2015/06/18先端GPGPUシミュレーション工学特論740
poisson.c
//黒色要素を計算for(int j=1;j<Ny‐1;j++){for(int i=1+(j‐1)%2;i<Nx‐1;i+=2){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);d_f =( (f[im1j]+f[ip1j])/dxdx
+(f[ijm1]+f[ijp1])/dydy‐(g[ij]))*dxdxdydy/dxdy2 ‐f[ij];
f[ij] += Accel*d_f;err_n += d_f*d_f;err_d += f[ij]*f[ij];
}}
if(err_d<1e‐20)err_d=1.0;err_relative = sqrt(err_n/err_d);ite_SOR++;
}while(err_relative > ERR_TOL);
}
赤色要素の計算
2015/06/18先端GPGPUシミュレーション工学特論741
j=1(奇数)のときに計算するiは2,4,6 j=2(偶数)のときに計算するiは1,3,5 計算の範囲は0<i<Nx‐1,0<j<Ny‐1
f[]
0 1 2 3 4 5 6 70
1 2 3
4 5 6 7
iの 小値は1,jが奇数の時+1,偶数の時+0jを2で割った剰余を利用してiの開始点を決定i=1+j%2
for(int j=1;j<Ny‐1;j++){for(int i=1+j%2;i<Nx‐1;i+=2){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);d_f =( (f[im1j]+f[ip1j])/dxdx
+(f[ijm1]+f[ijp1])/dydy‐(g[ij]))*dxdxdydy/dxdy2 ‐f[ij];
f[ij] += Accel*d_f;}}
黒色要素の計算
2015/06/18先端GPGPUシミュレーション工学特論742
j=1(奇数)のときに計算するiは1,3,5 j=2(偶数)のときに計算するiは2,4,6 計算の範囲は0<i<Nx‐1,0<j<Ny‐1
for(int j=1;j<Ny‐1;j++){for(int i=1+(j+1)%2;i<Nx‐1;i+=2){ij = i+Nx*j;ip1j = (i+1)+Nx*(j );im1j = (i‐1)+Nx*(j );ijp1 = (i )+Nx*(j+1);ijm1 = (i )+Nx*(j‐1);d_f =( (f[im1j]+f[ip1j])/dxdx
+(f[ijm1]+f[ijp1])/dydy‐(g[ij]))*dxdxdydy/dxdy2 ‐f[ij];
f[ij] += Accel*d_f;}}
f[]
0 1 2 3 4 5 6 70
1 2 3
4 5 6 7
iの 小値は1,jが奇数の時+0,偶数の時+1jを2で割った剰余を利用してiの開始点を決定i=1+(j+1)%2 (=2‐j%2とも書ける)
GPUへの移植
2015/06/18先端GPGPUシミュレーション工学特論743
赤色要素を計算するカーネルと黒色要素を計算するカーネルを分離
反復のループ制御や収束判定はCPUで実行
収束判定の誤差の指標にはl∞−ノルムを採用 l2−ノルムを用いると内積計算が発生
収束判定用の変数を用意(値を1に初期化)
各スレッドが計算した誤差が許容値より大きい場合は収束判定用変数に0を書き込む
収束していない格子点があれば反復を繰り返す
#include<stdio.h>#include<stdlib.h>#include<math.h>
#define Lx (2.0*M_PI)#define Ly (2.0*M_PI)#define Nx 256#define Ny Nx#define Nbytes (Nx*Ny*sizeof(double))#define dx (Lx/(Nx‐1))#define dy (Ly/(Ny‐1))
#define THREADX 16#define THREADY 16#define BLOCKX (Nx/THREADX)#define BLOCKY (Ny/THREADY)
void output(double *value, char *name);
#include"rbsor.cu"
__global__ void init(double *f, double *g){
int i=blockIdx.x*blockDim.x+threadIdx.x;int j=blockIdx.y*blockDim.y+threadIdx.y;int ij = i+Nx*j;
double x = (double)i*dx;double y = (double)j*dy;g[ij] = 2.0*sin(2.0*M_PI*x/Lx)
*sin(2.0*M_PI*y/Ly);f[ij] = 0.0;
}
GPUプログラム(メイン)
2015/06/18先端GPGPUシミュレーション工学特論744
poisson.cu
int main(void){
double *f,*g;double *dev_f,*dev_g;dim3 Block, Thread;
cudaSetDevice(3);cudaSetDeviceFlags(cudaDeviceMapHost);
cudaMalloc((void **)&dev_f, Nbytes);cudaMalloc((void **)&dev_g, Nbytes);
Thread = dim3(THREADX,THREADY,1);Block = dim3(BLOCKX,BLOCKY,1);init<<<Block, Thread >>>(dev_f,dev_g);
printf("iteration = %d¥n",rbsor(dev_f,dev_g));
f=(double *)malloc(Nbytes);
g=(double *)malloc(Nbytes);cudaMemcpy(f,dev_f,Nbytes, cudaMemcpyDeviceToHost);cudaMemcpy(g,dev_g,Nbytes, cudaMemcpyDeviceToHost);
output(f,"f.txt");output(g,"g.txt");
cudaFree(dev_f);cudaFree(dev_g);free(f);free(g);return 0;
}
GPUプログラム(メイン)
2015/06/18先端GPGPUシミュレーション工学特論745
poisson.cu
int rbsor(double *f, double *g){
int ite_SOR=0;int *converged,*conv=(int *)malloc(sizeof(int));dim3 Block= dim3(SORBx,SORBy,1), Thread= dim3(SORTx,SORTy,1);cudaMalloc((void **)&converged, sizeof(int));
do{ite_SOR++;*conv = 1; //1なら収束,0ならどこかの要素の値が収束していないcudaMemcpy(converged, conv, sizeof(int),cudaMemcpyHostToDevice);computeRed <<<Block, Thread>>>(f,g,converged);computeBlack<<<Block, Thread>>>(f,g,converged);//cudaThreadSynchronize();cudaMemcpy(conv, converged, sizeof(int),cudaMemcpyDeviceToHost);
}while(*conv == 0 && ite_SOR<1024*1024*1024);
free(conv);cudaFree(converged);return ite_SOR;
}
RB-SOR法(ループ制御部分)
2015/06/18先端GPGPUシミュレーション工学特論746
rbsor.cu
int rbsor(double *f, double *g){
int ite_SOR=0;int *converged,*conv;dim3 Block= dim3(SORBx,SORBy,1), Thread= dim3(SORTx,SORTy,1);
//ホストメモリに置かれたconvをGPUから読み書きcudaHostAlloc((void **)&conv, sizeof(int),cudaHostAllocMapped);cudaHostGetDevicePointer(&converged, conv, 0);
do{ite_SOR++;*conv = 1; //1なら収束,0ならどこかの要素の値が収束していないcomputeRed <<<Block, Thread>>>(f,g,converged);computeBlack<<<Block, Thread>>>(f,g,converged);//cudaThreadSynchronize();
}while(*conv == 0 && ite_SOR<1024*1024*1024);
cudaFreeHost(conv);return ite_SOR;
}
RB-SOR法(ループ制御部分)
2015/06/18先端GPGPUシミュレーション工学特論747
rbsor.cu
__global__ void computeRed(double *f,double *g, int *converged){
int j = blockIdx.y* blockDim.y + threadIdx.y;int i = blockIdx.x*(blockDim.x*2) + threadIdx.x*2 + (j+1)%2;int ij = i+Nx*j;int ip1j = (i+1)+Nx*(j );int im1j = (i‐1)+Nx*(j );int ijp1 = (i )+Nx*(j+1);int ijm1 = (i )+Nx*(j‐1);double d_f;
if(0<i && i<Nx‐1 && 0<j && j<Ny‐1){d_f =( (f[im1j]+f[ip1j])/dxdx
+(f[ijm1]+f[ijp1])/dydy‐(g[ij])
)*dxdxdydy/dxdy2 ‐f[ij];f[ij] += Accel*d_f;if(abs(d_f) > ERR_TOL) *converged = 0;
}}
RB-SOR法(赤色要素の計算)
2015/06/18先端GPGPUシミュレーション工学特論748
rbsor.cu
__global__ void computeBlack(double *f,double *g, int *converged){
int j = blockIdx.y* blockDim.y + threadIdx.y;int i = blockIdx.x*(blockDim.x*2) + threadIdx.x*2 + (j)%2;int ij = i+Nx*j;int ip1j = (i+1)+Nx*(j );int im1j = (i‐1)+Nx*(j );int ijp1 = (i )+Nx*(j+1);int ijm1 = (i )+Nx*(j‐1);double d_f;
if(0<i && i<Nx‐1 && 0<j && j<Ny‐1){d_f =( (f[im1j]+f[ip1j])/dxdx
+(f[ijm1]+f[ijp1])/dydy‐(g[ij])
)*dxdxdydy/dxdy2 ‐f[ij];f[ij] += Accel*d_f;if(abs(d_f) > ERR_TOL) *converged = 0;
}}
RB-SOR法(黒色要素の計算)
2015/06/18先端GPGPUシミュレーション工学特論749
rbsor.cu
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論750
初期化 1スレッドが一つの配列要素を計算
f[]
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7#define THREADX 16
#define THREADY 16#define BLOCKX (Nx/THREADX)#define BLOCKY (Ny/THREADY)
Thread = dim3(THREADX,THREADY,1);Block = dim3(BLOCKX,BLOCKY,1);init<<<Block, Thread >>>
(dev_f,dev_g);
i=blockIdx.x*blockDim.x+threadIdx.x;j=blockIdx.y*blockDim.y+threadIdx.y;
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論751
RB‐SOR法 i方向の計算に必要なスレッド数は初期化の1/2 必要なブロック数は同じ
f[]
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7#define SORTx (THREADX/2)
#define SORTy (THREADY)#define SORBx (BLOCKX)#define SORBy (BLOCKY)
dim3 Block, Thread;Thread = dim3(SORTx,SORTy,1);Block = dim3(SORBx,SORBy,1);computeRed<<<Block, Thread>>>
(f,g,converged);
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論752
RB‐SOR法 スレッド番号と配列添字をどう対応させるか
j方向は変更無し,i方向のみ変化
f[]
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7#define SORTx (THREADX/2)
#define SORTy (THREADY)#define SORBx (BLOCKX)#define SORBy (BLOCKY)
dim3 Block, Thread;Thread = dim3(SORTx,SORTy,1);Block = dim3(SORBx,SORBy,1);computeRed<<<Block, Thread>>>
(f,g,converged);
ストライドアクセス時の配列添字の決定
N=12, <<<3, 2>>>で実行
2015/06/18先端GPGPUシミュレーション工学特論753
c[i]
a[i]
b[i]
0 1 0
+ + + +
1
gridDim.x=3
blockDim.x=2
0 1
+ +
blockDim.x=2 blockDim.x=2blockIdx.x=0 blockIdx.x=1 blockIdx.x=2
threadIdx.x=
i= 0 1 2 3 4 5 6 7 8 9 10 11
配列の要素番号iの計算
2015/06/18先端GPGPUシミュレーション工学特論754
threadIdx.xとiの対応
iの決定 配列サイズ=ブロックの数×1ブロックあたりのスレッドの数
配列サイズ=ブロックの数×1ブロックあたりのスレッドの数×計算する点の間隔
blockIdx.x 0 1 2
threadIdx.x 0 1 0 1 0 1
i 0 2 4=4+0
6=4+2
8=8+0
10=8+2
i+threadIdx.x×2
ストライドアクセス時の配列添字の決定
N=12, <<<3, 2>>>で実行
2015/06/18先端GPGPUシミュレーション工学特論755
c[i]
a[i]
b[i]
0 1 0
+ + + +
1
gridDim.x=3
blockDim.x=2
0 1
+ +
blockDim.x=2 blockDim.x=2blockIdx.x=0 blockIdx.x=1 blockIdx.x=2
threadIdx.x=
i= 0 1 2 3 4 5 6 7 8 9 10 11= blockIdx.x*(blockDim.x*2) + threadIdx.x*2
計算する点の間隔 計算する点の間隔
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論756
i方向のスレッド番号と配列添字の対応 ストライドアクセス
f[]
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7
j = blockIdx.y* blockDim.y + threadIdx.y;i = blockIdx.x*(blockDim.x*2) + threadIdx.x*2;
j
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論757
i方向のスレッド番号と配列添字の対応 ストライドアクセス
開始点の変化(jが奇数のとき0開始,偶数のときは1開始)
f[]
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7
+0+1+0+1
j
(j+1)%2
j = blockIdx.y* blockDim.y + threadIdx.y;i = blockIdx.x*(blockDim.x*2) + threadIdx.x*2
+ (j+1)%2;
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論758
RB‐SOR法 スレッド番号と配列添字をどう対応させるか
j方向は変更無し,i方向のみ変化
f[]
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7#define SORTx (THREADX/2)
#define SORTy (THREADY)#define SORBx (BLOCKX)#define SORBy (BLOCKY)
dim3 Block, Thread;Thread = dim3(SORTx,SORTy,1);Block = dim3(SORBx,SORBy,1);computeBlack<<<Block, Thread>>>
(f,g,converged);
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論759
i方向のスレッド番号と配列添字の対応 ストライドアクセス
f[]
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7
j = blockIdx.y* blockDim.y + threadIdx.y;i = blockIdx.x*(blockDim.x*2) + threadIdx.x*2;
j
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論760
i方向のスレッド番号と配列添字の対応 ストライドアクセス
開始点の変化(jが奇数のとき0開始,偶数のときは1開始)
f[]
0 1 2 3 4 5 6 7
0 1 2 3
4 5 6 7
+1+0+1+0
j
(j)%2
j = blockIdx.y* blockDim.y + threadIdx.y;i = blockIdx.x*(blockDim.x*2) + threadIdx.x*2
+ (j)%2;
RB-SOR法の性能改善
2015/06/18先端GPGPUシミュレーション工学特論761
赤色要素,黒色要素の計算はストライドアクセス グローバルメモリにコアレスアクセスできない
読み込み速度が低下
赤色要素,黒色要素の計算でカーネルを切り替えるため,共有メモリが利用できない
メモリアクセスを改善したい グローバルメモリにコアレスアクセスしたい
赤色要素を格納する配列と黒色要素を格納する配列を分離
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論762
色ごとに格納する配列を区別 ストライドアクセスを排除
元の配列を分離,出力時に結合するカーネルを作成
f[]
i
j
fr[]
i
j
fb[]
i
int rbsor(double *f, double *g){
double *fr,*fb,*gr,*gb;int ite_SOR=0;int *converged,*conv;
dim3 BlockSplit= dim3(SplitBx,SplitBy,1),ThreadSplit= dim3(SplitTx,SplitTy,1);dim3 BlockMerge= dim3(MergeBx,MergeBy,1),ThreadMerge= dim3(MergeTx,MergeTy,1);dim3 BlockSOR = dim3( SORBx, SORBy,1),ThreadSOR = dim3( SORTx, SORTy,1);
cudaHostAlloc((void **)&conv, sizeof(int),cudaHostAllocMapped);cudaHostGetDevicePointer(&converged, conv, 0);
cudaMalloc((void **)&fr, Nbytes/2);cudaMalloc((void **)&fb, Nbytes/2);cudaMalloc((void **)&gr, Nbytes/2);cudaMalloc((void **)&gb, Nbytes/2);
split<<<BlockSplit, ThreadSplit>>>(f,fr,fb);split<<<BlockSplit, ThreadSplit>>>(g,gr,gb);
RB-SOR法(ループ制御部分)
2015/06/18先端GPGPUシミュレーション工学特論763
rbsor_split.cu
do{ite_SOR++;*conv = 1;
computeRed <<<BlockSOR, ThreadSOR>>>(fr, fb, gr, gb,converged);computeBlack<<<BlockSOR, ThreadSOR>>>(fb, fr, gb, gr, converged);cudaThreadSynchronize();
}while(*conv == 0 && ite_SOR<1024*1024*1024);
merge<<<BlockMerge, ThreadMerge>>>(f,fr,fb);
cudaFreeHost(conv);cudaFree(fr);cudaFree(fb);cudaFree(gr);cudaFree(gb);
return ite_SOR;}
配列を分離するRB-SOR法(ループ制御部分)
2015/06/18先端GPGPUシミュレーション工学特論764
rbsor_split.cu
__global__ void split(double *f, double *fr,double *fb){
__shared__ double sf[THREADX][THREADY];__shared__ double sfrb[THREADX][THREADY];
int tx = threadIdx.x;int ty = threadIdx.y;
int j = blockIdx.y* blockDim.y + threadIdx.y;int i = blockIdx.x*(blockDim.x*2) + threadIdx.x;int ij = i+Nx*j;
sf[tx][ty] = f[ij];__syncthreads();
i = blockIdx.x*(blockDim.x*2) + threadIdx.x+ blockDim.x;
ij = i+Nx*j;sf[tx+blockDim.x][ty] = f[ij];__syncthreads();
sfrb[tx ][ty]= sf[tx*2+(j+1)%2][ty];
sfrb[tx+blockDim.x][ty]= sf[tx*2+(j )%2][ty];
__syncthreads();
i = blockIdx.x*blockDim.x + threadIdx.x;ij= i+Nx/2*j;fr[ij] = sfrb[tx ][ty];fb[ij] = sfrb[tx+blockDim.x][ty];
}
配列を分離するRB-SOR法(配列分離)
2015/06/18先端GPGPUシミュレーション工学特論765
rbsor_split.cu
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論766
共有メモリ上に配列を宣言
グローバルメモリからコピーするための配列
並び替え後のデータを格納する配列
f[]__shared__ double sf[THREADX][THREADY];__shared__ double sfrb[THREADX][THREADY];
int tx = threadIdx.x;int ty = threadIdx.y;int j = blockIdx.y* blockDim.y + threadIdx.y;int i = blockIdx.x*(blockDim.x*2) + threadIdx.x;int ij = i+Nx*j;
sf[tx][ty] = f[ij];__syncthreads();
sf[][]
sfrb[][]
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論767
共有メモリ上に配列を宣言
グローバルメモリからコピーするための配列
並び替え後のデータを格納する配列
グローバルメモリから共有メモリにコピーして同期を取る
f[]__shared__ double sf[THREADX][THREADY];__shared__ double sfrb[THREADX][THREADY];
int tx = threadIdx.x;int ty = threadIdx.y;int j = blockIdx.y* blockDim.y + threadIdx.y;int i = blockIdx.x*(blockDim.x*2) + threadIdx.x;int ij = i+Nx*j;
sf[tx][ty] = f[ij];__syncthreads();
sf[][]
0 1 2 3
0 1 2 3
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論768
i方向の位置を変更してさらにグローバルメモリから共有メモリにコピー
f[]
i = blockIdx.x*(blockDim.x*2) + threadIdx.x+ blockDim.x;
ij = i+Nx*j;sf[tx+blockDim.x][ty] = f[ij];__syncthreads();
sf[][]
0 1 2 3
0 1 2 3
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論769
共有メモリ上でストライドアクセスして赤色要素と黒色要素を並び替え
jの位置に応じてiの位置を変更
f[]
sfrb[tx ][ty]= sf[tx*2+(j+1)%2][ty];
sf[][]
0 1 2 3
0 1 2 3
sfrb[][]
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論770
共有メモリ上でストライドアクセスして赤色要素と黒色要素を並び替え
jの位置に応じてiの位置を変更
並び替え後の配列に赤色要素を書込
f[]
sfrb[tx ][ty]= sf[tx*2+(j+1)%2][ty];
sf[][]
0 1 2 3
0 1 2 3
sfrb[][]
+0+1+0+1
(j+1)%2
0 1 2 3
0 1 2 3
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論771
共有メモリ上でストライドアクセスして赤色要素と黒色要素を並び替え
jの位置に応じてiの位置を変更
f[]
sfrb[tx+blockDim.x][ty]= sf[tx*2+(j )%2][ty];
__syncthreads();
sf[][]
0 1 2 3
0 1 2 3
sfrb[][]
0 1 2 3
0 1 2 3
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論772
共有メモリ上でストライドアクセスして赤色要素と黒色要素を並び替え
jの位置に応じてiの位置を変更
並び替え後の配列に黒色要素を書込
f[]
sfrb[tx+blockDim.x][ty]= sf[tx*2+(j )%2][ty];
__syncthreads();
sf[][]
0 1 2 3
0 1 2 3
sfrb[][]
+1+0+1+0
(j )%2
0 1 2 3
0 1 2 3
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論773
共有メモリの赤色,黒色要素を分離した配列に書込
各配列にコアレスアクセスで書込
i = blockIdx.x*blockDim.x + threadIdx.x;ij= i+Nx/2*j;fr[ij] = sfrb[tx ][ty];fb[ij] = sfrb[tx+blockDim.x][ty];
fr[] fb[]
sfrb[][]
0 1 2 3
0 1 2 3
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論774
共有メモリの赤色,黒色要素を分離した配列に書込
各配列にコアレスアクセスで書込
i = blockIdx.x*blockDim.x + threadIdx.x;ij= i+Nx/2*j;fr[ij] = sfrb[tx ][ty];fb[ij] = sfrb[tx+blockDim.x][ty];
fr[] fb[]
sfrb[][]
0 1 2 3
0 1 2 3
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
__global__ void merge(double *f, double *fr,double *fb){
__shared__ double sf[THREADX][THREADY];__shared__ double sfrb[THREADX][THREADY];
int tx = threadIdx.x;int ty = threadIdx.y;
int j = blockIdx.y*blockDim.y + threadIdx.y;int i = blockIdx.x*blockDim.x + threadIdx.x;int ij = i+Nx/2*j;
sfrb[tx ][ty] = fr[ij];sfrb[tx+blockDim.x][ty] = fb[ij];__syncthreads();
sf[tx*2+(j+1)%2][ty]= sfrb[tx ][ty];
sf[tx*2+(j )%2][ty]= sfrb[tx+blockDim.x][ty];
__syncthreads();
i = blockIdx.x*(blockDim.x*2) + threadIdx.x;ij= i+Nx*j;f[ij] = sf[tx ][ty];
i = blockIdx.x*(blockDim.x*2) + threadIdx.x+ blockDim.x;
ij= i+Nx*j;f[ij] = sf[tx+blockDim.x][ty];
}
配列を分離するRB-SOR法(配列結合)
2015/06/18先端GPGPUシミュレーション工学特論775
rbsor_split.cu
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論776
赤色要素をグローバルメモリから読み込み,共有メモリへ書込
コアレスアクセスで書込
__shared__ double sf[THREADX][THREADY];__shared__ double sfrb[THREADX][THREADY];
int tx = threadIdx.x;int ty = threadIdx.y;int j = blockIdx.y*blockDim.y + threadIdx.y;int i = blockIdx.x*blockDim.x + threadIdx.x;sfrb[tx ][ty] = fr[ij];sfrb[tx+blockDim.x][ty] = fb[ij];__syncthreads();
fr[] fb[]
sfrb[][]
0 1 2 3
0 1 2 3
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論777
黒色要素をグローバルメモリから読み込み,共有メモリへ書込
コアレスアクセスで書込
__shared__ double sf[THREADX][THREADY];__shared__ double sfrb[THREADX][THREADY];
int tx = threadIdx.x;int ty = threadIdx.y;int j = blockIdx.y*blockDim.y + threadIdx.y;int i = blockIdx.x*blockDim.x + threadIdx.x;sfrb[tx ][ty] = fr[ij];sfrb[tx+blockDim.x][ty] = fb[ij];__syncthreads();
fr[] fb[]
sfrb[][]
0 1 2 3
0 1 2 3
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論778
並び替え後の配列内の赤色要素を並び替え前の配列へ書込
jの位置に応じてiの位置を変更
sf[tx*2+(j+1)%2][ty]= sfrb[tx ][ty];
sfrb[][]
0 1 2 3
0 1 2 3
sf[][]
0 1 2 3
0 1 2 3
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論779
並び替え後の配列内の赤色要素を並び替え前の配列へ書込
jの位置に応じてiの位置を変更
sf[tx*2+(j+1)%2][ty]= sfrb[tx ][ty];
sfrb[][]
0 1 2 3
0 1 2 3
sf[][]
0 1 2 3
0 1 2 3 +0
+1+0+1
(j+1)%2
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論780
並び替え後の配列内の黒色要素を並び替え前の配列へ書込
jの位置に応じてiの位置を変更
sf[tx*2+(j )%2][ty]= sfrb[tx+blockDim.x][ty];
__syncthreads();
sfrb[][]
0 1 2 3
0 1 2 3
sf[][]
0 1 2 3
0 1 2 3
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論781
並び替え後の配列内の黒色要素を並び替え前の配列へ書込
jの位置に応じてiの位置を変更
sf[tx*2+(j )%2][ty]= sfrb[tx+blockDim.x][ty];
__syncthreads();
sfrb[][]
0 1 2 3
0 1 2 3
sf[][]
0 1 2 3
0 1 2 3 +1
+0+1+0
(j )%2
二つ目の共有メモリsfrb[][]を利用しないと性能はどのように変化するだろうか?
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論782
共有メモリの内容をコアレスアクセスして元の配列に書き込み
f[]i = blockIdx.x*(blockDim.x*2) + threadIdx.x;ij= i+Nx*j;f[ij] = sf[tx ][ty];
i = blockIdx.x*(blockDim.x*2) + threadIdx.x+ blockDim.x;
ij= i+Nx*j;f[ij] = sf[tx+blockDim.x][ty];
sf[][]
0 1 2 3
0 1 2 3
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論783
共有メモリの内容をコアレスアクセスして元の配列に書き込み
f[] sf[][]
0 1 2 3
0 1 2 3
i = blockIdx.x*(blockDim.x*2) + threadIdx.x;ij= i+Nx*j;f[ij] = sf[tx ][ty];
i = blockIdx.x*(blockDim.x*2) + threadIdx.x+ blockDim.x;
ij= i+Nx*j;f[ij] = sf[tx+blockDim.x][ty];
__global__ void computeRed(double *fr,double *fb, double *gr,double *gb, int *converged){
int j = blockIdx.y*blockDim.y + threadIdx.y;int i = blockIdx.x*blockDim.x + threadIdx.x;int ij = i+Nx/2*j;int iside = i+1‐(j%2)*2;int isidej = (iside)+Nx/2*(j );int ijp1 = (i )+Nx/2*(j+1);int ijm1 = (i )+Nx/2*(j‐1);double d_f;
if(1‐(j+1)%2<=i && i<Nx/2‐(j+1)%2 && 0<j && j<Ny‐1){d_f =( (fb[isidej]+fb[ij])/dxdx
+(fb[ijm1 ]+fb[ijp1])/dydy‐(gr[ij])
)*dxdxdydy/dxdy2 ‐fr[ij];fr[ij] += Accel*d_f;
if(abs(d_f) > ERR_TOL) *converged = 0;}
}
配列を分離するRB-SOR法(赤色要素の計算)
2015/06/18先端GPGPUシミュレーション工学特論784
rbsor_split.cu
__global__ void computeBlack(double *fb,double *fr, double *gb,double *gr, int *converged){
int j = blockIdx.y*blockDim.y + threadIdx.y;int i = blockIdx.x*blockDim.x + threadIdx.x;int ij = i+Nx/2*j;int iside = i‐1+(j%2)*2;int isidej = (iside)+Nx/2*(j );int ijp1 = (i )+Nx/2*(j+1);int ijm1 = (i )+Nx/2*(j‐1);double d_f;
if(0+(j+1)%2<=i && i<Nx/2‐(j )%2 && 0<j && j<Ny‐1){d_f =( (fr[isidej]+fr[ij])/dxdx
+(fr[ijm1 ]+fr[ijp1])/dydy‐(gb[ij])
)*dxdxdydy/dxdy2 ‐fb[ij];fb[ij] += Accel*d_f;
if(abs(d_f) > ERR_TOL) *converged = 0;}
}
配列を分離するRB-SOR法(黒色要素の計算)
2015/06/18先端GPGPUシミュレーション工学特論785
rbsor_split.cu
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論786
i方向のスレッド番号と配列添字の対応が単純化
fr[] fb[]
0 1 2 3
0 1 2 3
4 5 6 7
j = blockIdx.y*blockDim.y + threadIdx.y;i = blockIdx.x*blockDim.x + threadIdx.x;
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論787
黒色要素の参照点 jによってi方向のどちらを見るかが変化
jが奇数の時はi‐1,偶数の時はi+1fr[] fb[]
0 1 2 3
0 1 2 3
4 5 6 7
f[]
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論788
黒色要素の参照点 jによってi方向のどちらを見るかが変化
jが奇数の時はi‐1,偶数の時はi+1fr[] fb[]
0 1 2 3
0 1 2 3
4 5 6 7
f[]
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論789
黒色要素の参照点 jによってi方向のどちらを見るかが変化
iside = i+1‐(j%2)*2;fr[] fb[]
0 1 2 3
0 1 2 3
4 5 6 7 +1
+0+1+0+1+0+1+0
j%2 +1‐j%2*2
‐1+1‐1+1‐1+1‐1+1
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論790
赤色要素の計算範囲 jによって変化(jの範囲は変更無し 0<j && j<Ny‐1) 1‐(j+1)%2<=i && i<Nx/2‐(j+1)%2
fr[]
0 1 2 3
0 1 2 3
4 5 6 7
f[]
0‐10
‐10
‐10
‐1
‐(j+1)%2 1‐(j+1)%2
1<=0<=1<=0<=1<=0<=1<=0<=
Nx/2‐(j+1)%2
<4<3<4<3<4<3<4<3
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論791
i方向のスレッド番号と配列添字の対応が単純化
fr[] fb[]
0 1 2 3
0 1 2 3
4 5 6 7
j = blockIdx.y*blockDim.y + threadIdx.y;i = blockIdx.x*blockDim.x + threadIdx.x;
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論792
赤色要素の参照点 jによってi方向のどちらを見るかが変化
jが奇数の時はi+1,偶数の時はi‐1fr[] fb[]
0 1 2 3
4 5 6 7
f[]
0 1 2 3
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論793
赤色要素の参照点 jによってi方向のどちらを見るかが変化
jが奇数の時はi+1,偶数の時はi‐1fr[] fb[]
0 1 2 3
4 5 6 7
f[]
0 1 2 3
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論794
赤色要素の参照点 jによってi方向のどちらを見るかが変化
iside = i‐1+(j%2)*2;fr[] fb[]
0 1 2 3
0 1 2 3
4 5 6 7
‐1+j%2*2
+1‐1+1‐1+1‐1+1‐1
+1+0+1+0+1+0+1+0
j%2
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論795
黒色要素の計算範囲 jによって変化(jの範囲は変更無し 0<j && j<Ny‐1) 0+(j+1)%2<=i && i<Nx/2‐(j )%2
fb[]
0 1 2 3
0 1 2 3
4 5 6 7
f[] 0+(j+1)%2
0<=1<=0<=1<=0<=1<=0<=1<=
Nx/2‐(j )%2
<3<4<3<4<3<4<3<4
0+10
+10
+10
+1
+(j+1)%2
レポート課題4(提出期限は1学期末)
共役勾配法を用いてPoisson方程式を解くプログラムを作成せよ. 境界条件等は授業で紹介した条件を用いよ
右辺(ソース項)
境界条件
必ずcudaSetDevice命令を利用してGPUを選択せよ
カーネルは独自に 適化せよ
配列のサイズも自身で決定せよ
先端GPGPUシミュレーション工学特論796 2015/06/18
yxg sinsin2
0sinsin22 yxf