第10回 Poisson方程式の求解
(線形連立一次方程式)
長岡技術科学大学 電気電子情報工学専攻 出川智啓
今回の内容
2015/06/18先端GPGPUシミュレーション工学特論2
 偏微分方程式の分類
 Laplace方程式とその解法
 Jacobi法,Gauss‐Seidel法,SOR法
 2次元Laplace方程式
 共役勾配法
 SOR法(RB‐SOR法)
偏微分方程式の簡単な分類
2015/06/18先端GPGPUシミュレーション工学特論3
 2個の独立変数f(x,y)に対する2階線形偏微分方程式
 A, B, C, D, E, F, Gは定数もしくはx, yの関数
 最高階(2階)微分の係数による分類
02
22
2
2















GFf
y
f
E
x
f
D
y
f
C
yx
f
B
x
f
A
B2‐4AC 型 物理的挙動
=0 放物型 時間発展
>0 双曲型 時間発展
<0 楕円型 空間平衡
 移流拡散方程式
 −A=(>0), D=c, E=1, B=C=F=G=0
 yをtと形式的に置換
 移流項によってfが形を保ったまま速度cで移流
 拡散項によってfの形状がなだらかに減衰
 計算するためには初期値とt>0における境界値が必要
 初期値・境界値問題
放物型
2015/06/18先端GPGPUシミュレーション工学特論4
2
2
x
f
x
f
c
t
f









双曲型
2015/06/18先端GPGPUシミュレーション工学特論5
 波動方程式
 −A=c2, C=1, B=D=E=F=G=0
 yをtと形式的に置換
 時間的に振動する問題を記述
 fは時刻t,位置xにおける振動の変位
 cは振動の位相速度
 計算するためには初期値とt>0における境界値が必要
 初期値・境界値問題
2
2
2
2
2
x
f
c
t
f





楕円型
2015/06/18先端GPGPUシミュレーション工学特論6
 Laplace方程式
 A=1, C=1, B=D=E=F=G=0
 G≠0の場合はPoisson方程式
 式中に時間微分項が存在しない
 fの平衡状態(時間的変化が0の定常状態)を表す
 計算するためには境界値が重要
 境界値問題
02
2
2
2






y
f
x
f
Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論7
 順問題
 関数fの値・形が既知
 2階微分を計算
 逆問題
 関数fのラプラシアンが0
 関数fの形状を推定
?2
2
2
2






y
f
x
f
f
02
2
2
2






y
f
x
f
?f
Laplace方程式の離散化
 x方向2階偏微分の離散化
 y方向を固定してx方向に偏微分
 y方向2階偏微分の離散化
 x方向を固定してy方向に偏微分
2
,1,,1
22
2
2),(),(2),(
Δx
fff
Δx
yΔxxfyxfyΔxxf
x
f jijiji  





2
1,,1,
22
2
2),(),(2),(
Δy
fff
Δy
ΔyyxfyxfΔyyxf
y
f jijiji  





先端GPGPUシミュレーション工学特論8 2015/06/18
Laplace方程式の離散化
 Laplace方程式
 離散化までは拡散方程式と同じ
 fが未知
 離散化されたf同士の関係(差分式)と境界条件から推定
0
22
2
1,,1,
2
,1,,1
2
2
2
2









 
Δy
fff
Δx
fff
y
f
x
f jijijijijiji
先端GPGPUシミュレーション工学特論9 2015/06/18
1次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論10
 x方向のみを考慮
 L=4を5個の離散点に分割
 各位置iでの差分式(i=1,5では境界条件が必要)
0
2
0
2
0
2
2
543
2
432
2
321






Δx
fff
Δx
fff
Δx
fff
0
2
2
11
2
2




 
Δx
fff
x
f iii
1次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論11
 境界条件(左の境界でf=0, 右の境界でf=1)を課す
 f1~f5を未知数とした連立一次方程式
1
0
2
0
2
0
2
0
5
2
543
2
432
2
321
1








f
Δx
fff
Δx
fff
Δx
fff
f
L
f
1次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論12
 行列を用いた表記
 係数行列Aの逆行列A−1を左からかければfが求められる
 逆行列の計算は計算負荷が高く,多くの誤差が混入
 連立一次方程式の求解法を利用してfを求める




















































1
0
0
0
0
1
121
121
121
1
5
4
3
2
1
f
f
f
f
f
Ax=b
連立一次方程式の解法
2015/06/18先端GPGPUシミュレーション工学特論13
 直接法
 係数行列を単位行列(や上三角,下三角行列)に変形する
ことで未知数を求める方法
 所定の計算回数で解が得られる
 計算量が多く,大規模な問題には適用できない
 反復法
 係数行列を変更せず,未知数に推定値を代入して所定の
計算を行い,推定値が厳密解に十分近づくまで計算を繰り
返す方法
 よい推定値を選べば非常に高速に解が得られる
Jacobi法
2015/06/18先端GPGPUシミュレーション工学特論14
 未知数=...の形に変形し近似値を繰り返し計算
 初期の推定値f(0)を基にf(1), f(2), f(3),..., f(m)を計算
 f(m)の誤差が十分小さくなればf(m)を解とする(反復が収束)
 f(m)を元の方程式に代入して右辺と比較する 等
 
 
  5.020
020
020
5
)0(
3
)1(
4
)0(
4
)0(
2
)1(
3
)0(
31
)1(
2



fff
fff
fff
1
0
2
0
2
0
2
0
5
2
543
2
432
2
321
1








f
Δx
fff
Δx
fff
Δx
fff
f
Jacobi法
2015/06/18先端GPGPUシミュレーション工学特論15
 未知数=...の形に変形し近似値を繰り返し計算
 初期の推定値f(0)を基にf(1), f(2), f(3),..., f(m)を計算
 f(m)の誤差が十分小さくなればf(m)を解とする(反復が収束)
 f(m)を元の方程式に代入して右辺と比較する 等
 
 
  5.020
25.020
020
5
)1(
3
)2(
4
)1(
4
)1(
2
)2(
3
)1(
31
)2(
2



fff
fff
fff
1
0
2
0
2
0
2
0
5
2
543
2
432
2
321
1








f
Δx
fff
Δx
fff
Δx
fff
f
Jacobi法
2015/06/18先端GPGPUシミュレーション工学特論16
 未知数=...の形に変形し近似値を繰り返し計算
 初期の推定値f(0)を基にf(1), f(2), f(3),..., f(m)を計算
 f(m)の誤差が十分小さくなればf(m)を解とする(反復が収束)
 f(m)を元の方程式に代入して右辺と比較する 等
 
 
  625.020
25.020
125.020
5
)2(
3
)3(
4
)2(
4
)2(
2
)3(
3
)2(
31
)3(
2



fff
fff
fff
1
0
2
0
2
0
2
0
5
2
543
2
432
2
321
1








f
Δx
fff
Δx
fff
Δx
fff
f
Jacobi法
2015/06/18先端GPGPUシミュレーション工学特論17
 未知数=...の形に変形し近似値を繰り返し計算
 初期の推定値f(0)を基にf(1), f(2), f(3),..., f(m)を計算
 f(m)の誤差が十分小さくなればf(m)を解とする(反復が収束)
 f(m)を元の方程式に代入して右辺と比較する 等
 
 
  625.020
375.020
125.020
5
)4(
3
)4(
4
)4(
4
)4(
2
)4(
3
)4(
31
)4(
2



fff
fff
fff
1
0
2
0
2
0
2
0
5
2
543
2
432
2
321
1








f
Δx
fff
Δx
fff
Δx
fff
f
Gauss-Seidel法
2015/06/18先端GPGPUシミュレーション工学特論18
 既に計算したf(m+1)の値を同一反復内で利用
 更新した値を順次使用するため収束が速くなる
 
 
  5.020
020
020
5
)1(
3
)1(
4
)0(
4
)1(
2
)1(
3
)0(
31
)1(
2



fff
fff
fff
 
 
  625.020
25.020
020
5
)2(
3
)2(
4
)1(
4
)2(
2
)2(
3
)1(
31
)2(
2



fff
fff
fff
Gauss‐Seidel法Jacobi法
 
 
  5.020
020
020
5
)0(
3
)1(
4
)0(
4
)0(
2
)1(
3
)0(
31
)1(
2



fff
fff
fff
 
 
  5.020
25.020
020
5
)1(
3
)2(
4
)1(
4
)1(
2
)2(
3
)1(
31
)2(
2



fff
fff
fff
Gauss-Seidel法
2015/06/18先端GPGPUシミュレーション工学特論19
 既に計算したf(m+1)の値を同一反復内で利用
 更新した値を順次使用するため収束が速くなる
 
 
  0.68820
0.37520
125.020
5
)3(
3
)3(
4
)2(
4
)3(
2
)3(
3
)2(
31
)3(
2



fff
fff
fff
 
 
  0.71920
0.43820
0.187520
5
)4(
3
)4(
4
)3(
4
)4(
2
)4(
3
)3(
31
)4(
2



fff
fff
fff
Gauss‐Seidel法Jacobi法
 
 
  625.020
25.020
125.020
5
)2(
3
)3(
4
)2(
4
)2(
2
)3(
3
)2(
31
)3(
2



fff
fff
fff
 
 
  625.020
375.020
125.020
5
)3(
3
)4(
4
)3(
4
)3(
2
)4(
3
)3(
31
)4(
2



fff
fff
fff
SOR法(逐次過緩和法, Successive 0ver-Relaxation)
2015/06/18先端GPGPUシミュレーション工学特論20
 近似解の修正量に係数をかけて過剰に修正
 加速係数が0<<2の範囲で収束
 1<<2を取ると収束が加速
 0<<1なら収束が減速
  
  
   696.020
36.020
020
)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



fffff
fffff
fffff



  
  
   6.020
020
020
)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



fffff
fffff
fffff


 2.1
を採用
SOR法(逐次過緩和法, Successive 0ver-Relaxation)
2015/06/18先端GPGPUシミュレーション工学特論21
 近似解の修正量に係数をかけて過剰に修正
 加速係数が0<<2の範囲で収束
 1<<2を取ると収束が加速
 0<<1なら収束が減速
  
  
   749.020
498.020
242.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



fffff
fffff
fffff



  
  
   746.020
475.020
216.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



fffff
fffff
fffff


 2.1
を採用
共役勾配法
2015/06/18先端GPGPUシミュレーション工学特論22
 Jacobi法などとは異なる種類の反復解法
 係数行列が対称である連立一次方程式が対象
 丸め誤差が無ければ所定の回数で収束
 丸め誤差に弱く,所定の回数で収束しないこともある
共役勾配法のアルゴリズム
2015/06/18先端GPGPUシミュレーション工学特論23
 連立一次方程式Ax=bに対する共役勾配法
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の積
( , ) ベクトル同士の
内積
A 係数行列
x 解ベクトル
b 右辺ベクトル
r 残差ベクトル
p 補助ベクトル
||・|| l2−ノルム
2次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論24
 1方向から加熱される金属板の温度分布
 Laplace方程式
 境界条件
1℃
0℃0℃
0℃
B2
B1
B3
B4
4
3
2
1
on0
on0
on1
on0
BT
BT
BT
BT




02
2
2
2






y
T
x
T
x
y
2次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論25
 1方向から加熱される金属板の温度分布
 Laplace方程式
 計算条件
 計算領域 Lx=2, Ly=2
 離散点の数 Nx=3, Ny=3
 格子間隔 x=y=h
02
2
2
2






y
T
x
T
x
y
0
i
1 2
0
1
2
j
2次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論26
 1方向から加熱される金属板の温度分布
 Laplace方程式
 Laplace方程式の差分式
02
2
2
2






y
T
x
T
0
i
1 2
0
1
2
j
T[i][j]
0
22
2
1,,1,
2
,1,,1





 
y
TTT
x
TTT jijijijijiji
0
4
2
1,,1,1,,1


 
h
TTTTT jijijijiji
2次元Laplace方程式
2015/06/18先端GPGPUシミュレーション工学特論27
 1方向から加熱される金属板の温度分布
 Laplace方程式
 Laplace方程式の差分式
0
i
1 2
0
1
2
j
T[]
0 1 2
3 4 5
6 7 8
0
22
2
147
2
345






y
TTT
x
TTT
0
4
2
13475



h
TTTTT
02
2
2
2






y
T
x
T
連立一次方程式
2015/06/18先端GPGPUシミュレーション工学特論28
 Tに関する9元連立一次方程式を解く




























































































1
1
1
0
0
0
0
0
0
1
1
1
1
11411
1
1
1
1
8
7
6
5
4
3
2
1
0
T
T
T
T
T
T
T
T
T
Ax=b
連立一次方程式
2015/06/18先端GPGPUシミュレーション工学特論29
 行列Aを保持する必要は無い
 SOR法はT=… の形を作り,反復計算を行う
 変形した式をプログラムに記述
 周囲が全て固定値の境界条件なので1回の反復で収束
 





























































1
1
1
0
4
0
0
0
0
7531
8
7
6
5
)(
4
3
2
1
0
TTTT
T
T
T
T
T
T
T
T
T
m
連立一次方程式
2015/06/18先端GPGPUシミュレーション工学特論30
 行列Aを保持する必要は無い
 共役勾配法は行列-ベクトル積Axの結果がわかればよい
 行列-ベクトル積を計算して得られた式をプログラムに記述




























































































8
7
6
5
75431
3
2
1
0
8
7
6
5
4
3
2
1
0
4
1
1
1
1
11411
1
1
1
1
T
T
T
T
TTTTT
T
T
T
T
T
T
T
T
T
T
T
T
T
#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シミュレーション工学特論31
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‐1
T[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シミュレーション工学特論32
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‐1
T[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シミュレーション工学特論33
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シミュレーション工学特論34
laplace.c
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論35
 誤差
 真の値からのズレ
 近似値 =真値r+誤差
 誤差
 絶対誤差
 相対誤差
rr  ˆ
rˆ
rr  ˆ
rrr  ˆ
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論36
 ベクトル量における誤差の評価
 lp−ノルム(pは整数)
 l1−ノルム
 l2−ノルム
 l∞−ノルム
p
N
i
p
iip xxl   ˆ
x2
x1
x
xˆ
22111
ˆˆ xxxxl 
2
22
2
112
ˆˆ xxxxl 
 2211
ˆ,ˆmax xxxxl 
11
ˆxx 
22
ˆxx 
x1方向のズレとx2方向のズレの合計
2次元平面における距離
x1方向のズレとx2方向のズレの大きい方
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論37
 l2−ノルムを用いた相対
誤差評価
 m+1回目の反復の値が
真値になれば誤差と一致
 反復中に変化量が十分
小さくなれば収束と判定
x2
 
 




y x
y x
N
j
N
i
m
ji
N
j
N
i
m
ji
m
ji
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シミュレーション工学特論38
 残差
 方程式の解に近似値を用いたことによって生じる不整合
 残差rの定義
 誤差と残差
 誤差の計算には真の解が必要
 残差は方程式の係数と右辺値がわかれば計算できる
xAbr ˆ
反復の収束判定
2015/06/18先端GPGPUシミュレーション工学特論39
 残差の2乗ノルムを用
いた収束判定
 右辺の2乗ノルムを用い
た相対残差を計算 x2
 

 
y x
y x
N
j
N
i
ji
N
j
N
i
m
ji
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シミュレーション工学特論40
 前回授業で用いた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:1
set 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シミュレーション工学特論41
 前回授業で用いた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:1
unset contour 2次元等高線を表示
unset surface 3次元で等値面を表示しない
set pm3d カラー表示しない
以下でファイルを読み込み,3次元表示
splot 'T.txt' with line  
Poisson方程式への拡張
2015/06/18先端GPGPUシミュレーション工学特論42
 Poisson方程式
 離散化
 右辺が0ではなく関数gの値
g
y
f
x
f






2
2
2
2
ji
jijijijijiji
g
Δy
fff
Δx
fff
y
f
x
f
.2
1,,1,
2
,1,,1
2
2
2
2
22









 
SOR法で用いる式の拡張
2015/06/18先端GPGPUシミュレーション工学特論43
 xとyが等しくない場合
 差分式を変形してfi,j=… の形に変形
ji
jijijijijiji
g
Δy
fff
Δx
fff
.2
1,,1,
2
,1,,1 22



 





 








 
2
1,1,
2
,1,1
.,22
11
2
Δy
ff
Δx
ff
gf
ΔyΔx
jijijiji
jiji
 
22
22
2
1,1,
2
,1,1
.,
2
ΔyΔx
ΔyΔx
Δy
ff
Δx
ff
gf jijijiji
jiji














 


 
#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シミュレーション工学特論44
poisson.c
void cg(double *f, double *g){
double err;
int ij,ip1j,im1j,ijm1,ijp1;
double alph,beta,rr,pAp,bb;
double *p  = (double *)malloc
(Nx*Ny*sizeof(double));
double *r  = (double *)malloc
(Nx*Ny*sizeof(double));
double *Ap = (double *)malloc
(Nx*Ny*sizeof(double));
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;
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シミュレーション工学特論45
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シミュレーション工学特論46
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シミュレーション工学特論47 2015/06/18
yxg sinsin2
0sinsin22
 yxf
2
2
g
2−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.0
void 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シミュレーション工学特論48
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シミュレーション工学特論49
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シミュレーション工学特論50
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シミュレーション工学特論51
poisson.c
計算結果
2015/06/18先端GPGPUシミュレーション工学特論52
 前回授業で用いた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:1
set 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シミュレーション工学特論53
 前回授業で用いた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:1
unset contour 2次元等高線を表示
unset surface 3次元で等値面を表示しない
set pm3d カラー表示しない
以下でファイルを読み込み,3次元表示
splot 'f.txt' with line  
GPUへの移植
2015/06/18先端GPGPUシミュレーション工学特論54
 共役勾配法の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シミュレーション工学特論55
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シミュレーション工学特論56
残差を計算するカーネルと内積を計算する
カーネルに分離
・残差の計算には拡散方程式のカーネル
・内積の計算は総和計算のカーネル
を流用
容易ではないが,これまでの知識で並列
化・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シミュレーション工学特論57
ベクトル和と同様に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シミュレーション工学特論58
残差を計算するカーネルと内積を計算する
カーネルに分離
・残差の計算には拡散方程式のカーネル
・内積の計算は総和計算のカーネル
を流用
容易ではないが,これまでの知識で並列
化・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シミュレーション工学特論59
ベクトル和と同様に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); cudaDeviceSynchronize();
alph=0.0;
beta=0.0;
bb = dot_product(g,g); cudaDeviceSynchronize();
computeResidual<<<Block, Thread>>>(f,g,r); cudaDeviceSynchronize();
rr = dot_product(r,r); cudaDeviceSynchronize();
do{
computeAuxVector<<<Block, Thread>>>(p,r,beta);cudaDeviceSynchronize();
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論60
GPUの全スレッドを同期
(不要な箇所もある)
computeMxV<<<Block, Thread>>>(Ap,p); cudaDeviceSynchronize();
pAp = dot_product(p,Ap); cudaDeviceSynchronize();
alph = rr/pAp;
update<<<Block, Thread>>>(f,r); cudaDeviceSynchronize();
rr = dot_product(r,r); cudaDeviceSynchronize();
err = sqrt(rr/bb);
beta = rr/(alph*pAp);
}while(err > ERR_TOL);
}
共役勾配法の並列化・GPU移植
2015/06/18先端GPGPUシミュレーション工学特論61
SOR法の並列化
2015/06/18先端GPGPUシミュレーション工学特論62
 SOR法の処理の手順
 更新手順に依存性が存在
 Jacobi法なら依存性問題は回
避できるが収束が遅すぎる
 並列化の影響で反復毎に更新
順序が変化すると収束が保証さ
れない
 配列順序・計算順序の並び替え
(オーダリング)を導入
f[]
i
j  
22
22
2
)1(
1,
)(
1,
2
)1(
,1
)(
,1
.
)1(
,
2
ΔyΔx
ΔyΔx
Δy
ff
Δx
ff
g
f
m
ji
m
ji
m
ji
m
ji
ji
m
ji
















 









Red-Blackオーダリング
2015/06/18先端GPGPUシミュレーション工学特論63
 更新順序を2段階に分離
 赤色の要素を参照し,黒色の要素全てを更新
 黒色の要素を参照し,赤色の要素全てを更新
f[]
i
j
i
j
i
fr[] fb[]
Red-Blackオーダリング
2015/06/18先端GPGPUシミュレーション工学特論64
 更新順序を2段階に分離
 赤色の要素を参照し,黒色の要素全てを更新
 黒色の要素を参照し,赤色の要素全てを更新
f[]
i
j
fr[]
i
j
fb[]
i
Red-Black SOR法
2015/06/18先端GPGPUシミュレーション工学特論65
 赤色もしくは黒色の要素は常にm+1の値を用いて計算
 SOR法よりも速く収束
 
22
22
2
)(
1,
)(
1,
2
)(
,1
)(
,1
.
)1(
,
2
ΔyΔx
ΔyΔx
Δy
ff
Δx
ff
gf
m
ji
m
ji
m
ji
m
ji
ji
m
ji
















 


 
 
22
22
2
)1(
1,
)1(
1,
2
)1(
,1
)1(
,1
.
)1(
,
2
ΔyΔx
ΔyΔx
Δy
ff
Δx
ff
gf
m
ji
m
ji
m
ji
m
ji
ji
m
ji
















 

















eveniswhen,...5,3,1
oddiswhen,...6,4,2
,1,2,...,2,1
ji
ji
NNj yy






oddiswhen,...5,3,1
eveniswhen,...6,4,2
,1,2,...,2,1
ji
ji
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シミュレーション工学特論66
poisson.c
CPUプログラム(RB-SOR法)
2015/06/18先端GPGPUシミュレーション工学特論67
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シミュレーション工学特論68
 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  7
0 1  2  3 4  5  6  7
iの最小値は1,jが奇数の時+1,偶数の時+0
jを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;
}
}
剰余(j%2)をアンド演算(j&1)に置き換えると実行速度はどうなる?
黒色要素の計算
2015/06/18先端GPGPUシミュレーション工学特論69
 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  7
0 1  2  3 4  5  6  7
iの最小値は1,jが奇数の時+0,偶数の時+1
jを2で割った剰余を利用してiの開始点を決定
i=1+(j+1)%2 (=2‐j%2とも書ける)
GPUへの移植
2015/06/18先端GPGPUシミュレーション工学特論70
 赤色要素を計算するカーネルと黒色要素を計算する
カーネルを分離
 反復のループ制御や収束判定は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シミュレーション工学特論71
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シミュレーション工学特論72
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);//cudaDeviceSynchronize();
cudaMemcpy(conv, converged, sizeof(int),cudaMemcpyDeviceToHost);
}while(*conv == 0 && ite_SOR<1024*1024*1024);//反復回数が230を超えると反復終了
free(conv);
cudaFree(converged);
return ite_SOR;
}
RB-SOR法(ループ制御部分)
2015/06/18先端GPGPUシミュレーション工学特論73
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);//cudaDeviceSynchronize();
}while(*conv == 0 && ite_SOR<1024*1024*1024);//反復回数が230を超えると反復終了
cudaFreeHost(conv);
return ite_SOR;
}
RB-SOR法(ループ制御部分)
2015/06/18先端GPGPUシミュレーション工学特論74
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シミュレーション工学特論75
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シミュレーション工学特論76
rbsor.cu
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論77
 初期化
 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シミュレーション工学特論78
 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シミュレーション工学特論79
 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シミュレーション工学特論80
c[i]
a[i]
b[i]
0 1 0
+ + + +
1
gridDim.x=3
blockDim.x=2
0 1
+ +
blockDim.x=2 blockDim.x=2
blockIdx.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シミュレーション工学特論81
 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シミュレーション工学特論82
c[i]
a[i]
b[i]
0 1 0
+ + + +
1
gridDim.x=3
blockDim.x=2
0 1
+ +
blockDim.x=2 blockDim.x=2
blockIdx.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シミュレーション工学特論83
 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シミュレーション工学特論84
 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シミュレーション工学特論85
 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シミュレーション工学特論86
 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シミュレーション工学特論87
 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;
剰余(j%2)をアンド演算(j&1)に置き換える
と実行速度はどう変わるだろうか
RB-SOR法の性能改善
2015/06/18先端GPGPUシミュレーション工学特論88
 赤色要素,黒色要素の計算はストライドアクセス
 グローバルメモリにコアレスアクセスできない
 読み込み速度が低下
 赤色要素,黒色要素の計算でカーネルを切り替えるため,共
有メモリが利用できない
 メモリアクセスを改善したい
 グローバルメモリにコアレスアクセスしたい
 赤色要素を格納する配列と黒色要素を格納する配列を分離
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論89
 色ごとに格納する配列を区別
 ストライドアクセスを排除
 元の配列を分離,出力時に結合するカーネルを作成
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シミュレーション工学特論90
rbsor_split.cu
do{
ite_SOR++;
*conv = 1;
computeRed <<<BlockSOR, ThreadSOR>>>(fr, fb, gr, gb,converged);
computeBlack<<<BlockSOR, ThreadSOR>>>(fb, fr, gb, gr, converged);
cudaDeviceSynchronize();
}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シミュレーション工学特論91
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シミュレーション工学特論92
rbsor_split.cu
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論93
 共有メモリ上に配列を宣言
 グローバルメモリからコピーするための配列
 並び替え後のデータを格納する配列
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[][]
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論94
 共有メモリ上に配列を宣言
 グローバルメモリからコピーするための配列
 並び替え後のデータを格納する配列
 グローバルメモリから共有メモリにコ
ピーして同期を取る
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シミュレーション工学特論95
 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シミュレーション工学特論96
 共有メモリ上でストライドアクセスして
赤色要素と黒色要素を並び替え
 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シミュレーション工学特論97
 共有メモリ上でストライドアクセスして
赤色要素と黒色要素を並び替え
 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シミュレーション工学特論98
 共有メモリ上でストライドアクセスして
赤色要素と黒色要素を並び替え
 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
二つ目の共有メモリsfrb[][]を利用しない
と性能はどのように変化するだろうか?
配列の分離
2015/06/18先端GPGPUシミュレーション工学特論99
 共有メモリ上でストライドアクセスして
赤色要素と黒色要素を並び替え
 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シミュレーション工学特論100
 共有メモリの赤色,黒色要素を分離し
た配列に書込
 各配列にコアレスアクセスで書込
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シミュレーション工学特論101
 共有メモリの赤色,黒色要素を分離し
た配列に書込
 各配列にコアレスアクセスで書込
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シミュレーション工学特論102
rbsor_split.cu
配列の結合
2015/06/18先端GPGPUシミュレーション工学特論103
 赤色要素をグローバルメモリから読み
込み,共有メモリへ書込
 コアレスアクセスで書込
__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シミュレーション工学特論104
 黒色要素をグローバルメモリから読み
込み,共有メモリへ書込
 コアレスアクセスで書込
__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シミュレーション工学特論105
 並び替え後の配列内の赤色要素を並
び替え前の配列へ書込
 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シミュレーション工学特論106
 並び替え後の配列内の赤色要素を並
び替え前の配列へ書込
 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シミュレーション工学特論107
 並び替え後の配列内の黒色要素を並
び替え前の配列へ書込
 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シミュレーション工学特論108
 並び替え後の配列内の黒色要素を並
び替え前の配列へ書込
 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シミュレーション工学特論109
 共有メモリの内容をコアレスアクセスし
て元の配列に書き込み
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シミュレーション工学特論110
 共有メモリの内容をコアレスアクセスし
て元の配列に書き込み
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シミュレーション工学特論111
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シミュレーション工学特論112
rbsor_split.cu
並列度の指定
2015/06/18先端GPGPUシミュレーション工学特論113
 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シミュレーション工学特論114
 黒色要素の参照点
 jに応じてi方向のどちらを見るかが変化
 jが奇数の時はi‐1,偶数の時はi+1
fr[] fb[]
0  1  2  3
0 1  2  3 4  5  6  7
f[]
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論115
 黒色要素の参照点
 jに応じてi方向のどちらを見るかが変化
 jが奇数の時はi‐1,偶数の時はi+1
fr[] fb[]
0  1  2  3
0 1  2  3 4  5  6  7
f[]
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論116
 黒色要素の参照点
 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シミュレーション工学特論117
 赤色要素の計算範囲
 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
‐1
0
‐1
0
‐1
0
‐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シミュレーション工学特論118
 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シミュレーション工学特論119
 赤色要素の参照点
 jに応じてi方向のどちらを見るかが変化
 jが奇数の時はi+1,偶数の時はi‐1
fr[] fb[]
0 1  2  3 4  5  6  7
f[]
0  1  2  3
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論120
 赤色要素の参照点
 jに応じてi方向のどちらを見るかが変化
 jが奇数の時はi+1,偶数の時はi‐1
fr[] fb[]
0 1  2  3 4  5  6  7
f[]
0  1  2  3
配列アクセス
2015/06/18先端GPGPUシミュレーション工学特論121
 赤色要素の参照点
 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シミュレーション工学特論122
 黒色要素の計算範囲
 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
+1
0
+1
0
+1
0
+1
+(j+1)%2

2015年度先端GPGPUシミュレーション工学特論 第10回 Poisson方程式の求解 (線形連立一次方程式)