More Related Content Similar to GPGPU Seminar (GPU Accelerated Libraries, 3 of 3, Thrust) (20) GPGPU Seminar (GPU Accelerated Libraries, 3 of 3, Thrust) 3. 本日の内容
GPU最適化ライブラリの利用(その3)
thrustの紹介
thrustによる共役勾配法実装の改良(可搬性の向上)
連立一次方程式を解くプログラムの作成
ライブラリを利用
関数(およびCUDA API)の呼出のみで作成
3回に分けて徐々に効率化
今回はThrustライブラリを利用して実装の効率化を図る
併せてCPUコードとの共通性や可搬性を向上する
2015/10/28GPGPU講習会3
14. 共役勾配法のアルゴリズム
2015/10/28GPGPU講習会14
連立一次方程式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−ノルム
16. 共役勾配法のバリエーション
2015/10/28GPGPU講習会16
安定化双共役勾配法(Bi‐CGSTAB法)
非対称行列に対応
Compute r(0)=b−Ax(0). Set p(0)=0,c2
(0)=0, r*=r(0).
For k=1,…, until ||r||/||b|| < , Do
p(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 補助ベクトル
18. CPUプログラム(制御部分)
2015/10/28GPGPU講習会18
#include<stdlib.h>
#include<stdio.h>
#include<math.h>
int main(void){
int N = 1 << 10; //未知数の数210
const 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
21. CPUプログラム(共役勾配法部分)
2015/10/28GPGPU講習会21
//残差ベクトルの計算 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
23. CPUプログラム(共役勾配法内の関数)
2015/10/28GPGPU講習会23
//値のコピー(単純代入)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
29. コンテナ
2015/10/28GPGPU講習会29
thrust::host_vector<T>
ホスト(CPU)メモリに確保されるvector
thrust::device_vector<T>
デバイス(GPU)メモリに確保されるvector
#include<thrust/host_vector.h>
#include<thrust/device_vector.h>
int main(){
thrust::host_vector<int> h_vec(3); //3要素からなるvectorをホストメモリに確保
//templateを利用して型を決定
h_vec[0] = 10; h_vec[1] = 20; h_vec[2] = 30;//配列のように各要素にアクセス
thrust::device_vector<int> d_vec = h_vec; //vectorをGPUへコピー
return 0; //vectorは自動的に解放される(free()やcudaFree()を呼ぶ必要がない)
}
確保される場所が異な
るだけで取り扱い方法
は同じ
型を指定
31. イテレータ(iterator, 反復子)
2015/10/28GPGPU講習会31
Thrustの並列処理の書き方
forループのように個々の要素に対する処理は書かない
「vectorのここからここまでにこの処理を適用する」という書
き方をする
#include<thrust/host_vector.h>
int main(){
thrust::host_vector<int> vec1(3);
thrust::host_vector<int> vec2(3);
vec1[0] = 10; vec1[1] = 20; vec1[2] = 30;
//ここから ここまで vec1をコピー(コピー先はvec2の最初から)
thrust::copy(vec1.begin(), vec1.end(), vec2.begin());
return 0;
}
42. constant_iterator
2015/10/28GPGPU講習会42
一定値を返すイテレータ
vectorの値を0で初期化する場合などに有効
fill
#include<stdio.h>
#include<thrust/host_vector.h>
#include<thrust/iterator/constant_iterator.h>
int main(void){
int N=10;
thrust::host_vector<int> vec(N);
thrust::constant_iterator<int> itr_cnst(0);//一定値(ここでは0)を返すイテレータ
thrust::copy(itr_cnst, itr_cnst+N, vec.begin());//イテレータがN個のデータを
//生成し,vecに書込
for(int i=0;i<N;i++)
printf("vec(%d) = %d¥n", i, vec[i]);
return 0;
}
一定値
44. counting_iterator
2015/10/28GPGPU講習会44
連続的に変化する値を返すイテレータ
1,2,3,4・・・など連続的な値の生成に有効
fill
#include<stdio.h>
#include<thrust/host_vector.h>
#include<thrust/iterator/counting_iterator.h>
int main(void){
int N=10;
thrust::host_vector<int> vec(N);
thrust::counting_iterator<int> itr_count(0);//0から1ずつ連続的に変化する値を
//返すイテレータ
thrust::copy(itr_count, itr_count+N, vec.begin());//イテレータがN個のデータを
//生成し,vecに書込
for(int i=0;i<N;i++)
printf("vec(%d) = %d¥n", i, vec[i]);
return 0;
}
初期値
48. transform_iterator
2015/10/28GPGPU講習会48
#include<stdio.h>
#include<thrust/host_vector.h>
#include<thrust/iterator/transform_iterator.h>
#include<thrust/sequence.h>
struct negate{//入力された値に‐1をかけた値を返す
__host__ __device__ int operator()(const int x){
return ‐x;
}
};
int main(void){
int N=10;
thrust::host_vector<int> vec(N);
thrust::sequence(vec.begin(),vec.end());//vecに0,1,2,3,...を設定
//vecの要素に対してnegate()を実行した結果を返すイテレータ
thrust::transform_iterator<negate,thrust::host_vector<int>::iterator,int>
itr_trns(vec.begin(),negate());
thrust::copy(itr_trns, itr_trns+N, vec.begin());
for(int i=0;i<N;i++) printf("vec(%d) = %d¥n", i, vec[i]);
return 0;
}
49. transform関数
2015/10/28GPGPU講習会49
#include<stdio.h>
#include<thrust/host_vector.h>
#include<thrust/iterator/transform_iterator.h>
#include<thrust/sequence.h>
struct negate{//入力された値に‐1をかけた値を返す
__host__ __device__ int operator()(const int x){
return ‐x;
}
};
int main(void){
int N=10;
thrust::host_vector<int> vec(N);//vecに0,1,2,3,...を設定
thrust::sequence(vec.begin(),vec.end());
//vecの要素に対してnegate()を実行した結果をvecに代入
thrust::transform(vec.begin(), vec.end(), vec.begin(), negate());
for(int i=0;i<N;i++) printf("vec(%d) = %d¥n", i, vec[i]);
return 0;
}
51. l2ノルムの計算(単純な実装)
2015/10/28GPGPU講習会51
#include<stdio.h>
#include<thrust/host_vector.h>
#include<thrust/sequence.h>
struct square{//入力された値の2乗を返す
__host__ __device__ int operator()(int x){
return x*x;
}
};
int main(void){//vec={1,2,3,4,...,N}のl2ノルムを計算するプログラム
int N=10;
thrust::host_vector<int> vec(N);//ベクトルの値(1,2,3,4,...)を保持
thrust::host_vector<int> temp(N);//ベクトルの値の2乗(1,4,9,16,...)を保持
//メモリの読み書き write N, read N + write N, read N
thrust::sequence(vec.begin(),vec.end(),1);//vecに1,2,3,4,...を設定
thrust::transform(vec.begin(),vec.end(),temp.begin(),square());//vecの2乗
float snrm2 = sqrt(thrust::reduce(temp.begin(),temp.end()));//総和と平方根
printf("%f¥n",snrm2);
return 0;
} 無駄が多い書き方
54. ベクトル和(Functorを利用)
2015/10/28GPGPU講習会54
#include<thrust/device_vector.h>
#include<thrust/transform.h>
#include<thrust/sequence.h>
#include<iostream>
struct saxpy{//a*x + yを返す
float a; //aの値
saxpy(float _a) : a(_a){} //コンストラクタ
__host__ __device__ float operator()(float x, float y){
return a*x + y;
}
};
int main(void){
int N=256;
thrust::device_vector<float> x(N), y(N);
thrust::sequence(x.begin(),x.end(),1); //xに1,2,3,4,...を設定
thrust::fill(y.begin(),y.end(),0); //yを0に初期化
//ベクトルx,yの全要素をsaxpyに渡して2.0*x+yを計算し,結果をyに代入
thrust::transform(x.begin(),x.end(),y.begin(),y.begin(),saxpy(2.0));
for(int i=0;i<N;i++) std::cout<< "y("<<i<<")="<<y[i]<<std::endl;
return 0;
}
55. ベクトル和(placeholdersを利用)
2015/10/28GPGPU講習会55
#include<thrust/device_vector.h>
#include<thrust/transform.h>
#include<thrust/sequence.h>
#include<iostream>
int main(void){
int N=256;
thrust::device_vector<float> x(N), y(N);
thrust::sequence(x.begin(),x.end(),1);
thrust::fill(y.begin(),y.end(),0);
//ベクトルx,yの全要素で2.0*x+yを計算し,結果をyに代入
//thrust::placeholdersを利用する事で,引数のベクトルに対する処理を直接記述(ファンクタが不要)
thrust::transform(x.begin(),x.end(),y.begin(),y.begin(),
2.0*thrust::placeholders::_1+thrust::placeholders::_2);
for(int i=0;i<N;i++)
std::cout<< "y("<<i<<")="<<y[i]<<std::endl;
return 0;
}
61. GPUプログラム(制御部分)
2015/10/28GPGPU講習会61
//係数行列Aの生成
setTridiagonalMatrix(Aval, AcolIdx, ArowPtr, N, NonZero);
//右辺ベクトルbの生成
setRightHandSideVector(b, Aval, AcolIdx, ArowPtr, sol, N);
//cuBLASハンドルの生成
cublasHandle_t cublasHandle = 0;
cublasStatus_t cublasStatus;
cublasStatus=cublasCreate(&cublasHandle);
//cuSPARSハンドルの生成
cusparseHandle_t cusparseHandle = 0;
cusparseStatus_t cusparseStatus;
cusparseStatus = cusparseCreate(&cusparseHandle);
//疎行列の詳細情報を扱う変数descrを生成
cusparseMatDescr_t descr = 0;
cusparseStatus = cusparseCreateMatDescr(&descr);
cusparseSetMatType(descr, CUSPARSE_MATRIX_TYPE_GENERAL); //行列の種類の指定
cusparseSetMatIndexBase(descr, CUSPARSE_INDEX_BASE_ZERO);//配列が0開始であることを明記
cg_cusparse.cu
(cuSPARSE版再掲)
62. GPUプログラム(制御部分)
2015/10/28GPGPU講習会62
//GPU上にメモリを確保
cudaMalloc((void **)&d_Aval , NonZero*sizeof(double));
cudaMalloc((void **)&d_AcolIdx, NonZero*sizeof(int) );
cudaMalloc((void **)&d_ArowPtr, (N+1) *sizeof(int) );
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));
//GPU上のメモリに設定したデータをコピー
cublasSetVector(NonZero, sizeof(double), Aval , 1, d_Aval , 1);
cublasSetVector(NonZero, sizeof(int) , AcolIdx, 1, d_AcolIdx, 1);
cublasSetVector(N+1 , sizeof(int) , ArowPtr, 1, d_ArowPtr, 1);
cublasSetVector(N, sizeof(double), x, 1, d_x, 1);
cublasSetVector(N, sizeof(double), b, 1, d_b, 1);
// :
// ここで共役勾配法を実行
// :
cg_cusparse.cu
(cuSPARSE版再掲)
64. GPUプログラム(共役勾配法部分)
2015/10/28GPGPU講習会64
//残差ベクトルの計算 r(0)=b−Ax(0)
cusparseDcsrmv(cusparseHandle, CUSPARSE_OPERATION_NON_TRANSPOSE, N, N,
one, descr, d_Aval, d_ArowPtr, d_AcolIdx, d_x, zero, d_Ax);
//cusparseDcsrmv(cusparseHandle, CUSPARSE_OPERATION_NON_TRANSPOSE, N, N, NonZero,
// &one, descr, d_Aval, d_ArowPtr, d_AcolIdx, d_x, &zero, d_Ax);
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);
}
cusparseが新しい
バージョンの場合
cg_cusparse.cu
(cuSPARSE版再掲)
65. GPUプログラム(共役勾配法部分)
2015/10/28GPGPU講習会65
//(p(k), Ap(k))を計算
//行列ベクトル積Apを実行し,結果とpの内積
cusparseDcsrmv(cusparseHandle, CUSPARSE_OPERATION_NON_TRANSPOSE, N, N,
one, descr, d_Aval, d_ArowPtr, d_AcolIdx, d_p, zero, d_Ax);
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);
cudaThreadSynchronize();
k++;
}
//計算結果をGPUからCPUへコピー
cublasGetVector(N, sizeof(double), d_x, 1, x, 1); cg_cusparse.cu
(cuSPARSE版再掲)
69. p(k) = r(k)+c2
(k−1)p(k−1)の置き換え
2015/10/28GPGPU講習会69
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
70. p(k) = r(k)+c2
(k−1)p(k−1)の置き換え
2015/10/28GPGPU講習会70
k>1
ベクトル和を計算 p(k) = r(k)+c2
(k−1)p(k−1)
Thrustのtransformは任意の処理を実行できる
k=1は単純な代入のため代入演算子を利用
//Functor
template<typename T> struct xpay{
T a;
xpay(T _a) :a(_a){}
__host__ __device__ T operator()(const T x, const T y){
//y <‐ x + a*y
return x + a*y;
}
};
//thrust::transform関数を用いた記述
//p = r+c2p
thrust::transform(r.begin(), r.end(), p.begin(), p.begin(), xpay<double>(c2));
72. GPUプログラム(制御部分)
2015/10/28GPGPU講習会72
thrust::host_vector<double> x(N); //近似解ベクトル
thrust::host_vector<double> b(N); //右辺ベクトル
thrust::host_vector<double> Aval(Nnz); //係数行列Aの情報
thrust::host_vector<int> AcolIdx(Nnz); //要素数の列の情報
thrust::host_vector<int> ArowPtr(N+1); //行にある要素数の情報
thrust::host_vector<double> sol(N); //厳密解
double c1, c2, rr, dot;
int k;
const double one = 1.0;
const double zero = 0.0;
thrust::fill(b.begin(), b.end(), (double)0); //右辺ベクトルbを初期化
thrust::fill(x.begin(), x.end(), (double)0); //近似解ベクトルxを初期化
thrust::sequence(sol.begin(), sol.end()); //厳密解ベクトルを0,1,2,...と初期化
cg_thrust.cu
73. GPUプログラム(制御部分)
2015/10/28GPGPU講習会73
//係数行列と右辺ベクトルを生成
//vectorをポインタにキャストすることで既存の関数を再利用
setTridiagonalMatrix(thrust::raw_pointer_cast(&Aval[0]),
thrust::raw_pointer_cast(&AcolIdx[0]),
thrust::raw_pointer_cast(&ArowPtr[0]), N, NonZero);
setRightHandSideVector(thrust::raw_pointer_cast(&b[0]),
thrust::raw_pointer_cast(&Aval[0]),
thrust::raw_pointer_cast(&AcolIdx[0]),
thrust::raw_pointer_cast(&ArowPtr[0]),
thrust::raw_pointer_cast(&sol[0]), N);
//cuSPARSハンドルの生成
cusparseHandle_t cusparseHandle = 0;
cusparseStatus_t cusparseStatus;
cusparseStatus = cusparseCreate(&cusparseHandle);
//疎行列の詳細情報を扱う変数descrを生成
cusparseMatDescr_t descr = 0;
cusparseStatus = cusparseCreateMatDescr(&descr);
cusparseSetMatType(descr, CUSPARSE_MATRIX_TYPE_GENERAL); //行列の種類の指定
cusparseSetMatIndexBase(descr, CUSPARSE_INDEX_BASE_ZERO);//配列が0開始であることを明記
cg_thrust.cu
77. GPUプログラム(共役勾配法部分)
2015/10/28GPGPU講習会77
//残差ベクトルの計算 r(0)=b−Ax(0)
cusparseDcsrmv(cusparseHandle, CUSPARSE_OPERATION_NON_TRANSPOSE, N, N,
one, descr, d_Aval, d_ArowPtr, d_AcolIdx, d_x, zero, d_Ax);
thrust::transform(dev_Ax.begin(), dev_Ax.end(), dev_b.begin(), dev_r.begin(),
axpy<double>(‐1.0));
//thrustのバージョンが新しい場合は ‐1.0*thrust::placeholders::_1 + thrust::placeholders::_2
//残差ベクトルの内積を計算
rr = thrust::inner_product(dev_r.begin(), dev_r.end(), dev_r.begin(), 0.0);
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)
dev_p = dev_r;
}
else{ //p(k) = r(k)+c2
(k−1)p(k−1)
c2 = rr / (c1*dot);
thrust::transform(dev_r.begin(), dev_r.end(), dev_p.begin(), dev_p.begin(),
xpay<double>(c2));
//thrustのバージョンが新しい場合は thrust::placeholders::_1 + c2*thrust::placeholders::_2
}
cg_thrust.cu
78. GPUプログラム(共役勾配法部分)
2015/10/28GPGPU講習会78
//(p(k), Ap(k))を計算
//行列ベクトル積Apを実行し,結果とpの内積
cusparseDcsrmv(cusparseHandle, CUSPARSE_OPERATION_NON_TRANSPOSE, N, N,
&one, descr, d_Aval, d_ArowPtr, d_AcolIdx, d_p, &zero, d_Ax);
dot = thrust::inner_product(dev_p.begin(), dev_p.end(), dev_Ax.begin(), 0.0);
c1 = rr / dot;
//x(k+1) = x(k)+c1
(k)p(k)
//r(k+1) = r(k)−c1
(k)Ap(k)
thrust::transform(dev_p .begin(), dev_p .end(), dev_x.begin(), dev_x.begin(),
axpy<double>( c1));
//thrustのバージョンが新しい場合は c1*thrust::placeholders::_1 + thrust::placeholders::_2
thrust::transform(dev_Ax.begin(), dev_Ax.end(), dev_r.begin(), dev_r.begin(),
axpy<double>(‐c1));
//thrustのバージョンが新しい場合は‐c1*thrust::placeholders::_1 + thrust::placeholders::_2
//残差ベクトルの内積を計算
rr = thrust::inner_product(dev_r.begin(), dev_r.end(), dev_r.begin(), 0.0);
cudaThreadSynchronize();
k++;
}
//計算結果をGPUからCPUへコピー
x = dev_x;
cg_thrust.cu
86. Thrustのデバイスシステムの選択
2015/10/28GPGPU講習会86
これまで
デバイス=GPU
Thrustではいくつかの並列処理の環境を選択可能
コンパイルオプション(プリプロセッサの定義)で指定
‐Dに続けてTHRUST_DEVICE_BACKENDを指定*
CUDA (GPU) THRUST_DEVICE_BACKEND_CUDA
OpenMP(CPU) THRUST_DEVICE_BACKEND_OMP
TBB (CPU) THRUST_DEVICE_BACKEND_TBB
intelが提供するの並列実行ライブラリ
*古いバージョンではTHRUST_DEVICE_BACKENDだったが,現行はTHRUST_DEVICE_SYSTEMを利用する
(THRUST_DEVICE_SYSTEM_CUDA等)
89. #if - #endifディレクティブ
#if の後ろに定数式を記述
定数式が真なら#if ‐ #endif間の命令を有効化して
コンパイル
defined()でマクロが定義されているかを判別
GPGPU講習会89 2015/10/28
#include<stdio.h>
#define DEBUG
int main(void){
int a=1,b=2,c=0;
c = a + b;
#if defined(DEBUG) //DEBUGが定義されていればprintfを実行
printf("%d, %d, %d¥n",a,b,c);
#endif
return 0;
}
90. #if - #endifディレクティブ
定数式に利用できる演算
defined() マクロが定義されているかを判別
比較
a>b, a<b, a==b
論理演算
条件1 && 条件2
条件1 || 条件2
!条件
GPGPU講習会90 2015/10/28
91. #if - #endifディレクティブ
2015/10/28GPGPU講習会91
THRUST_DEVICE_BACKEND_CUDAが定義されている場合
THRUST_DEVICE_BACKENDが定義されていない場合
cuSPARSEを有効化
ハンドルの生成や破棄,デスクリプタ作成も含む
THRUST_DEVICE_BACKEND_OMPが定義されている場合
CPUで実行できる行列-ベクトル積の関数を指定
前回の講習会で作った関数を再利用
#if !defined(THRUST_DEVICE_BACKEND) || (THRUST_DEVICE_BACKEND==THRUST_DEVICE_BACKEND_CUDA)
//GPUで実行する場合のみここを有効化
#endif
#if (THRUST_DEVICE_BACKEND==THRUST_DEVICE_BACKEND_OMP)
//OpenMPで並列化する場合のみここを有効化
#endif
93. GPUプログラム
2015/10/28GPGPU講習会93
int main(void){
int N = 1 << 20; //未知数の数220
const double err_tol = 1e‐9; //許容誤差
const int max_ite = 1<<20; //反復回数の上限
int NonZero = 2 + (N‐2)*3 + 2; //非ゼロ要素数
thrust::host_vector<double> x(N); //近似解ベクトル
thrust::host_vector<double> b(N); //右辺ベクトル
thrust::host_vector<double> Aval(Nnz); //係数行列Aの情報
thrust::host_vector<int> AcolIdx(Nnz); //要素数の列の情報
thrust::host_vector<int> ArowPtr(N+1); //行にある要素数の情報
thrust::host_vector<double> sol(N); //厳密解
double c1, c2, rr, dot;
int k;
const double one = 1.0;
const double zero = 0.0;
thrust::fill(b.begin(), b.end(), (double)0); //右辺ベクトルbを初期化
thrust::fill(x.begin(), x.end(), (double)0); //近似解ベクトルxを初期化
thrust::sequence(sol.begin(), sol.end()); //厳密解ベクトルを0,1,2,...と初期化
cg_thrust_omp.cu
94. GPUプログラム
2015/10/28GPGPU講習会94
//係数行列と右辺ベクトルを生成
//vectorをポインタにキャストすることで既存の関数を再利用
setTridiagonalMatrix(thrust::raw_pointer_cast(&Aval[0]),
thrust::raw_pointer_cast(&AcolIdx[0]),
thrust::raw_pointer_cast(&ArowPtr[0]), N, NonZero);
setRightHandSideVector(thrust::raw_pointer_cast(&b[0]),
thrust::raw_pointer_cast(&Aval[0]),
thrust::raw_pointer_cast(&AcolIdx[0]),
thrust::raw_pointer_cast(&ArowPtr[0]),
thrust::raw_pointer_cast(&sol[0]), N);
//GPUで実行する場合のみ有効化
#if !defined(THRUST_DEVICE_BACKEND) || (THRUST_DEVICE_BACKEND==THRUST_DEVICE_BACKEND_CUDA)
//cuSPARSハンドルの生成
cusparseHandle_t cusparseHandle = 0;
cusparseStatus_t cusparseStatus;
cusparseStatus = cusparseCreate(&cusparseHandle);
//疎行列の詳細情報を扱う変数descrを生成
cusparseMatDescr_t descr = 0;
cusparseStatus = cusparseCreateMatDescr(&descr);
cusparseSetMatType(descr, CUSPARSE_MATRIX_TYPE_GENERAL); //行列の種類の指定
cusparseSetMatIndexBase(descr, CUSPARSE_INDEX_BASE_ZERO);//配列が0開始であることを明記
#endif
cg_thrust_omp.cu
96. GPUプログラム
2015/10/28GPGPU講習会96
//残差ベクトルの計算 r(0)=b−Ax(0)
#if (THRUST_DEVICE_BACKEND==THRUST_DEVICE_BACKEND_OMP)//OpenMPの場合
computeMxV(d_Ax, d_Aval, d_AcolIdx, d_ArowPtr, d_x, N);
#endif
#if !defined(THRUST_DEVICE_BACKEND) || (THRUST_DEVICE_BACKEND==THRUST_DEVICE_BACKEND_CUDA)//CUDAの場合
cusparseDcsrmv(cusparseHandle, CUSPARSE_OPERATION_NON_TRANSPOSE, N, N,
one, descr, d_Aval, d_ArowPtr, d_AcolIdx, d_x, zero, d_Ax);
#endif
thrust::transform(dev_Ax.begin(), dev_Ax.end(), dev_b.begin(), dev_r.begin(), axpy<double>(‐1.0));
//残差ベクトルの内積を計算
rr = thrust::inner_product(dev_r.begin(), dev_r.end(), dev_r.begin(), 0.0);
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)
dev_p = dev_r;
}
else{ //p(k) = r(k)+c2
(k−1)p(k−1)
c2 = rr / (c1*dot);
thrust::transform(dev_r.begin(), dev_r.end(), dev_p.begin(), dev_p.begin(), xpay<double>(c2));
}
cg_thrust_omp.cu
97. GPUプログラム
2015/10/28GPGPU講習会97
//(p(k), Ap(k))を計算
//行列ベクトル積Apを実行し,結果とpの内積
#if (THRUST_DEVICE_BACKEND==THRUST_DEVICE_BACKEND_OMP)//OpenMPの場合
computeMxV(d_Ax, d_Aval, d_AcolIdx, d_ArowPtr, d_p, N);
#endif
#if !defined(THRUST_DEVICE_BACKEND) || (THRUST_DEVICE_BACKEND==THRUST_DEVICE_BACKEND_CUDA)//CUDAの場合
cusparseDcsrmv(cusparseHandle, CUSPARSE_OPERATION_NON_TRANSPOSE, N, N,
&one, descr, d_Aval, d_ArowPtr, d_AcolIdx, d_p, &zero, d_Ax);
#endif
dot = thrust::inner_product(dev_p.begin(), dev_p.end(), dev_Ax.begin(), 0.0);
c1 = rr / dot;
//x(k+1) = x(k)+c1
(k)p(k)
//r(k+1) = r(k)−c1
(k)Ap(k)
thrust::transform(dev_p .begin(), dev_p .end(), dev_x.begin(), dev_x.begin(), axpy<double>( c1));
thrust::transform(dev_Ax.begin(), dev_Ax.end(), dev_r.begin(), dev_r.begin(), axpy<double>(‐c1));
//残差ベクトルの内積を計算
rr = thrust::inner_product(dev_r.begin(), dev_r.end(), dev_r.begin(), 0.0);
#if !defined(THRUST_DEVICE_BACKEND) || (THRUST_DEVICE_BACKEND==THRUST_DEVICE_BACKEND_CUDA)
cudaThreadSynchronize();//GPUで実行する場合のみ有効化
#endif
k++;
}
//計算結果をGPUからCPUへコピー
x = dev_x; cg_thrust_omp.cu