第14回 N体問題
長岡技術科学大学 電気電子情報工学専攻 出川智啓
今回の内容
2015/07/15GPGPU実践プログラミング2
 天体の運動方程式
 天体運動のGPU実装
 最適化による性能変化
 #pragma unroll
計算の種類
2015/07/15GPGPU実践プログラミング3
 画像処理,差分法
 空間に固定された観測点を配置
 観測点上で物理量がどのように
変化するかを追跡
 Euler型
 多粒子の運動
 観測点を配置せず,観測点が粒
子と共に移動
 Lagrange型
観測点
(固定)
観測点
(粒子と共に移動)
N体問題
2015/07/15GPGPU実践プログラミング4
 万有引力によって運動する天体群のシミュレーション
 天体を質点と考え,衝突を無視
 天体は自身以外の天体から受ける万有引力のみで運動
 天体iの加速度


bN
ijj ji
ji
ji m
,1
3
r
r
ga
rji : 2天体j, iの距離
g : 重力加速度
mj: 天体の質量
Nb: 天体の総数
時間微分の離散化
 天体iの位置rと速度v
 時間微分の離散化
 テイラー展開を利用
i
i
dt
d
v
r
 i
i
dt
d
a
v

 3
33
2
22
!3!2
)()(
dt
dΔt
dt
dΔt
dt
d
ΔttΔtt
rrr
rr
v
rrrrr








 3
33
2
22
!3!2
1)()(
dt
dΔt
dt
dΔt
ΔtΔt
tΔtt
dt
d
2015/07/15GPGPU実践プログラミング5
時間微分の離散化
2015/07/15GPGPU実践プログラミング6
 離散時刻を上付き添字nで表現
 位置r,速度vの時間発展
 時刻nでの天体位置r,速度vが既知
 加速度anを求めることで時刻n+1の天体位置を予測
n
t rr )(
1
)( 
 n
Δtt rr
Δtn
i
n
i
n
i vrr 1
Δtn
i
n
i
n
i avv 1
プログラムの流れ
 プログラムは2重ループ
 全ペア相互作用(all pair interaction)
 全ての天体が全ての天体のデータを利用
 一度読み込んだデータの再利用が重要
 計算量は天体個数の2乗に比例
for 全ての天体について{
for 全ての天体について{
天体間の距離を計算
if(同じ天体でなければ){
万有引力を計算
加速度aの値を更新
}
}
}    
2015/07/15GPGPU実践プログラミング7
プログラムの流れ
 プログラムは2重ループ
 全ペア相互作用(all pair interaction)
 全ての天体が全ての天体のデータを利用
 一度読み込んだデータの再利用が重要
 計算量は天体個数の2乗に比例
for 全ての天体について{
for 全ての天体について{
天体間の距離を計算
if(同じ天体でなければ){
万有引力を計算
加速度aの値を更新
}
}
}    
2015/07/15GPGPU実践プログラミング8
プログラムの流れ
 プログラムは2重ループ
 全ペア相互作用(all pair interaction)
 全ての天体が全ての天体のデータを利用
 一度読み込んだデータの再利用が重要
 計算量は天体個数の2乗に比例
for 全ての天体について{
for 全ての天体について{
天体間の距離を計算
if(同じ天体でなければ){
万有引力を計算
加速度aの値を更新
}
}
}    
2015/07/15GPGPU実践プログラミング9
プログラムの流れ
 プログラムは2重ループ
 全ペア相互作用(all pair interaction)
 全ての天体が全ての天体のデータを利用
 一度読み込んだデータの再利用が重要
 計算量は天体個数の2乗に比例
for 全ての天体について{
for 全ての天体について{
天体間の距離を計算
if(同じ天体でなければ){
万有引力を計算
加速度aの値を更新
}
}
}    
2015/07/15GPGPU実践プログラミング10
プログラムの流れ
 プログラムは2重ループ
 全ペア相互作用(all pair interaction)
 全ての天体が全ての天体のデータを利用
 一度読み込んだデータの再利用が重要
 計算量は天体個数の2乗に比例
for 全ての天体について{
for 全ての天体について{
天体間の距離を計算
if(同じ天体でなければ){
万有引力を計算
加速度aの値を更新
}
}
}    
2015/07/15GPGPU実践プログラミング11
プログラムの流れ
 万有引力は同一天体には影響しない
 同一天体間の万有引力の計算は不可能(ゼロ除算)
 ifで区別
 if文を排除し,ゼロ除算を回避するため,微小値を付加
  : 軟化因子
 同一天体なら分子が0となるので計算結果に影響しない






 

bN
ijj
ji
ji
j
n
i m
,1
2/3
22
r
r
ga
2015/07/15GPGPU実践プログラミング12
//重力加速度が1になるように規格化
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define N      (4096)
#define dt 0.001
#define Soften (1e‐6)
//プロトタイプ宣言
void initial(…);
void kernel(…);
void integrate(…);
int main(void){
float *x,*y,*z,*m;
float *vx,*vy,*vz;
float *ax,*ay,*az;
x = (float *)malloc(N*sizeof(float));
y = (float *)malloc(N*sizeof(float));
z = (float *)malloc(N*sizeof(float));
m = (float *)malloc(N*sizeof(float));
vx = (float *)malloc(N*sizeof(float));
vy = (float *)malloc(N*sizeof(float));
vz = (float *)malloc(N*sizeof(float));
ax = (float *)malloc(N*sizeof(float));
ay = (float *)malloc(N*sizeof(float));
az = (float *)malloc(N*sizeof(float));
//初期値設定
initial(x,y,z,vx,vy,vz,m);
//for(){ //本来なら必要な回数だけ繰り返す
//加速度の計算
kernel(x,y,z,vx,vy,vz,ax,ay,az,m);
//時間積分
integrate(x,y,z,vx,vy,vz,ax,ay,az);
//}
return 0;
}
CPUプログラム
2015/07/15GPGPU実践プログラミング13
nbody.c
//初期値の設定
void initial(float *x,float *y,float *z,float *vx,float *vy,float *vz,float *m){
int i;
//乱数で配置を決定
//x,y座標,x,y方向速度が‐1~1の範囲に収まるように決定
srand(N); 
for(i=0;i<N;i++){
x[i] = (float)rand()/RAND_MAX*2.0 ‐ 1.0;
y[i] = (float)rand()/RAND_MAX*2.0 ‐ 1.0;
z[i] = 0.0f;
m[i] = 1.0f;
vx[i] = (float)rand()/RAND_MAX*2.0 ‐ 1.0;
vy[i] = (float)rand()/RAND_MAX*2.0 ‐ 1.0;
vz[i] = 0.0f;
}
}
CPUプログラム
2015/07/15GPGPU実践プログラミング14
nbody.c
//時間積分
void integrate(float *x, float *y, float *z,
float *vx, float *vy, float *vz, float *ax, float *ay, float *az){
int i;
//Euler法で位置と速度を積分
//必ず位置の積分を先に実行
for(i=0;i<N;i++){
x[i] =  x[i] + dt*vx[i];
y[i] =  y[i] + dt*vy[i];
z[i] =  z[i] + dt*vz[i];
vx[i] = vx[i] + dt*ax[i];
vy[i] = vy[i] + dt*ay[i];
vz[i] = vz[i] + dt*az[i];
}
}
CPUプログラム
2015/07/15GPGPU実践プログラミング15
nbody.c
//加速度の計算
void kernel(float *x, float *y, float *z,
float *vx, float *vy, float *vz,
float *ax, float *ay, float *az,
float *m){
int i,j;
float rx,ry,rz;
float dist2, dist6, invDist3,s;
for(i=0;i<N;i++){
ax[i] = 0.0f;
ay[i] = 0.0f;
az[i] = 0.0f;
}
for(i=0;i<N;i++){
for(j=0;j<N;j++){
//if(i==j)continue;
rx=x[j]‐x[i]; 
ry=y[j]‐y[i];
rz=z[j]‐z[i];
//2天体間の距離を計算
dist2 = rx*rx + ry*ry + rz*rz
+ Soften;//軟化パラメータ
// m/r^3 の計算
dist6 = dist2*dist2*dist2;
invDist3 = 1.0/sqrt(dist6);
s = m[j]*invDist3;
//天体jによる加速度を加算
ax[i] = ax[i] + rx*s;
ay[i] = ay[i] + ry*s;
az[i] = az[i] + rz*s;
}
}
}
CPUプログラム
2015/07/15GPGPU実践プログラミング16
nbody.c
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define N      (64*64)
#define dt 0.001
#define Soften (1e‐6)
#include "kernel0.cu"//カーネルを切替
int main(void){
//GPUのメモリ上に確保
float *x,*y,*z,*m;
float *vx,*vy,*vz;
float *ax,*ay,*az;
//CPUのメモリ上に確保 初期設定用
float *host_x,*host_y,*host_z,*host_m;
float *host_vx,*host_vy,*host_vz;
cudaMalloc((void **)&x, (N*sizeof(float)));
cudaMalloc((void **)&y, (N*sizeof(float)));
cudaMalloc((void **)&z, (N*sizeof(float)));
cudaMalloc((void **)&m, (N*sizeof(float)));
cudaMalloc((void **)&vx, (N*sizeof(float)));
cudaMalloc((void **)&vy, (N*sizeof(float)));
cudaMalloc((void **)&vz, (N*sizeof(float)));
cudaMalloc((void **)&ax, (N*sizeof(float)));
cudaMalloc((void **)&ay, (N*sizeof(float)));
cudaMalloc((void **)&az, (N*sizeof(float)));
host_x = (float *)malloc(N*sizeof(float));
//以下,host_y等も確保
//初期設定はCPUと同じ
initial(host_x,host_y,host_z,
host_vx,host_vy,host_vz,host_m);
cudaMemcpy(x, host_x, N*sizeof(float), 
cudaMemcpyHostToDevice);
//以下,host_y等もGPUへコピー
GPUプログラム(1スレッド版)
2015/07/15GPGPU実践プログラミング17
nbody.cu
//for(){ //本来は必要な回数だけ繰り返す
kernel<<<NB,NT>>>
(x,y,z,vx,vy,vz,ax,ay,az,m);
integrate<<<NB,NT>>>
(x,y,z,vx,vy,vz,ax,ay,az);
//必要な結果をCPUへコピー
//}
free(host_x);
free(host_y);
free(host_z);
free(host_m);
free(host_vx);
free(host_vy);
free(host_vz);
cudaFree(x);
cudaFree(y);
cudaFree(z);
cudaFree(m);
cudaFree(vx);
cudaFree(vy);
cudaFree(vz);
cudaFree(ax);
cudaFree(ay);
cudaFree(az);
return 0;
}
GPUプログラム(1スレッド版)
2015/07/15GPGPU実践プログラミング18
nbody.cu
#define NT 1
#define NB 1
//加速度の計算
__global__ void kernel(float *x,float *y,
float *z,float *vx, float *vy,
float *vz,float *ax, float *ay,
float *az,float *m){
int i,j;
float rx,ry,rz;
float dist2, dist6, invDist3,s;
for(i=0;i<N;i++){
ax[i] = 0.0f;
ay[i] = 0.0f;
az[i] = 0.0f;
}
for(i=0;i<N;i++){
for(j=0;j<N;j++){
if(i==j)continue;
rx=x[j]‐x[i]; 
ry=y[j]‐y[i];
rz=z[j]‐z[i];
//2天体間の距離を計算
dist2 = rx*rx + ry*ry + rz*rz;
// m/r^3 の計算
dist6 = dist2*dist2*dist2;
invDist3 = 1.0/sqrt(dist6);
s = m[j]*invDist3;
//天体jによる加速度を加算
ax[i] = ax[i] + rx*s;
ay[i] = ay[i] + ry*s;
az[i] = az[i] + rz*s;
}
}
}
GPUプログラム(1スレッド版)
2015/07/15GPGPU実践プログラミング19
kernel0.cu
//時間積分
__global__ void integrate(float *x, float *y, float *z,
float *vx, float *vy, float *vz, float *ax, float *ay, float *az){
int i;
for(i=0;i<N;i++){
x[i] =  x[i] + dt*vx[i];
y[i] =  y[i] + dt*vy[i];
z[i] =  z[i] + dt*vz[i];
vx[i] = vx[i] + dt*ax[i];
vy[i] = vy[i] + dt*ay[i];
vz[i] = vz[i] + dt*az[i];
}
}
GPUプログラム(1スレッド版)
2015/07/15GPGPU実践プログラミング20
kernel0.cu
実行時間
2015/07/15GPGPU実践プログラミング
 天体の個数 N = 4096
21
カーネル 実行時間 [ms]
1スレッド実行 33×103
#define NT 1
#define NB 1
//加速度の計算
__global__ void kernel(float *x,float *y,
float *z,float *vx, float *vy,
float *vz,float *ax, float *ay,
float *az,float *m){
int i,j;
float rx,ry,rz;
float dist2, dist6, invDist3,s;
for(i=0;i<N;i++){
ax[i] = 0.0f;
ay[i] = 0.0f;
az[i] = 0.0f;
}
for(i=0;i<N;i++){
for(j=0;j<N;j++){
rx=x[j]‐x[i]; 
ry=y[j]‐y[i];
rz=z[j]‐z[i];
//2天体間の距離を計算
dist2 = rx*rx + ry*ry + rz*rz
+Soften;
// m/r^3 の計算
dist6 = dist2*dist2*dist2;
invDist3 = 1.0/sqrt(dist6);
s = m[j]*invDist3;
//天体jによる加速度を加算
ax[i] = ax[i] + rx*s;
ay[i] = ay[i] + ry*s;
az[i] = az[i] + rz*s;
}
}
}
GPUプログラム(1スレッド版,if消去)
2015/07/15GPGPU実践プログラミング22
kernel1.cu
実行時間
2015/07/15GPGPU実践プログラミング
 天体の個数 N = 4096
23
カーネル 実行時間 [ms]
1スレッド実行 33×103
1スレッド実行(ifの消去) 29×103
1スレッドが天体一つの加速度を計算
2015/07/15GPGPU実践プログラミング24
 天体のデータを1次元配列で保持
 加速度を求めたい天体iのforループ
 相互作用の計算のために参照される天体jのループ
x[i]
x[j]
加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
・・・
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング25
 ベクトル和と本質的には同じ
・・・
・・・
・・・c[i]
a[i]
b[i]
+
for(i=0;i<N;i++){
c[i]=a[i]+b[i];
}
+ + + + +
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング26
 1スレッドが一つの配列添字を計算
・・・
・・・
・・・c[i]
a[i]
b[i]
i=blockIdx.x*blockDim.x+threadIdx.x;
c[i]=a[i]+b[i];
+ + + + + +
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング27
 加速度の計算
・・・x[j]
for(i=0;i<N;i++){
for(j=0;j<N;j++){
加速度を積算
}
}
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
jのループ
i=0
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング28
 加速度の計算
・・・x[j]
for(i=0;i<N;i++){
for(j=0;j<N;j++){
加速度を積算
}
}
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
jのループ
i=1
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング29
 加速度の計算
・・・x[j]
for(i=0;i<N;i++){
for(j=0;j<N;j++){
加速度を積算
}
}
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
jのループ
i=2
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング30
 加速度の計算
・・・x[j]
for(i=0;i<N;i++){
for(j=0;j<N;j++){
加速度を積算
}
}
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
jのループ
i=3
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング31
 加速度の計算
・・・x[j]
for(i=0;i<N;i++){
for(j=0;j<N;j++){
加速度を積算
}
}
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
jのループ
i=4
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング32
 加速度の計算
・・・x[j]
for(i=0;i<N;i++){
for(j=0;j<N;j++){
加速度を積算
}
}
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
jのループ
i=5
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング33
 ベクトル和と同様に1スレッドが一つの天体を計算
i=blockIdx.x*blockDim.x+threadIdx.x;
for(j=0;j<N;j++){
加速度を積算
}
・・・x[j]
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
複数のスレッド(異なるiをもつ)が
一斉にx[j(=0)]の値を読込
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング34
 ベクトル和と同様に1スレッドが一つの天体を計算
i=blockIdx.x*blockDim.x+threadIdx.x;
for(j=0;j<N;j++){
加速度を積算
}
・・・x[j]
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
複数のスレッド(異なるiをもつ)が
一斉にx[j(=1)]の値を読込
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング35
 ベクトル和と同様に1スレッドが一つの天体を計算
i=blockIdx.x*blockDim.x+threadIdx.x;
for(j=0;j<N;j++){
加速度を積算
}
・・・x[j]
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
複数のスレッド(異なるiをもつ)が
一斉にx[j(=2)]の値を読込
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング36
 ベクトル和と同様に1スレッドが一つの天体を計算
i=blockIdx.x*blockDim.x+threadIdx.x;
for(j=0;j<N;j++){
加速度を積算
}
・・・x[j]
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
複数のスレッド(異なるiをもつ)が
一斉にx[j(=3)]の値を読込
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング37
 ベクトル和と同様に1スレッドが一つの天体を計算
i=blockIdx.x*blockDim.x+threadIdx.x;
for(j=0;j<N;j++){
加速度を積算
}
・・・x[j]
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
複数のスレッド(異なるiをもつ)が
一斉にx[j(=4)]の値を読込
GPUによる並列処理の方針
2015/07/15GPGPU実践プログラミング38
 ベクトル和と同様に1スレッドが一つの天体を計算
i=blockIdx.x*blockDim.x+threadIdx.x;
for(j=0;j<N;j++){
加速度を積算
}
・・・x[j]
・・・x[i]加速度を求めたい
天体
相互作用の計算の
ために参照される
天体
複数のスレッド(異なるiをもつ)が
一斉にx[j(=5)]の値を読込
#define NT 256
#define NB (N/NT)
//加速度の計算
__global__ void kernel(float *x,float *y,
float *z,float *vx, float *vy,
float *vz,float *ax, float *ay,
float *az,float *m){
int i,j;
float rx,ry,rz;
float dist2, dist6, invDist3,s;
i = blockDim.x*blockIdx.x+threadIdx.x;
for(j=0;j<N;j++){
rx=x[j]‐x[i]; 
ry=y[j]‐y[i];
rz=z[j]‐z[i];
//2天体間の距離を計算
dist2 = rx*rx + ry*ry + rz*rz
+ Soften;//軟化パラメータ
// m/r^3 の計算
dist6 = dist2*dist2*dist2;
invDist3 = 1.0/sqrt(dist6);
s = m[j]*invDist3;
//天体jによる加速度を加算
ax[i] = ax[i] + rx*s;
ay[i] = ay[i] + ry*s;
az[i] = az[i] + rz*s;
}
}
GPUプログラム(1スレッドが天体一つを計算)
2015/07/15GPGPU実践プログラミング39
kernel2.cu
//時間積分
__global__ void integrate(float *x, float *y, float *z,
float *vx, float *vy, float *vz, float *ax, float *ay, float *az){
int i=blockIdx.x*blockDim.x + threadIdx.x;
x[i] =  x[i] + dt*vx[i];
y[i] =  y[i] + dt*vy[i];
z[i] =  z[i] + dt*vz[i];
vx[i] = vx[i] + dt*ax[i];
vy[i] = vy[i] + dt*ay[i];
vz[i] = vz[i] + dt*az[i];
}
GPUプログラム(1スレッドが天体一つを計算)
2015/07/15GPGPU実践プログラミング40
kernel2.cu
実行時間
2015/07/15GPGPU実践プログラミング
 天体の個数 N = 4096
 スレッド数 NT = 256
41
カーネル 実行時間 [ms]
1スレッド実行 33×103
1スレッド実行(ifの消去) 29×103
1スレッド1天体 7.63
#define NT 256
#define NB (N/NT)
//加速度の計算
__global__ void kernel(float *x,float *y,
float *z,float *vx, float *vy,
float *vz,float *ax, float *ay,
float *az,float *m){
int i,j;
float rx,ry,rz;
float dist2, dist6, invDist3,s;
//加速度のデータをレジスタに置く
float r_ax, r_ay, r_az;
i = blockDim.x*blockIdx.x+threadIdx.x;
r_ax = r_ay = r_az = 0.0f;
for(j=0;j<N;j++){
rx=x[j]‐x[i]; 
ry=y[j]‐y[i];
rz=z[j]‐z[i];
//2天体間の距離を計算
dist2 = rx*rx + ry*ry + rz*rz
+ Soften;//軟化パラメータ
// m/r^3 の計算
dist6 = dist2*dist2*dist2;
invDist3 = 1.0/sqrt(dist6);
s = m[j]*invDist3;
//天体jによる加速度を加算
r_ax = r_ax + rx*s;
r_ay = r_ay + ry*s;
r_az = r_az + rz*s;
}
ax[i] = r_ax;
ay[i] = r_ay;
az[i] = r_az;
}
GPUプログラム(レジスタ利用)
2015/07/15GPGPU実践プログラミング42
kernel3.cu
実行時間
2015/07/15GPGPU実践プログラミング
 天体の個数 N = 4096
 スレッド数 NT = 256
43
カーネル 実行時間 [ms]
1スレッド実行 33×103
1スレッド実行(ifの消去) 29×103
1スレッド1天体 7.63
レジスタ利用 4.49
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
・・・
共有メモリ
blockIdx.x=0 blockIdx.x=1
2015/07/15GPGPU実践プログラミング44
i=   0    1   2    3   4    5   6    7
threadIdx.x=   0    1   2    3   0    1   2    3
x[0] x[1] x[2]x[3]   x[0] x[1] x[2]x[3]
j=   0    1   2    3   4    5   6    7
j=0+threadIdx.x
j=0+threadIdx.x
・・・
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
blockIdx.x=0 blockIdx.x=1
i=0~3の天体と
j=0~3の天体で
加速度を計算
i=4~7の天体と
j=0~3の天体で
加速度を計算
2015/07/15GPGPU実践プログラミング45
x[0] x[1] x[2]x[3]   x[0] x[1] x[2]x[3]
j=   0    1   2    3   4    5   6    7
i=   0    1   2    3   4    5   6    7
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
x[j]を保持する
共有メモリ
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
blockIdx.x=0 blockIdx.x=1
i=0~3の天体と
j=0~3の天体で
加速度を計算
i=4~7の天体と
j=0~3の天体で
加速度を計算
2015/07/15GPGPU実践プログラミング46
x[0] x[1] x[2]x[3]   x[0] x[1] x[2]x[3]
j=   0    1   2    3   4    5   6    7
i=   0    1   2    3   4    5   6    7
x[j]を保持する
共有メモリ
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
blockIdx.x=0 blockIdx.x=1
i=0~3の天体と
j=0~3の天体で
加速度を計算
i=4~7の天体と
j=0~3の天体で
加速度を計算
2015/07/15GPGPU実践プログラミング47
x[0] x[1] x[2]x[3]   x[0] x[1] x[2]x[3]
j=   0    1   2    3   4    5   6    7
i=   0    1   2    3   4    5   6    7
x[j]を保持する
共有メモリ
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
blockIdx.x=0 blockIdx.x=1
i=0~3の天体と
j=0~3の天体で
加速度を計算
i=4~7の天体と
j=0~3の天体で
加速度を計算
2015/07/15GPGPU実践プログラミング48
x[0] x[1] x[2]x[3]   x[0] x[1] x[2]x[3]
j=   0    1   2    3   4    5   6    7
i=   0    1   2    3   4    5   6    7
x[j]を保持する
共有メモリ
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
x[j]を保持する
共有メモリ
blockIdx.x=0 blockIdx.x=1
2015/07/15GPGPU実践プログラミング49
i=   0    1   2    3   4    5   6    7
x[4] x[5] x[6]x[7]   x[4] x[5] x[6]x[7]
j=   0    1   2    3   4    5   6    7
j=4(=NT)+threadIdx.x
j=4(=NT)+threadIdx.x
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
blockIdx.x=0 blockIdx.x=1
i=0~3の天体と
j=4~7の天体で
加速度を計算
i=4~7の天体と
j=4~7の天体で
加速度を計算
2015/07/15GPGPU実践プログラミング50
j=   0    1   2    3   4    5   6    7
i=   0    1   2    3   4    5   6    7
x[4] x[5] x[6]x[7]   x[4] x[5] x[6]x[7] x[j]を保持する
共有メモリ
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
blockIdx.x=0 blockIdx.x=1
i=0~3の天体と
j=4~7の天体で
加速度を計算
i=4~7の天体と
j=4~7の天体で
加速度を計算
2015/07/15GPGPU実践プログラミング51
j=   0    1   2    3   4    5   6    7
i=   0    1   2    3   4    5   6    7
x[4] x[5] x[6]x[7]   x[4] x[5] x[6]x[7] x[j]を保持する
共有メモリ
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
blockIdx.x=0 blockIdx.x=1
i=0~3の天体と
j=4~7の天体で
加速度を計算
i=4~7の天体と
j=4~7の天体で
加速度を計算
2015/07/15GPGPU実践プログラミング52
j=   0    1   2    3   4    5   6    7
i=   0    1   2    3   4    5   6    7
x[4] x[5] x[6]x[7]   x[4] x[5] x[6]x[7] x[j]を保持する
共有メモリ
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
GPUプログラム(共有メモリによる再利用)
x[i]
x[j]
blockIdx.x=0 blockIdx.x=1
i=0~3の天体と
j=4~7の天体で
加速度を計算
i=4~7の天体と
j=4~7の天体で
加速度を計算
2015/07/15GPGPU実践プログラミング53
j=   0    1   2    3   4    5   6    7
i=   0    1   2    3   4    5   6    7
x[4] x[5] x[6]x[7]   x[4] x[5] x[6]x[7] x[j]を保持する
共有メモリ
・・・
threadIdx.x=   0    1   2    3   0    1   2    3
・・・
#define NT 256
#define NB (N/NT)
//加速度の計算
__global__ void kernel(…){
int i,j;
float rx,ry,rz;
float dist2, dist6, invDist3,s;
//加速度と座標のデータをレジスタに置く
float r_ax,r_ay,r_az,r_x,r_y,r_z;
__shared__ float 
s_x[NT],s_y[NT],s_z[NT],s_m[NT];
i = blockDim.x*blockIdx.x+threadIdx.x;
r_ax = r_ay = r_az = 0.0f;
r_x = x[i];
r_y = y[i];
r_z = z[i];
for(j=0;j<N;j+=NT){
s_x[threadIdx.x] = x[j+threadIdx.x];
s_y[threadIdx.x] = y[j+threadIdx.x];
s_z[threadIdx.x] = z[j+threadIdx.x];
s_m[threadIdx.x] = m[j+threadIdx.x];
__syncthreads();
for(js = 0;js<NT;js++){
rx=s_x[js]‐r_x;
ry=s_y[js]‐r_y;
rz=s_z[js]‐r_z;
//2天体間の距離を計算
dist2 = rx*rx + ry*ry + rz*rz
+ Soften;//軟化パラメータ
// m/r^3 の計算
dist6 = dist2*dist2*dist2;
invDist3 = 1.0/sqrt(dist6);
s = s_m[js]*invDist3;
//天体jによる加速度を加算
r_ax = r_ax + rx*s;
r_ay = r_ay + ry*s;
r_az = r_az + rz*s;
}
}
ax[i] = r_ax;
ay[i] = r_ay;
az[i] = r_az;
}
GPUプログラム(共有メモリによる再利用)
2015/07/15GPGPU実践プログラミング54
kernel4.cu
実行時間
2015/07/15GPGPU実践プログラミング
 天体の個数 N = 4096
 スレッド数 NT = 256
カーネル 実行時間 [ms]
1スレッド実行 33×103
1スレッド実行(ifの消去) 29×103
1スレッド1天体 7.63
レジスタ利用 4.49
共有メモリ利用 4.27
55
#define NT 256
#define NB (N/NT)
//加速度の計算
__global__ void kernel(…){
int i,j;
float rx,ry,rz;
float dist2, dist6, invDist3,s;
//加速度と座標のデータをレジスタに置く
float r_ax,r_ay,r_az,r_x,r_y,r_z;
__shared__ float 
s_x[NT],s_y[NT],s_z[NT],s_m[NT];
i = blockDim.x*blockIdx.x+threadIdx.x;
r_ax = r_ay = r_az = 0.0f;
r_x = x[i];
r_y = y[i];
r_z = z[i];
for(j=0;j<N;j+=NT){
s_x[threadIdx.x] = x[j+threadIdx.x];
s_y[threadIdx.x] = y[j+threadIdx.x];
s_z[threadIdx.x] = z[j+threadIdx.x];
s_m[threadIdx.x] = m[j+threadIdx.x];
__syncthreads();
#pragma unroll
for(js = 0;js<NT;js++){
rx=s_x[js]‐r_x;
ry=s_y[js]‐r_y;
rz=s_z[js]‐r_z;
//2天体間の距離を計算
dist2 = rx*rx + ry*ry + rz*rz
+ Soften;//軟化パラメータ
// m/r^3 の計算
dist6 = dist2*dist2*dist2;
invDist3 = 1.0/sqrt(dist6);
s = s_m[js]*invDist3;
//天体jによる加速度を加算
r_ax = r_ax + rx*s;
r_ay = r_ay + ry*s;
r_az = r_az + rz*s;
}
}
ax[i] = r_ax;
ay[i] = r_ay;
az[i] = r_az;
}
GPUプログラム(ループアンロール)
2015/07/15GPGPU実践プログラミング56
kernel5.cu
実行時間
2015/07/15GPGPU実践プログラミング
 天体の個数 N = 4096
 スレッド数 NT = 256
カーネル 実行時間 [ms]
1スレッド実行 33×103
1スレッド実行(ifの消去) 29×103
1スレッド1天体 7.63
レジスタ利用 4.49
共有メモリ利用 4.27
共有メモリ利用+ループアンロール 3.50
57
実行時間
2015/07/15GPGPU実践プログラミング
 天体の個数 N = 65536
 スレッド数 NT = 256
 共有メモリを単純に利用しただけでは遅くなる
 共有メモリを利用することは高速化に有効
 「共有メモリを利用するために追加される処理」が遅くなる要因
58
カーネル 実行時間 [ms]
1スレッド1天体 916
レジスタ利用 638
共有メモリ利用 646
共有メモリ利用+ループアンロール 499

2015年度GPGPU実践プログラミング 第14回 N体問題