Upload
-
View
251
Download
11
Embed Size (px)
Citation preview
GPGPU講習会GPU最適化ライブラリの利用(その1)
長岡技術科学大学電気電子情報工学専攻 出川智啓
本講習会の目標
GPGPU先端シミュレーションシステムの使用方法の習得
GPUの活用方法の修得
CUDAプログラミング技法の修得
並列計算手法の修得
2015/10/14GPGPU講習会2
本日の内容
GPU最適化ライブラリの利用(その1)
GPU最適化ライブラリの紹介
共役勾配法(およびKrylov部分空間法)
cuBLASの紹介
cuBLASによる共役勾配法の実装
連立一次方程式を解くプログラムの作成
ライブラリを利用
関数(およびCUDA API)の呼出のみで作成
3回に分けて徐々に効率化
2015/10/14GPGPU講習会3
GPU最適化ライブラリ
ライブラリ
2015/10/14GPGPU講習会5
特定の処理を行う複数のプログラムを再利用可能な形でまとめた集合体
動画像処理やファイル圧縮,数値計算などが有名
自作のプログラムよりも性能が高いため,関数を置き換えるだけで処理速度の向上に貢献
数値計算ライブラリ
2015/10/14GPGPU講習会6
FFT(Fast Fourier Transform) FFTW
線形代数演算(ベクトル処理,行列処理)
BLAS(Basic Linear Algebra Subprogram)
BLASを利用した線形代数演算ライブラリ LAPACK LINPACK ScaLAPACK
BLASやLAPACKのメーカー別実装 MKL Intel Math Kernel Library ACML AMD Core Math Library IMSL International Mathematics and Statistics Library
CUDA付属のライブラリ
2015/10/14GPGPU講習会7
cuBLAS 密行列向け線形代数演算
cuSPARSE 疎行列向け線形代数演算
cuFFT フーリエ変換
cuRAND 乱数生成
Thrust ソート,縮約,スキャン等
NPP 画像処理,信号処理
など
NVIDIAホームページに一覧がある
https://developer.nvidia.com/gpu‐accelerated‐libraries
その他GPU向けライブラリ
2015/10/14GPGPU講習会8
cuDNN https://developer.nvidia.com/cudnn
Deep Neural Network用のライブラリ
機械学習用のフレームワークをサポート
Caffe Theano Torch
cuDNNを使ったDIGITSというシステムを利用してNeural Networkのトレーニングを行うことが可能
その他GPU向けライブラリ
2015/10/14GPGPU講習会9
MAGMA http://icl.cs.utk.edu/magma/
NVIDIA GPU向けの線形代数ライブラリ
CPUも同時に使うハイブリッド型のライブラリであるため,GPU単体より高速
BLAS, LAPACKに準ずるような形で関数形が定められている
cuBLASに取り込まれている関数もある
ソースコードが配布されており,無料で入手できる
その他GPU向けライブラリ
2015/10/14GPGPU講習会10
cuBLAS‐XT https://developer.nvidia.com/cublasxt
cuBLASライブラリのマルチGPU向け実装
CUDA 6.0, 6.5から利用可能
共役勾配法
数値シミュレーションと連立一次方程式
2015/10/14GPGPU講習会12
数値シミュレーション
自然現象を記述する偏微分方程式の解を求める
微積分を計算機で形に変換(離散化)
離散化方法
有限差分法
空間に飛び飛びの点(離散点)を配置し,その点上で物理量を定義
微分を差分に置き換え,離散点での物理量を使って微分を計算
有限要素法
空間を要素と呼ばれる微小な空間に分割
要素内での物理量変化を基底関数で定義し,空間全体の物理量変化を基底関数の結合として表現
数値シミュレーションと連立一次方程式
2015/10/14GPGPU講習会13
空間の平衡状態の計算
水・空気中の圧力分布
物体中の温度分布
金属材料が受ける応力分布
逆問題
物理量が従う式だけが分かっていて,物理量の値が不明
Laplace/Poisson方程式
?2
2
2
2
yf
xf
f 02
2
2
2
yf
xf
?f
逆問題順問題
数値シミュレーションと連立一次方程式
2015/10/14GPGPU講習会14
空間の平衡状態の計算
水・空気中の圧力分布
物体中の温度分布
金属材料が受ける応力分布
逆問題
物理量が従う式だけが分かっていて,物理量の値が不明
ある点での値の変化が他の点にも影響
連立一次方程式を作成して全点の値を同時に求める
方程式を解く時間が数値シミュレーションの大半を占める
シミュレーションの規模が大きくなると連立一次方程式の規模も大きくなる
連立一次方程式の解法
2015/10/14GPGPU講習会15
直接法
係数行列を単位行列(や上三角,下三角行列)に変形することで未知数を求める方法
所定の計算回数で解が得られる
計算量が多く,大規模な問題には適用が難しい
反復法
係数行列を変更せず,未知数に推定値を代入して所定の計算を行い,推定値が解に十分近づくまで計算を繰り返す方法
よい推定値を選べば非常に高速に解が得られる
共役勾配法
2015/10/14GPGPU講習会16
連立一次方程式を解くためのアルゴリズム
係数行列が対称・正定値である連立一次方程式が対象
Hestenes and Stiefel(1952)によって提案
反復解法の性質を持ちながら,直接解法のように有限回の計算で解が得られる
「世紀の大解法」ともてはやされた
丸め誤差に弱く,有限回の計算で終わらないこともある
Hestenes, Magnus R., Stiefel, Eduard (December, 1952). "Methods of Conjugate Gradients for Solving Linear Systems". Journal of Research of the National Bureau of Standards 49 (6).
共役勾配法のアルゴリズム
2015/10/14GPGPU講習会17
連立一次方程式Ax=bに対する共役勾配法
Ap 係数行列Aとベクトルpの積
( , ) ベクトル同士の内積
Compute r(0)=b−Ax(0). Set p(0)=0,c2(0)=0.
For k=1,…, until ||r||/||b|| < , Do
p(k) = r(k)+c2(k−1)p(k−1)
c1(k) = (r(k), r(k))/(p(k), Ap(k))
x(k+1) = x(k)+c1(k)p(k)
r(k+1) = r(k)−c1(k)Ap(k)
c2(k) = (r(k+1), r(k+1))/{c1
(k)(p(k), Ap(k))}
EndDo
A 係数行列x 解ベクトルb 右辺ベクトルr 残差ベクトルp 補助ベクトル||・|| l2−ノルム
共役勾配法のバリエーション
2015/10/14GPGPU講習会18
自乗共役勾配法(CGS法)
非対称行列に対応
Compute r(0)=b−Ax(0). Set p(0)=0,c2(0)=0, r*=r(0).
For k=1,…, until ||r||/||b|| < , Dop(k) = r(k)+c2
(k−1)z(k−1)
u(k) = p(k)+c2(k−1)(z(k−1)+c2
(k−1)u(k−1))c1
(k) = (r*, r(k))/(r*, Au(k))z(k) = p(k)−c1
(k)Au(k)
x(k+1) = x(k)+c1(k)(p(k)+z(k))
r(k+1) = r(k)−c1(k)A(p(k)+z(k))
c2(k) = (r*, r(k+1))/{c1
(k)(r*, Au(k))}EndDo
r* 疑似残差u 補助ベクトルz 補助ベクトル
共役勾配法のバリエーション
2015/10/14GPGPU講習会19
安定化双共役勾配法(Bi‐CGSTAB法)
非対称行列に対応
Compute r(0)=b−Ax(0). Set p(0)=0,c2(0)=0, r*=r(0).
For k=1,…, until ||r||/||b|| < , Dop(k) = r(k)+c2
(k−1)(p(k−1)−c3(k−1)Ap(k−1))
c1(k) = (r*, r(k))/(r*, Ap(k))t(k) = r(k)−c1
(k)Ap(k)
c3(k) = (At(k), t(k))/(At(k), At(k))x(k+1) = x(k)+c1
(k)p(k)+c3(k)t(k)
r(k+1) = r(k)−c3(k)At(k)
c2(k) = (r*, r(k+1))/{c3
(k)(r*, Ap(k))}EndDo
r* 疑似残差t 補助ベクトル
連立一次方程式
2015/10/14GPGPU講習会20
3重対角行列
2次元Poisson方程式から導かれる係数行列を簡略化
解(x)が0, 1, 2・・・N−1となるようbを設定
N
N
N
N
bb
bb
xx
xx
1
2
1
1
2
1
41141
14114
0
0
CPUプログラム(制御部分)
2015/10/14GPGPU講習会21
#include<stdlib.h>#include<stdio.h>#include<math.h>int main(void){
int N = 1 << 10; //未知数の数210const double err_tol = 1e‐9; //許容誤差const int max_ite = 1<<20;//反復回数の上限
double *x; //近似解ベクトルdouble *b; //右辺ベクトルdouble *A; //係数行列double *sol; //厳密解double *r, rr; //残差ベクトル, 残差の内積double *p, *Ax; //補助ベクトル,行列ベクトル積double c1, c2, dot; //計算に使う係数int i, k;//メモリの確保A = (double *)malloc(sizeof(double)*N*N);x = (double *)malloc(sizeof(double)*N);b = (double *)malloc(sizeof(double)*N);sol= (double *)malloc(sizeof(double)*N);r = (double *)malloc(sizeof(double)*N);p = (double *)malloc(sizeof(double)*N);
Ax = (double *)malloc(sizeof(double)*N);
for (i = 0; i < N; i++){sol[i] = (double)i; //厳密解を設定
x[i] = 0.0; //近似解を0で初期化}//係数行列Aの生成setTridiagonalMatrix(A, N);//右辺ベクトルbの生成setRightHandSideVector(b, A, sol, N);// :// ここで共役勾配法を実行// ://確保したメモリを解放free(x);free(b);free(A);free(sol);free(r);free(p);free(Ax);
} cg_cpu.c
CPUプログラム(係数行列の生成)
2015/10/14GPGPU講習会22
void setTridiagonalMatrix(double *A, int N){int i,j;
for (j = 0; j < N; j++){for (i = 0; i < N; i++){
A[i+N*j] = 0.0;}
}
i = 0;A[i + N*i ] = ‐4.0;A[i + N*i+1] = 1.0;
for(i = 1; i < N‐1; i++){A[i + N*i‐1] = 1.0;A[i + N*i ] = ‐4.0;A[i + N*i+1] = 1.0;
}i = N‐1;
A[i + N*i‐1] = 1.0;A[i + N*i ] = ‐4.0;
}
41141
14114
cg_cpu.c
i
j
CPUプログラム(右辺ベクトルの生成)
2015/10/14GPGPU講習会23
void setRightHandSideVector(double *b, double *A, double *x, int N){
int i,j;
for (i = 0; i < N; i++){b[i] = 0.0;for (j = 0; j < N; j++){ //係数行列と厳密解ベクトルを用いて行列-ベクトル積を計算し,
b[i] += A[i + N*j] * x[j]; //結果を右辺ベクトルに代入}
}
}
cg_cpu.c
CPUプログラム(共役勾配法部分)
2015/10/14GPGPU講習会24
//残差ベクトルの計算 r(0)=b−Ax(0)
computeResidual(r, b, A, x, N);//残差ベクトルの内積を計算rr = innerProduct(r, r, N);
k = 1;while(rr>err_tol*err_tol && k<=max_ite){
if (k == 1){//p(k) = r(k)+c2
(k−1)p(k−1) c2とpが0のためp(k) = r(k)copy(p, r, N);
}else{
c2 = rr / (c1*dot);//p(k) = r(k)+c2
(k−1)p(k−1)
computeVectorAdd(p, c2, r, 1.0, N);}
//(p(k), Ap(k))を計算//行列ベクトル積Apを実行し,結果とpの内積computeMxV(Ax, A, p, N);dot = innerProduct(p, Ax, N);c1 = rr / dot;
//x(k+1) = x(k)+c1(k)p(k)
//r(k+1) = r(k)−c1(k)Ap(k)
computeVectorAdd(x, 1.0, p, c1, N);computeVectorAdd(r, 1.0, Ax,‐c1, N);
//残差ベクトルの内積を計算rr = innerProduct(r, r, N);
k++;}
/*Compute r(0)=b−Ax(0). Set p(0)=0,c2
(0)=0.For k=1,…, until ||r||/||b|| < , Do
p(k) = r(k)+c2(k−1)p(k−1)
c1(k) = (r(k), r(k))/(p(k), Ap(k))
x(k+1) = x(k)+c1(k)p(k)
r(k+1) = r(k)−c1(k)Ap(k)
c2(k) = (r(k+1), r(k+1))/{c1
(k)(p(k), Ap(k))}EndDo*/ cg_cpu.c
CPUプログラム(共役勾配法内の関数)
2015/10/14GPGPU講習会25
//残差ベクトルr(0)=b−Ax(0)の計算void computeResidual(double *r, double *b,
double *A, double *x,int N){
int i,j;double Ax;for (i = 0; i < N; i++){
Ax = 0.0;for (j = 0; j < N; j++){
Ax += A[i + N*j] * x[j];}r[i] = b[i]‐Ax;
}
}
//内積の計算double innerProduct(double *vec1,
double *vec2, int N){
int i;double dot;
dot=0.0;for (i = 0; i < N; i++){
dot += vec1[i]*vec2[i];}
return dot;
}
cg_cpu.c
CPUプログラム(共役勾配法内の関数)
2015/10/14GPGPU講習会26
//値のコピー(単純代入)p(k) = r(k)void copy(double *lhs, double *rhs, int N){
int i;for (i = 0; i < N; i++){
lhs[i] = rhs[i];}
}
//ベクトル和y(k) = ax(k) + by(k)の計算void computeVectorAdd(
double *y, const double b, double *x, const double a,
int N){
int i;for (i = 0; i < N; i++){
y[i] = a*x[i] + b*y[i];}
}
//行列-ベクトル積Axの計算void computeMxV(double *Ax,
double *A, double *x, int N){
int i,j;for (i = 0; i < N; i++){
Ax[i] = 0.0;for (j = 0; j < N; j++){
Ax[i] += A[i + N*j] * x[j];}
}
}
cg_cpu.c
実行結果(N=210)
2015/10/14GPGPU講習会27
iteration = 1, residual = 2.098826e+003iteration = 2, residual = 4.308518e+002iteration = 3, residual = 1.102497e+002:
iteration = 21, residual = 5.547180e‐009iteration = 22, residual = 1.486358e‐009iteration = 23, residual = 3.982675e‐010x[0] = ‐0.000000x[1] = 1.000000x[2] = 2.000000:
x[1021] = 1021.000000x[1022] = 1022.000000x[1023] = 1023.000000Total amount of Absolute Error = 5.009568e‐010
収束履歴
2015/10/14GPGPU講習会28
反復回数
残差
cuBLASの紹介
cuBLAS*
2015/10/14GPGPU講習会30
BLASのGPU向け実装 Level 1
ベクトルに対する計算
Level 2 行列-ベクトルの計算
Level 3 行列-行列の計算
BLASはFORTRAN用ライブラリとして開発 C言語から使用する場合には注意が必要
配列の添字は1から
メモリの配置がC言語と異なる
*CUBLASやcublasと書かれる事もあるが,NVIDIAのCUDA Toolkit Documentationの標記に従う
cuBLAS
2015/10/14GPGPU講習会31
関数群はドキュメントを参照
http://docs.nvidia.com/cuda/cublas/
実行する際の並列度の設定は自動
処理内容に対応した関数を呼び出す
cublas<>axpy() <>は用いる型に応じて選択
S 単精度実数
D 倍精度実数
C 単精度複素数型
Z 倍精度複素数型
cuBLAS
2015/10/14GPGPU講習会32
計算内容に対応した関数を呼ぶ事で計算を実行
cublas<>axpy() ベクトル和
cublas<>gemv() 行列‐ベクトル積
cublas<>gemm() 行列‐行列積
コンパイルの際はオプションとして –lcublas を追加
y[] = x[] + y[]
y[] = OP(A[][])x[] + y[]
C[][] = OP(A[][])OP(B[][]) + C[][]
OP(A[][])は行列Aに対する処理(転置など)
#include<stdio.h>#include<math.h>#include<cublas_v2.h>
#define N 256
int main(){
int i,j;float A[N*N], B[N], C[N];float *d_A, *d_B, *d_C;float alpha, beta;
for(j=0;j<N;j++){for(i=0;i<N;i++){
A[i+N*j] = (float)i/(float)N;}
}
for(i=0;i<N;i++){C[i] = 0.0f;
}for(i=0;i<N;i++){
B[i] = 1.0f;}
cudaMalloc((void**)&d_A, N*N*sizeof(float));
cudaMalloc((void**)&d_B, N*sizeof(float));
cudaMalloc((void**)&d_C, N*sizeof(float));
alpha = 1.0f;beta = 0.0f;
cuBLASによる行列-ベクトル積
GPGPU講習会33 2015/10/14
cublas_gemv.cu
111
256/2256/2256/2256/1256/1256/1256/0256/0256/0
]2[]1[]0[
ccc
cublasHandle_t handle;cublasCreate(&handle);
cublasSetMatrix(N, N, sizeof(float), A, N, d_A, N);
cublasSetVector(N, sizeof(float), B, 1, d_B, 1);
cublasSetVector(N, sizeof(float), C, 1, d_C, 1);
cublasSgemv(handle, CUBLAS_OP_N, N, N, &alpha,
d_A, N, d_B, 1, &beta, d_C, 1);
cublasGetVector(N, sizeof(float), d_C, 1, C, 1);
for(i=0;i<N;i++){printf("C[%3d] = %f ¥n",j, C[j]);
}
cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);
cublasDestroy(handle);
return 0;}
cuBLASによる行列-ベクトル積
GPGPU講習会34 2015/10/14
cublas_gemv.cu
cuBLASによる行列-ベクトル積
2015/10/14GPGPU講習会35
ヘッダファイルのインクルード #include<cublas_v2.h>
CUBLASのハンドルの作成 cublasHandle_t handle;
cublasCreate(&handle); ハンドル(行列やベクトルの情報を管理)の生成
返値はcublasStatus_t型
GPU上のメモリ確保 cudaMalloc
以前はcublasAlloc()という関数もあったが無くなった
cuBLASによる行列-ベクトル積
2015/10/14GPGPU講習会36
データ転送
cublasSetMatrix(N,N,sizeof(float),A,N,d_A,N);
cublasSetVector(N,sizeof(float),B,1,d_B,1);
cublasSetVector(N,sizeof(float),C,1,d_C,1);
関数内部でcudaMemcpyが呼ばれる それぞれB,d_B, C, d_Cにアクセスする際のストライド
行列(A, d_A)の第1次元の要素数
cuBLASによる行列-ベクトル積
2015/10/14GPGPU講習会37
演算を行う関数の呼び出し
cublasSgemv(handle, CUBLAS_OP_N, N, N, &alpha, d_A, N, d_B, 1, &beta, d_C, 1);
CUBLAS_OP_N 行列Aに対する操作
cublasOperation_t型 CUBLAS_OP_N OP(A)=A 処理しない
CUBLAS_OP_T OP(A)=AT 転置
CUBLAS_OP_C OP(A)=AH 共役転置
C[N] = OP(A[N][N])B[N] + C[N]d_B, d_Cにアクセスする際のストライド
行列d_Aの第1次元の要素数
cuBLASによる行列-ベクトル積
2015/10/14GPGPU講習会38
データの読み戻し
cublasGetVector(N,sizeof(float),d_C,1,C,1);
GPU上のメモリの解放
cudaFree
ハンドルの削除
cublasDestroy(handle);
d_C, Cにアクセス
する際のストライド
cuBLASを使う際の注意点
2015/10/14GPGPU講習会39
行列を表現するときのメモリ配置
A[i][j] 2次元配列でもメモリ上は1次元に配置
i方向が先かj方向が先か
C言語はj方向優先
Fortranはi方向優先
cuBLASを使う際の注意点
2015/10/14GPGPU講習会40
C言語におけるA[i][j]のメモリ上の配置
0/256,0/256,0/256・・・1/256,1/256,1/256・・・2/256,2/256,2/256 ・・・ FortranにおけるA(i,j)のメモリ上の配置
0/256,1/256,2/256・・・0/256,1/256,2/256・・・0/256,1/256,2/256・・・
256/2256/2256/2256/1256/1256/1256/0256/0256/0j
i
cuBLASを使う際の注意点
2015/10/14GPGPU講習会41
C言語におけるA[i][j]のメモリ上の配置
0/256,0/256,0/256・・・1/256,1/256,1/256・・・2/256,2/256,2/256 ・・・ FortranにおけるA(i,j)のメモリ上の配置
0/256,1/256,2/256・・・0/256,1/256,2/256・・・0/256,1/256,2/256・・・
256/2256/2256/2256/1256/1256/1256/0256/0256/0j
i
cuBLASを使う際の注意点
2015/10/14GPGPU講習会42
C言語におけるA[i][j]のメモリ上の配置
0/256,0/256,0/256・・・1/256,1/256,1/256・・・2/256,2/256,2/256 ・・・ FortranにおけるA(i,j)のメモリ上の配置 ←cuBLAS 0/256,1/256,2/256・・・0/256,1/256,2/256・・・0/256,1/256,2/256・・・
256/2256/2256/2256/1256/1256/1256/0256/0256/0j
i
第1次元の要素数(lda)とは?
2015/10/14GPGPU講習会43
Leading dimension of matrix A 行列Aの一部分だけを取り出せるようにするためのオプション
M行N列の行列としてAを宣言したときのMの値
ldaの指定を間違えると(例えば2を指定すると)
987654321
A M
N
682493571
A
987654321
A
行列サイズに2,2,ldaにM(=3)を指定
行列全体を使って計算するときはldaの値=行数と覚える
cuBLASによる共役勾配法の実装
共役勾配法のアルゴリズム(再掲)
2015/10/14GPGPU講習会45
連立一次方程式Ax=bに対する共役勾配法
Compute r(0)=b−Ax(0). Set p(0)=0,c2(0)=0.
For k=1,…, until ||r||/||b|| < , Do
p(k) = r(k)+c2(k−1)p(k−1)
c1(k) = (r(k), r(k))/(p(k), Ap(k))
x(k+1) = x(k)+c1(k)p(k)
r(k+1) = r(k)−c1(k)Ap(k)
c2(k) = (r(k+1), r(k+1))/{c1
(k)(p(k), Ap(k))}
EndDo
Ap 係数行列Aとベクトルpの積
( , ) ベクトル同士の内積
A 係数行列x 解ベクトルb 右辺ベクトルr 残差ベクトルp 補助ベクトル||・|| l2−ノルム
CPUプログラム(共役勾配法)(再掲)
2015/10/14GPGPU講習会46
//残差ベクトルの計算 r(0)=b−Ax(0)
computeResidual(r, b, A, x, N);//残差ベクトルの内積を計算rr = innerProduct(r, r, N);
k = 1;while(rr>err_tol*err_tol && k<=max_ite){
if (k == 1){//p(k) = r(k)+c2
(k−1)p(k−1) c2とpが0のためp(k) = r(k)copy(p, r, N);
}else{
c2 = rr / (c1*dot);//p(k) = r(k)+c2
(k−1)p(k−1)
computeVectorAdd(p, c2, r, 1.0, N);}
//(p(k), Ap(k))を計算//行列ベクトル積Apを実行し,結果とpの内積computeMxV(Ax, A, p, N);dot = innerProduct(p, Ax, N);c1 = rr / dot;
//x(k+1) = x(k)+c1(k)p(k)
//r(k+1) = r(k)−c1(k)Ap(k)
computeVectorAdd(x, 1.0, p, c1, N);computeVectorAdd(r, 1.0, Ax,‐c1, N);
//残差ベクトルの内積を計算rr = innerProduct(r, r, N);
k++;}
/*Compute r(0)=b−Ax(0). Set p(0)=0,c2
(0)=0.For k=1,…, until ||r||/||b|| < , Do
p(k) = r(k)+c2(k−1)p(k−1)
c1(k) = (r(k), r(k))/(p(k), Ap(k))
x(k+1) = x(k)+c1(k)p(k)
r(k+1) = r(k)−c1(k)Ap(k)
c2(k) = (r(k+1), r(k+1))/{c1
(k)(p(k), Ap(k))}EndDo*/ cg_cpu.c
共役勾配法の各処理の置き換え
2015/10/14GPGPU講習会47
共役勾配法に必要な処理とcuBLAS関数の対応
合致する処理が無い場合は処理を分解して対応
処理 cuBLAS関数
ベクトルの代入(y[]=x[]) cublas<>copy
ベクトルのスカラ倍(y[]=a*y[]) cublas<>scal
ベクトル同士の和(y[]=a*x[]+y[]) cublas<>axpy
ベクトルの内積(y=x[]·x[]) cublas<>dot
行列-ベクトル積(y[]=A[][]x[]) cublas<>gemv
r(0)=b−Ax(0)の置き換え
2015/10/14GPGPU講習会48
いきなり難関
cuBLASはベクトルを2個までしか扱えない
3段階に分けて計算
行列-ベクトル積を計算 r(0)=b−Ax(0)
計算した結果を別の配列(例えばAx)に保持
cublasDgemv
bの値をrに代入 r(0)=r−Ax(0)
bを使う箇所をrに置き換えることができる
cublasDcopy
ベクトル和を計算 r(0)=r−1×Ax(0)
cublasDaxpy
r(0)=b−Ax(0)の置き換え
2015/10/14GPGPU講習会49
cublasDgemv cublasDgemv(handle, CUBLAS_OP_N, N, N, &one,
d_A, N, d_x, 1, &zero, d_Ax, 1); Ax[N] = one*A[N][N]x[N] + zero*Ax[N]
cublasDcopy cublasDcopy(handle, N, d_b, 1, d_r, 1); r[N] = b[N]
cublasDaxpy cublasDaxpy(handle, N, &minusOne,d_Ax,1 ,d_r,1); r[N] = r[N] + minusOne*Ax[N]
one =1.0;zero=0.0;
minusOne =‐1.0;
必ず変数のアドレスを指定
r(0)の内積の計算
2015/10/14GPGPU講習会50
cublasDdot cublasDdot(handle, N, d_r, 1, d_r, 1, &rr); rr = (r[N], r[N])
p(k) = r(k)+c2(k−1)p(k−1)の置き換え
2015/10/14GPGPU講習会51
k=1 c2=0のため単純な代入 p(k) = r(m)
cublasDcopy
k>1 ベクトル和を計算 p(k) = r(k)+c2
(k−1)p(k−1)
またもや難題
cublasDaxpyでは足されるベクトル(p)への操作はできない
2段階に分けて処理を実行
pの値をc2倍 p(k)=c2×p(k−1)
cublasDscal ベクトル和を計算 p(k) = p(k)+1×r(k)
cublasDaxpy
p(k) = r(k)+c2(k−1)p(k−1)の置き換え
2015/10/14GPGPU講習会52
cublasDscal cublasDscal(handle, N, &c2, d_p, 1); p[N] = c2*p[N]
cublasDaxpy cublasDaxpy(handle, N, &one,d_r,1 ,d_p,1); p[N] = one*r[N] + p[N]
係数c1の計算
2015/10/14GPGPU講習会53
r(k)の内積は既に計算済みなので省略
分母(p(k), Ap(k))の計算
2段階に分けて計算
行列-ベクトル積Ap(k)を計算
計算した結果を別の配列(例えばAxを流用)に保持
cublasDgemv
pとAxの内積を計算(p(k), Ap(k)) cublasDdot
c1を計算
係数c1の計算
2015/10/14GPGPU講習会54
cublasDgemv cublasDgemv(handle, CUBLAS_OP_N, N, N, &one,
d_A, N, d_p, 1, &zero, d_Ax, 1); Ax[N] = one*OP(A[N][N])p[N] + zero*Ax[N]
cublasDdot cublasDdot(handle, N, d_p, 1, d_Ax, 1, &dot); dot = (p[N], Ax[N])
c1 = rr/dot
解ベクトルx(k)と残差ベクトルr(k)を更新
2015/10/14GPGPU講習会55
ベクトル和を計算 x(k+1) = x(k)+c1(k)×p(k)
cublasDaxpy(handle, N, &c1,d_p,1 ,d_x,1); x[N] = c1*p[N] + x[N]
ベクトル和を計算 r(k+1) = r(k)−c1(k)×Ap(k)
既に計算した行列-ベクトル積(Ax)を利用
cublasDaxpy(handle, N, &minusC1, d_Ax,1 ,d_r,1); r[N] = minusC1*Ax[N] + r[N]
minusC1 =‐c1;
係数c2の計算
2015/10/14GPGPU講習会56
分母(p(k), Ap(k))は計算済み
更新されたr(k+1)の内積を計算
cublasDdot(handle, N, d_r, 1, d_r, 1, &rr); rr = (r[N], r[N])
c2 = rr/(c1*dot)
GPUプログラム(制御部分)
2015/10/14GPGPU講習会57
#include <stdlib.h>#include <stdio.h>#include <cublas_v2.h>int main(void){
int N = 1 << 10; //未知数の数210const double err_tol = 1e‐9; //許容誤差const int max_ite = 1<<20;//反復回数の上限
double *x; //近似解ベクトルdouble *b; //右辺ベクトルdouble *A; //係数行列double *sol; //厳密解
//GPU用変数double *d_x;double *d_b;double *d_A;double *d_r, rr;double *d_p, *d_Ax;double c1, c2, minusC1, dot;
//cublasで利用する定数const double one = 1.0;const double zero = 0.0;const double minusOne = ‐1.0;int i, k;
A = (double *)malloc(sizeof(double)*N*N);x = (double *)malloc(sizeof(double)*N);b = (double *)malloc(sizeof(double)*N);sol= (double *)malloc(sizeof(double)*N);
for (i = 0; i < N; i++){sol[i] = (double)i; //厳密解を設定
x[i] = 0.0; //近似解を0で初期化}//係数行列Aの生成setTridiagonalMatrix(A, N);//右辺ベクトルbの生成setRightHandSideVector(b, A, sol, N);
cg_cublas.cu
GPUプログラム(制御部分)
2015/10/14GPGPU講習会58
//cuBLASハンドルの生成cublasHandle_t cublasHandle = 0;cublasStatus_t cublasStatus;cublasStatus=cublasCreate(&cublasHandle);
cudaMalloc((void **)&d_A, N*N*sizeof(double));
cudaMalloc((void **)&d_x, N *sizeof(double));
cudaMalloc((void **)&d_b, N *sizeof(double));
cudaMalloc((void **)&d_r, N *sizeof(double));
cudaMalloc((void **)&d_p, N *sizeof(double));
cudaMalloc((void **)&d_Ax,N *sizeof(double));
cublasSetMatrix(N, N, sizeof(double), A, N, d_A, N);
cublasSetVector(N, sizeof(double), x, 1, d_x, 1);
cublasSetVector(N, sizeof(double), b, 1, d_b, 1);
// :// ここで共役勾配法を実行// :
//ハンドルの破棄cublasDestroy(cublasHandle);
//確保したメモリを解放free(x);free(b);free(A);free(sol);cudaFree(d_x);cudaFree(d_b);cudaFree(d_A);cudaFree(d_r);cudaFree(d_p);cudaFree(d_Ax);
} cg_cublas.cu
GPUプログラム(共役勾配法部分)
2015/10/14GPGPU講習会59
//残差ベクトルの計算 r(0)=b−Ax(0)
cublasDgemv(cublasHandle, CUBLAS_OP_N, N, N, &one, d_A, N, d_x, 1, &zero, d_Ax, 1);cublasDcopy(cublasHandle, N, d_b, 1, d_r, 1);cublasDaxpy(cublasHandle, N, &minusOne, d_Ax, 1, d_r, 1);//残差ベクトルの内積を計算cublasDdot(cublasHandle, N, d_r, 1, d_r, 1, &rr);
k = 1;while(rr>err_tol*err_tol && k<=max_ite){
if (k == 1){//p(k) = r(k)+c2
(k−1)p(k−1) c2とpが0のためp(k) = r(k)cublasDcopy(cublasHandle, N, d_r, 1, d_p, 1);
}else{
c2 = rr / (c1*dot);//p(k) = r(k)+c2
(k−1)p(k−1)
cublasDscal(cublasHandle, N, &c2, d_p, 1);cublasDaxpy(cublasHandle, N, &one, d_r, 1, d_p, 1);
}cg_cublas.cu
GPUプログラム(共役勾配法部分)
2015/10/14GPGPU講習会60
//(p(k), Ap(k))を計算//行列ベクトル積Apを実行し,結果とpの内積cublasDgemv(cublasHandle, CUBLAS_OP_N, N, N, &one, d_A, N, d_p, 1, &zero, d_Ax, 1);cublasDdot(cublasHandle, N, d_p, 1, d_Ax, 1, &dot);c1 = rr / dot;
//x(k+1) = x(k)+c1(k)p(k)
//r(k+1) = r(k)−c1(k)Ap(k)
cublasDaxpy(cublasHandle, N, &c1, d_p, 1, d_x, 1);cublasDaxpy(cublasHandle, N, &minusC1, d_Ax, 1, d_r, 1);
//残差ベクトルの内積を計算cublasDdot(cublasHandle, N, d_r, 1, d_r, 1, &rr);
cudaDeviceSynchronize();k++;
}
//計算結果をGPUからCPUへコピーcublasGetVector(N, sizeof(double), d_x, 1, x, 1);
cg_cublas.cu
実行時間の比較
2015/10/14GPGPU講習会61
ベクトルの次元 CPU [sec/iteration]
cuBLAS[sec/iteration]
210 8.26×10‐3 0.553×10‐3
211 39.6×10‐3 0.865×10‐3
212 231×10‐3 1.84×10‐3
213 1.20 5.78×10‐3
214 6.06 23.0×10‐3
実行時間の比較
2015/10/14GPGPU講習会62
ベクトルの次元
1反復あたりの実行時間[sec]
改善点
2015/10/14GPGPU講習会63
ベクトルの次元214は多いか? 少なすぎて話にならない
現在のシミュレーションでは220~230(数千万から数億)
行列の格納形式
無駄が多い
配列要素数に対する非ゼロ要素の割合
(2+(N‐2)*3+2) / N2
N=214のとき非ゼロ要素の割合は0.02%
改善点
行列の非ゼロ要素のみを格納してメモリ利用効率を上げる* cuSPARSEを利用
41141
14114
0
0
*cuBLASにも帯行列‐ベクトル積を計算する関数があるので,それを使えば0要素を保持しなくてもよくなる.調査してみよう.
補足
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会65
cublasCreate cublasStatus_tcublasCreate(cublasHandle_t *handle)
cublasDestroy cublasStatus_tcublasDestroy(cublasHandle_t handle)
cuBLASのハンドルのアドレス
cuBLASのハンドル
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会66
cublasSetVector y[n]<‐x[n] cublasStatus_tcublasSetVector(int n,
int elemSize,const void *x,int incx,void *y,int incy)
ベクトルの要素数
1要素のバイト数
コピー元ベクトルのアドレス
コピー元ベクトルのストライド
コピー先ベクトルのアドレス
コピー先ベクトルのストライド
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会67
cublasGetVector y[n]<‐x[n] cublasStatus_tcublasGetVector(int n,
int elemSize,const void *x,int incx,void *y,int incy)
ベクトルの要素数
1要素のバイト数
コピー元ベクトルのアドレス
コピー元ベクトルのストライド
コピー先ベクトルのアドレス
コピー先ベクトルのストライド
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会68
cublasSetMatrix B[rows][cols]<‐A[rows][cols] cublasStatus_tcublasSetMatrix(int rows,
int cols,int elemSize,const void *A,int lda,void *B,int ldb)
行の要素数
列の要素数
1要素のバイト数
コピー元行列のアドレス
コピー元行列の1次元の要素数
コピー先行列のアドレス
コピー先行列の1次元の要素数
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会69
cublasGetMatrix B[rows][cols]<‐A[rows][cols] cublasStatus_tcublasGetMatrix(int rows,
int cols,int elemSize,const void *A,int lda,void *B,int ldb)
行の要素数
列の要素数
1要素のバイト数
コピー元行列のアドレス
コピー元行列の1次元の要素数
コピー先行列のアドレス
コピー先行列の1次元の要素数
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会70
cublasDcopy y[n]<‐x[n] cublasStatus_tcublasDcopy(cublasHandle_t handle,
int n,const double *x,int incx,double *y,int incy)
ハンドル
ベクトルの要素数
コピー元ベクトルのアドレス
コピー元ベクトルのストライド
コピー先ベクトルのアドレス
コピー先ベクトルのストライド
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会71
cublasDdot result <‐ (x[n]·y[n]) cublasStatus_tcublasDdot (cublasHandle_t handle,
int n,const double *x,int incx,const double *y,int incy,double *result)
ハンドル
ベクトルの要素数
一つ目のベクトルのアドレス
一つ目のベクトルのストライド
二つ目のベクトルのアドレス
二つ目のベクトルのストライド
内積の結果を格納する変数のアドレス
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会72
cublasDscal x[n]<‐alpha*x[n] cublasStatus_tcublasDscal(cublasHandle_t handle,
int n,const double *alpha,double *x,int incx)
ハンドル
ベクトルの要素数
かける数を代入した変数のアドレス
入出力ベクトルのアドレス
入出力ベクトルのストライド
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会73
cublasDaxpy y[n]<‐alpha*x[n] + y[n] cublasStatus_tcublasDaxpy(cublasHandle_t handle,
int n,const double *alpha,const double *x,int incx,double *y,int incy)
ハンドル
ベクトルの要素数
かける数を代入した変数のアドレス
加算するベクトルのアドレス
加算するベクトルのストライド
加算されるベクトルのアドレス
加算されるベクトルのストライド
A. cuBLAS関数のリファレンス
2015/10/14GPGPU講習会74
cublasDgemv y[m] = alpha*OP(A[m][n])x[n] + beta*y[m] cublasStatus_tcublasDgemv(cublasHandle_t handle,
cublasOperation_t trans,
int m, int n,
const double *alpha,
const double *A, int lda,
const double *x, int incx,
const double *beta,
double *y, int incy)
ハンドル
行列に対する操作
行列の行と列の要素数
かける数を代入した変数のアドレス
行列と1次元の要素数
行列にかけるベクトルのアドレスとストライド
計算結果のベクトルにかける数を代入した変数のアドレス
計算結果のベクトルのアドレスとストライド
B. 実行時間の測定
GPGPU講習会75
#include<time.h> // clock_t型や関数clock()を利用
int main(void){clock_t start_c, stop_c;float time_s;
start_c = clock(); //プログラム実行時からの経過時間を取得: //: //ここに処理を記述: //
stop_c = clock(); //プログラム実行時からの経過時間を取得
//処理に要した時間を秒に変換time_s = (stop_c‐start_c)/(float)CLOCKS_PER_SEC;printf(...); //画面表示
return 0;}
2015/10/14
B. 実行時間の測定
CPUで時間計測はできない
clock関数を使うと実行時間を正しく測定できない
CUDAで用意されている関数を使って時間を計測
2015/10/14GPGPU講習会76
初期化の指示初期化
カーネルの実行指示カーネルを実行
結果の取得実行結果をコピー
time
CPUとGPUは非同期CPUは別の処理を実行可能
必要なデータのコピーメモリに書込
C. CPUコードの実行時間の測定
2015/10/14GPGPU講習会77
#include<time.h> // clock_t型や関数clock()を利用
int main(void){clock_t start_c, stop_c;float time_s;
start_c = clock(); //プログラム実行時からの経過時間を取得: //: //ここに処理を記述: //
stop_c = clock(); //プログラム実行時からの経過時間を取得
//処理に要した時間を秒に変換time_s = (stop_c‐start_c)/(float)CLOCKS_PER_SEC;printf(...); //画面表示
return 0;}
D. CUDA Eventで時間測定*
GPUへの指示をEventと呼び,そのEventが発生した時間の差から時間を測定
準備
どのEventを取り扱うかを宣言
kernelの呼び出し(startイベント)と実行終了(stopイベント)
イベントを取り扱う変数の宣言と利用準備
*http://gpu.fixstars.com/index.php/CUDA_Eventで時間計測
2015/10/14GPGPU講習会78
cudaEvent_t start,stop; ・・・イベントを取り扱う変数float elapsed_time_ms = 0.0f; ・・・経過時間保存用
cudaEventCreate(&start); ・・・イベントのクリエイトcudaEventCreate(&stop);
D. CUDA Eventで時間測定
Event発生時間を記録
Eventの同期
startとstopの時間を正しく記録し終わっていることを保証
2015/10/14GPGPU講習会79
cudaEventRecord(start, 0);//ここでkernelを実行cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
D. CUDA Eventで時間測定
Eventが発生した時間差を計算(ミリ秒単位)
Eventの時間を記録した変数を破棄
2015/10/14GPGPU講習会80
cudaEventElapsedTime(&elapsed_time_ms, start,stop);
cudaEventDestroy(start);cudaEventDestroy(stop);
D. CUDA Eventで時間測定
int main(void){float *a,*b,*c;//イベント記録の準備
cudaMalloc((void **)&a, Nbytes);cudaMalloc((void **)&b, Nbytes);cudaMalloc((void **)&c, Nbytes);
init<<< , >>>(a,b,c);
//イベント発生時間の記録add<<< , >>>(a,b,c);//イベント発生時間の記録//イベントの同期//イベント間の時間差を計算printf("%f¥n", elapsed_time_ms);//イベントの破棄return 0;
}
2015/10/14GPGPU講習会81