第15回 GPU最適化ライブラリ
長岡技術科学大学 電気電子情報工学専攻 出川智啓
今回の内容
2015/07/22GPGPU実践プログラミング2
 CUDA付属のライブラリ
 cuBLAS*
 行列‐ベクトル積,行列‐行列積
 cuSPERSE*
 行列格納形式
 cuFFT
 余弦波のFFT
 cuRAND
 モンテカルロ法による円周率計算
 Thrust*
*cuBLAS, cuSPARSE, ThrustはGPGPU講習会でも取り扱っています
ライブラリ
2015/07/22GPGPU実践プログラミング3
 特定の処理を行う複数のプログラムを再利用可能な形で
まとめた集合体
 動画像処理やファイル圧縮,数値計算のライブラリが有名
 数値計算ライブラリは自作のプログラムよりも性能が高い
ため,処理速度の向上に貢献
数値計算ライブラリ
2015/07/22GPGPU実践プログラミング4
 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/07/22GPGPU実践プログラミング5
 NVIDIA製GPUに最適化
 cuBLAS
 BLASのGPU向け実装(密行列)
 cuSPARSE
 BLASのGPU向け実装(疎行列)
 cuFFT
 高速フーリエ変換
 cuRAND
 乱数生成
cuBLAS
2015/07/22GPGPU実践プログラミング6
 BLASのGPU向け実装
 Level 1
 ベクトルに対する計算
 Level 2
 行列-ベクトルの計算
 Level 3
 行列-行列の計算
 BLASはFORTRAN用ライブラリとして開発
 C言語から使用する場合には注意が必要
 配列の添字は1から
 メモリの配置がC言語と異なる
cuBLAS
2015/07/22GPGPU実践プログラミング7
 関数群はドキュメントを参照
 http://docs.nvidia.com/cuda/cublas/
 グリッド,ブロック,スレッドの設定は自動
 処理内容に対応した関数を呼び出す
 cublas<>axpy()
 <>は用いる型に応じて選択
 S 単精度実数
 D 倍精度実数
 C 単精度複素数型
 Z 倍精度複素数型
cuBLAS
2015/07/22GPGPU実践プログラミング8
 計算内容に対応した関数を呼ぶ事で計算を実行
 cublas<>axpy() ベクトル和
 cublas<>gemv() 行列‐ベクトル積
 cublas<>gemm() 行列‐行列積
 コンパイルの際はオプションとして –lcublas を追加
y[] = x[] + y[]
y[] = OP(A[][])x[] + y[]
C[][] = OP(A[][])OP(B[][]) + C[][]
#include<stdio.h>
#include<math.h>
#include<cublas_v2.h>
#define M 256
#define N 256
int main(){
int i,j;
float A[M*N], B[N], C[M];
float *dev_A, *dev_B, *dev_C;
float alpha, beta;
for(j=0;j<N;j++){
for(i=0;i<N;i++){
A[i+M*j] = ((float) i)/256.0;
}
}
for(i=0;i<M;i++){
C[i] = 0.0f;
}
for(i=0;i<N;i++){
B[i] = 1.0f;
}
cudaMalloc
((void**)&dev_A, N*N*sizeof(float));
cudaMalloc
((void**)&dev_B, N*sizeof(float));
cudaMalloc
((void**)&dev_C, M*sizeof(float));
alpha = 1.0f;
beta  = 0.0f;
cuBLASによる行列-ベクトル積
GPGPU実践プログラミング9 2015/07/22
cublas_gemv.cu
cublasHandle_t handle;
cublasCreate(&handle);
cublasSetMatrix
(M, N, sizeof(float), A, M, dev_A, M);
cublasSetVector
(N, sizeof(float), B, 1, dev_B, 1);
cublasSetVector
(M, sizeof(float), C, 1, dev_C, 1);
cublasSgemv
(handle, CUBLAS_OP_N, M, N, &alpha,
dev_A, M, dev_B, 1, &beta, dev_A, 1);
cublasGetVector
(M, sizeof(float), dev_C, 1, C, 1);
for(i=0;i<M;i++){
printf("C[%3d] = %f ¥n",j, C[j]);
}
cudaFree(dev_A);
cudaFree(dev_B);
cudaFree(dev_C);
cublasDestroy(handle);
return 0;
}
cuBLASによる行列-ベクトル積
GPGPU実践プログラミング10 2015/07/22
cublas_gemv.cu
cuBLASによる行列-ベクトル積
2015/07/22GPGPU実践プログラミング11
 ヘッダファイルのインクルード
 #include<cublas_v2.h>
 CUBLASのハンドルの作成
 cublasHandle_t handle;
 cublasCreate(&handle);
 ハンドル(行列やベクトルの情報を管理)の生成
 返値はcublasStatus_t型
 GPU上のメモリ確保
 cudaMalloc
cuBLASによる行列-ベクトル積
2015/07/22GPGPU実践プログラミング12
 データ転送
 cublasSetMatrix(M,N,sizeof(float),A,M,dev_A,M);
 cublasSetVector(N,sizeof(float),B,1,dev_B,1);
 cublasSetVector(M,sizeof(float),C,1,dev_C,1);
 関数内部でcudaMemcpyが呼ばれる アクセスの
ストライド
cuBLASによる行列-ベクトル積
2015/07/22GPGPU実践プログラミング13
 演算を行う関数の呼び出し
 cublasSgemv(handle, CUBLAS_OP_N, M, N, &alpha, 
dev_A, M, dev_B, 1, &beta, dev_C, 1);
 CUBLAS_OP_N 行列Bに対する操作
 cublasOperation_t型
 CUBLAS_OP_N OP(B)=B 処理しない
 CUBLAS_OP_T OP(B)=BT 転置
 CUBLAS_OP_C OP(B)=BH 共役転置
C[M] = OP(A[M][N])B[N] + C[M]
アクセスの
ストライド
cuBLASによる行列-ベクトル積
2015/07/22GPGPU実践プログラミング14
 データの読み戻し
 cublasGetVector(M,sizeof(float),dev_C,1,C,1);
 GPU上のメモリの解放
 cudaFree
 ハンドルの削除
 cublasDestroy(handle);
アクセスの
ストライド
cuBLASを使う際の注意点
2015/07/22GPGPU実践プログラミング15
 行列を表現するときのメモリ配置
 A[i][j]
 2次元配列でもメモリ上は1次元に配置
 i方向が先かj方向が先か
 C言語はj方向優先
 Fortranはi方向優先
cuBLASを使う際の注意点
2015/07/22GPGPU実践プログラミング16
 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)のメモリ上の配置 ←BLAS
 0/256,1/256,2/256・・・0/256,1/256,2/256・・・0/256,1/256,2/256・・・


















256/2256/2256/2
256/1256/1256/1
256/0256/0256/0
j
i
cuBLASによる行列-行列積
2015/07/22GPGPU実践プログラミング17
 cublasSgemm(cublasHandle_t handle,      
char trans_a, char trans_b,              
int m, int n, int k, 
float alpha,  const float *A, int lda,
const float *B, int ldb, 
float beta,      float *C, int ldc);
 trans_a,trans_b
 CUBLAS_OP_N, CUBLAS_OP_T, CUBLAS_OP_C
 lda, ldb, ldc A,B,Cを格納する配列の行数
C[m][n] = OP(A[m][k])OP(B[k][n]) + C[m][n]
cuBLASによる行列-行列積
2015/07/22GPGPU実践プログラミング18
 m,n,kとlda,ldb,ldcの数値による挙動の変化
 cublasSgemm(handle,CUBLAS_OP_N, CUBLAS_OP_N,
4, 4, 4, 0.0, A,4, B,4, 0.0, C,4);
 配列Aの行方向(第1次元)要素数4,行数4,行列の形状A[4][4]
 配列Bの行方向(第1次元)要素数4,行数4,行列の形状B[4][4]
 配列Cの行方向(第1次元)要素数4,行数4,行列の形状C[4][4]












































4,43,42,41,4
4,33,32,31,3
4,33,22,21,2
4,13,12,11,1
4,43,42,41,4
4,33,32,31,3
4,33,22,21,2
4,13,12,11,1
4,43,42,41,4
4,33,32,31,3
4,33,22,21,2
4,13,12,11,1
bbbb
bbbb
bbbb
bbbb
aaaa
aaaa
aaaa
aaaa
cccc
cccc
cccc
cccc
cuBLASによる行列-行列積
2015/07/22GPGPU実践プログラミング19
 m,n,kとlda,ldb,ldcの数値による挙動の変化
 cublasSgemm(handle,CUBLAS_OP_N, CUBLAS_OP_N,
3, 3, 3, 0.0, A,4, B,4, 0.0, C,4);
 配列Aの行方向(第1次元)要素数4,行数3,行列の形状A[3][3]
 配列Bの行方向(第1次元)要素数4,行数3,行列の形状B[3][3]
 配列Cの行方向(第1次元)要素数4,行数3,行列の形状C[3][3]












































4,43,42,41,4
4,33,32,31,3
4,33,22,21,2
4,13,12,11,1
4,43,42,41,4
4,33,32,31,3
4,33,22,21,2
4,13,12,11,1
3,32,31,3
3,22,21,2
3,12,11,1
0000
0
0
0
bbbb
bbbb
bbbb
bbbb
aaaa
aaaa
aaaa
aaaa
ccc
ccc
ccc
cuBLASによる行列-行列積
2015/07/22GPGPU実践プログラミング20
 m,n,kとlda,ldb,ldcの数値による挙動の変化
 cublasSgemm(handle,CUBLAS_OP_N, CUBLAS_OP_N,
2, 2, 3, 0.0, A,4, B,4, 0.0, C,4);
 配列Aの行方向(第1次元)要素数4,行数2,行列の形状A[2][3]
 配列Bの行方向(第1次元)要素数4,行数3,行列の形状B[3][2]
 配列Cの行方向(第1次元)要素数4,行数2,行列の形状C[2][2]












































4,43,42,41,4
4,33,32,31,3
4,33,22,21,2
4,13,12,11,1
4,43,42,41,4
4,33,32,31,3
4,33,22,21,2
4,13,12,11,1
2,21,2
2,11,1
0000
0000
00
00
bbbb
bbbb
bbbb
bbbb
aaaa
aaaa
aaaa
aaaa
cc
cc
#include<stdio.h>
#include<math.h>
#include<cublas_v2.h>
#define N 4096
int main(){
int i,j;
float *A, *B, *C;
float *dev_A, *dev_B, *dev_C;
float alpha, beta;
A=(float *)malloc(N*N*sizeof(float));
B=(float *)malloc(N*N*sizeof(float));
C=(float *)malloc(N*N*sizeof(float));
for(j=0;j<N;j++){
for(i=0;i<N;i++){
A[i+N*j] = (float)i;
B[i+N*j] = (float)j;
C[i+N*j] = 0.0f;
}
}
cudaMalloc((void**)&dev_A, 
N*N*sizeof(float));
cudaMalloc((void**)&dev_B, 
N*N*sizeof(float));
cudaMalloc((void**)&dev_C, 
N*N*sizeof(float));
alpha = 1.0f;
beta  = 0.0f;
cuBLASによる行列-行列積
GPGPU実践プログラミング21 2015/07/22
cublas_gemm.cu
cublasHandle_t handle;
cublasCreate(&handle);
cublasSetMatrix(N, N, sizeof(float),
A, N, dev_A, N);
cublasSetMatrix(N, N, sizeof(float),
B, N, dev_B, N);
cublasSetMatrix(N, N, sizeof(float),
C, N, dev_C, N);
cublasSgemm(handle,
CUBLAS_OP_N, CUBLAS_OP_N,
N, N, N,
&alpha, dev_A, N,
dev_B, N,
&beta,  dev_C, N);
cudaFree(dev_A);
cudaFree(dev_B);
cudaFree(dev_C);
cublasDestroy(handle);
return 0;
}
cuBLASによる行列-行列積
GPGPU実践プログラミング22 2015/07/22
cublas_gemm.cu
cuBLASによる行列-行列積の実効性能
2015/07/22GPGPU実践プログラミング23
2 2 2 2 2 2 2 2
行列サイズ
GFLOPS Tesla M2050の理論演算性能
cuSPARSE
2015/07/22GPGPU実践プログラミング24
 BLASのGPU向け実装(疎行列向け)
 Level 1, Level 2, Level 3が存在
 行列の格納形式の操作に関するHelper Function
 疎行列
 行列の要素の大半が0
 非ゼロ要素がまばらに存在
 非ゼロ要素のみを格納
 そもそも疎行列を作るのが大変
疎行列 密行列
ゼロ要素
非ゼロ要素
疎行列格納形式
2015/07/22GPGPU実践プログラミング25
 疎行列
 行列の要素の大半が0
 実際には8割程度の要素が0
 全てを保持するのは効率が悪い
 疎行列格納形式
 非ゼロ要素だけを保持してメモリを有効に利用
 COO形式(Coordinate Format)
 CSR形式(Compressed Sparse Row Format)
 CSC形式(Compressed Sparse Column Format)
 その他色々














9080
7605
4030
0201
疎行列格納形式(COO形式)
2015/07/22GPGPU実践プログラミング26
 COO形式(Coordinate Format)
 非ゼロ要素の行番号,列番号,値を保持














9080
7605
4030
0201
Row    = {0, 0, 1, 1, 2, 2, 2, 3, 3}
Column = {0, 2, 1, 3, 0, 2, 3, 1, 3}
Value  = {1, 2, 3, 4, 5, 6, 7, 8, 9}
0 1 2 3
Column
0
1
2
3
Row
疎行列格納形式(CSR形式)
2015/07/22GPGPU実践プログラミング27
 CSR形式(Compressed Sparse Row Format)
 COO形式のRowでは同じ数字が繰り返される
 Rowのデータも圧縮














9080
7605
4030
0201
Value  = {1, 2, 3, 4, 5, 6, 7, 8, 9}
Column = {0, 2, 1, 3, 0, 2, 3, 1, 3}
Row    = {0, 2, 4, 7, 9}
0 1 2 3
Column
0
1
2
3
Row
疎行列格納形式(CSR形式)
2015/07/22GPGPU実践プログラミング28














9080
7605
4030
0201
0 1 2 3
Column
0
1
2
3
Row
Value  = {1, 2, 3, 4, 5, 6, 7, 8, 9}
Column = {0, 2, 1, 3, 0, 2, 3, 1, 3}
Row    = {0, 2, 4, 7, 9}
Column配列の0,1は0行目のデータ
i行目のデータの個数は Row[i+1]‐Row[i]
forループが簡単に書ける for(j=row[i]; j<row[i+1];j++) 
疎行列格納形式(CSR形式)
2015/07/22GPGPU実践プログラミング29














9080
7605
4030
0201
0 1 2 3
Column
0
1
2
3
Row
Value  = {1, 2, 3, 4, 5, 6, 7, 8, 9}
Column = {0, 2, 1, 3, 0, 2, 3, 1, 3}
Row    = {0, 2, 4, 7, 9}
Column配列の2,3は1行目のデータ
i行目のデータの個数は Row[i+1]‐Row[i]
forループが簡単に書ける for(j=row[i]; j<row[i+1];j++) 
疎行列格納形式(CSR形式)
2015/07/22GPGPU実践プログラミング30














9080
7605
4030
0201
0 1 2 3
Column
0
1
2
3
Row
Value  = {1, 2, 3, 4, 5, 6, 7, 8, 9}
Column = {0, 2, 1, 3, 0, 2, 3, 1, 3}
Row    = {0, 2, 4, 7, 9}
Column配列の4,5,6は2行目のデータ
i行目のデータの個数は Row[i+1]‐Row[i]
forループが簡単に書ける for(j=row[i]; j<row[i+1];j++) 
疎行列格納形式(CSR形式)
2015/07/22GPGPU実践プログラミング31














9080
7605
4030
0201
0 1 2 3
Column
0
1
2
3
Row
Value  = {1, 2, 3, 4, 5, 6, 7, 8, 9}
Column = {0, 2, 1, 3, 0, 2, 3, 1, 3}
Row    = {0, 2, 4, 7, 9}
Column配列の7,8は3行目のデータ
i行目のデータの個数は Row[i+1]‐Row[i]
forループが簡単に書ける for(j=row[i]; j<row[i+1];j++) 
cuSPARSEの行列-行列積
2015/07/22GPGPU実践プログラミング32
 N×Nの正方行列同士のかけ算
 cuBLASの場合
 [C] = OP([A])*OP([B]) + [C]
 cublasSgemm(handle, OPERATION, OPERATION,
N, N, N, , [A], N, [B], N, , [C], N);
 cuSPERSEの場合
 [C] = OP([A])*[B] + [C]
 cusparseScsrmm(handle, OPERATION, N, N, N, NNZ,
, descr[A], csrVal[A], csrRow[A], csrCol[A], 
[B], N, , [C], N);
行列サイズ 行数 行数 行数
行列転置の有無
非ゼロ
要素数行列サイズ
配列Aの詳細情報 配列Aの値 配列Aの行情報 配列Aの列情報
cuFFT
2015/07/22GPGPU実践プログラミング33
 FFTWと似たインターフェースを持つFFTライブラリ
 以下の各FFTをサポート
 1次元
 2次元
 3次元
 複素数 → 複素数
 実数 → 複素数
 複素数 → 実数
cuFFTの利用手順
2015/07/22GPGPU実践プログラミング34
1.planの作成
 FFTの次元と種類を指定
 1次元,2次元,3次元のいずれか
 複素数→複素数,実数→複素数,複素数→実数のいずれか
2.FFTの実行
 planに沿ってFFTを実行
 順方向FFTか逆方向FFTかを実行時に指定
3.planの破棄
planの作成
2015/07/22GPGPU実践プログラミング35
 FFTの次元
 1次元
cufftPlan1d(cufftHandle *plan, int nx, cufftType type,int batch);
 2次元
cufftPlan2d(cufftHandle *plan, int nx, int ny, cufftType type);
 3次元
cufftPlan3d(cufftHandle *plan, int nx, int ny, int nz, cufftType
type);
 FFTの種類(type)
 複素数 → 複素数 CUFFT_C2C
 実数 → 複素数 CUFFT_R2C
 複素数 → 実数 CUFFT_C2R
cufftHandle型
ポインタ変数
FFTを行うデータの
x,y,z方向サイズ
1次元FFTを
何回行うか
FFTの実行
2015/07/22GPGPU実践プログラミング36
 複素数 → 複素数
cufftExecC2C(cufftHandle plan, cufftComplex *idata, 
cufftComplex *odata, int direction);
 実数 → 複素数
cufftExecR2C(cufftHandle plan, cufftReal *idata, 
cufftComplex *odata, int direction);
 複素数 → 実数
cufftExecC2R(cufftHandle plan, cufftComplex *idata, 
cufftReal *odata, int direction);
 FFTの方向
 int direction
 CUFFT_FORWARD 順FFT
 CUFTT_INVERSE 逆FFT
FFTの実行,planの破棄
2015/07/22GPGPU実践プログラミング37
 倍精度利用する場合
 cufftDoubleComplex, cufftDoubleRealを指定
cufftExecZ2D(cufftHandle plan, cufftDoubleComplex *idata, 
cufftDoubleReal *odata, int direction);
 planの破棄
 cufftDestroy(cufftHandle plan);
#include<stdio.h>
#include<math.h>
#include<cufft.h>
#define N 256
#define MAX_WAVE 5
int main(){
int i,wavenum;
float real[N],imag[N];
cufftHandle plan;
cufftComplex host[N];
cufftComplex *dev;
cufftComplex re_host[N];
for(i=0;i<N;i++){
real[i] = 0.0;
imag[i] = 0.0;
}
for(wavenum=1;wavenum<=MAX_WAVE;
wavenum++){
for(i=0;i<N;i++){
real[i] += 
cos(2.0*M_PI*(float)i/N
*(float)wavenum);
}
}
for(i=0;i<N;i++){
printf("%f,%f¥n",(float)i,real[i]);
}
for(i=0;i<N;i++){
host[i] = make_cuComplex(
real[i],imag[i]);
}
cuFFTによる余弦波のFFT
2015/07/22GPGPU実践プログラミング38
cufft.cu
cudaMalloc((void**)&dev,    
N*sizeof(cufftComplex));
cudaMemcpy(dev, host, 
N*sizeof(cufftComplex), 
cudaMemcpyHostToDevice);
cufftPlan1d(&plan, N, CUFFT_C2C,1);
cufftExecC2C(plan, dev, dev, 
CUFFT_FORWARD);
cudaMemcpy(re_host, dev, 
N*sizeof(cufftComplex), 
cudaMemcpyDeviceToHost);
for(i=0;i<N/2;i++){
printf("dev[%3d] = %f %f ¥n",
i,cuCrealf(re_host[i]), 
cuCimagf(re_host[i]));
}
cufftDestroy(plan);
cudaFree(dev);
return 0;
}
cuFFTによる余弦波のFFT
2015/07/22GPGPU実践プログラミング39
cufft.cu
cuFFTによる余弦波のFFT
2015/07/22GPGPU実践プログラミング40
 ヘッダファイルのインクルード
 #include<cufft.h>
 二つの実数(実部と虚部)から複素数を作成
 host[i] = make_cuComplex(real[i],imag[i]);
 GPU上のメモリ確保
 cudaMalloc
 1次元FFTのプランを作成
 cufftPlan1d(&plan, N, CUFFT_C2C, 1);
N個のデータを使って
1次元FFTを1回行う
cuFFTによる余弦波のFFT
2015/07/22GPGPU実践プログラミング41
 複素数から複素数へのFFTを実行
 cufftExecC2C(plan, dev, dev,CUFFT_FORWARD);
 結果の表示(実部と虚部の取り出し)
 cuCrealf(re_host[i]) 複素数の実部を取り出す
 cuCimagf(re_host[i]) 複素数の虚部を取り出す
 プランの破棄とGPU上のメモリの解放
 cufftDestroy(plan);
 cudaFree
 コンパイルの際はオプションとして –lcufft を追加
cuFFTによる余弦波のFFT
2015/07/22GPGPU実践プログラミング42
 入力信号
 波数1~5の余弦波の重ね合わせ
-2
-1
0
1
2
3
4
5
0 32 64 96 128 160 192 224 256
格子点番号
cuFFTによる余弦波のFFT
2015/07/22GPGPU実践プログラミング43
 スペクトラムの実部
0
20
40
60
80
100
120
140
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
入力した波の波数
波数
スペクトラム
cuRAND
 乱数(列)を生成するライブラリ
 既知の数列から次の数列の値が予測できない
 一様乱数,正規乱数など
 真の乱数列の生成は困難
 乱数列と同じような性質を持った数列(疑似乱数)を生成
 CPUから呼ばれ,GPUで乱数を生成
 GPUから呼ばれ,GPUで乱数を生成
GPGPU実践プログラミング44 2015/07/22
cuRAND
 CPUから関数を呼ぶ場合
 ヘッダーファイルのインクルード
 #include<curand.h>
 Generatorを宣言
 curandGenerator_t 変数名
GPGPU実践プログラミング45 2015/07/22
cuRAND
 Generatorの生成
curandStatus_t curandCreateGenerator
(curandGenerator_t* generator, curandRngType_t rng_type )
 curandRngType_t(乱数の発生方法)の指定
 CURAND_RNG_PSEUDO_XORWOW XORWOW
 CURAND_RNG_PSEUDO_MRG32K3A MRG32k3a
 CURAND_RNG_PSEUDO_MTGP32 メルセンヌ・ツイスタ
 CURAND_RNG_QUASI_SOBOL32 Sobol32
 CURAND_RNG_QUASI_SCRAMBLED_SOBOL64 Scrambled Sobol64
GPGPU実践プログラミング46 2015/07/22
cuRAND
2015/07/22GPGPU実践プログラミング47
 乱数の種の設定
curandStatus_t curandSetPseudoRandomGeneratorSeed
(curandGenerator_t generator, unsigned long long seed )
 乱数の生成
 一様乱数を生成
curandStatus_t curandGenerateUniform
(curandGenerator_t generator, float* outputPtr, 
size_t num )
 コンパイルの際はオプションとして –lcurand を追加
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <curand.h>
#define N 1024
int main(){
int i;
float *value, *value_d;
curandGenerator_t gen;
cudaMalloc((void**)&value_d,    
N*sizeof(float));
curandCreateGenerator (&gen,     
CURAND_RNG_PSEUDO_DEFAULT);
curandGenerateUniform
(gen, value_d, N);
value
= (float *)malloc(N*sizeof(float));
cudaMemcpy(value, value_d,
N*sizeof(float),             
cudaMemcpyDeviceToHost);
for(i=0;i<N;i++){
printf("%d¥n",value[i]);
}
curandDestoryGenerator(gen);
cudaFree(value_d);
free(value);
return 0;
}
cuRANDサンプル
GPGPU実践プログラミング48 2015/07/22
curand.cu
cuRAND
 GPUから乱数を生成するデバイス関数を呼ぶ場合
 ヘッダファイルのインクルード
 #include<curand_kernel.h>
 乱数生成の初期化
 __device__ void curand_init(unsigned long long seed, unsigned 
long long subsequence, unsigned long long offset, 
curandState_t* state )
 第1引数は乱数のシード(全スレッドで同じ値)
 第2引数はシーケンス番号(スレッド番号など,全スレッドで異なる値)
 乱数の生成
 __device__ float curand_uniform(curandState_t* state )
GPGPU実践プログラミング49 2015/07/22
#include<stdio.h>
#include<stdlib.h>
#include<curand_kernel.h>
#define N (1024)
__global__ void random(float *value){
int i
= blockIdx.x*blockDim.x + threadIdx.x;
unsigned int seed;
curandState stat;
seed = 1;
curand_init(seed, i, 0, &stat);
value[i] = curand_uniform(&stat);
}
int main(){
int i;
float *value, *value_d;
cudaMalloc((void**)&value_d, 
N*sizeof(float));
random<<<1,1>>>(value_d);
value = (float *)malloc
(N*sizeof(float));
cudaMemcpy(value, value_d,
N*sizeof(float),
cudaMemcpyDeviceToHost);
for(i=0;i<N;i++){
printf("%f ¥n",value[i]);
}
cudaFree(value_d);
free(value);
return 0;
}
cuRANDサンプル
GPGPU実践プログラミング50 2015/07/22
curand_kernel.cu
Thrust
2015/07/22GPGPU実践プログラミング51
 CUDA用の並列アルゴリズムライブラリ
 C++の標準テンプレートライブラリ(STL)とよく似た高水
準なインタフェースを保有
 CUDA4.0からCUDA本体に吸収
 https://developer.nvidia.com/thrust
#include<iostream>
#include<thrust/host_vector.h>
#include<thrust/device_vector.h>
#include<thrust/copy.h>
#include<thrust/sort.h>
int main(void) {
thrust::host_vector < float > host_vec(3);
thrust::device_vector < float > device_vec(3);
host_vec[0] = 1.1; host_vec[1] = 3.3;    host_vec[2] = 2.2;
thrust::copy(host_vec.begin(), host_vec.end(), device_vec.begin());
thrust::sort(device_vec.begin(), device_vec.end());
thrust::copy(device_vec.begin(), device_vec.end(), host_vec.begin());
std::cout << host_vec[0] << std::endl;
std::cout << host_vec[1] << std::endl;
std::cout << host_vec[2] << std::endl;
return 0;
}
Thrustサンプル
GPGPU実践プログラミング52 2015/07/22
thrust_sort.cu
Thrustサンプル
2015/07/22GPGPU実践プログラミング53
 ヘッダのインクルード
 #include<thrust/host_vector.h>
 #include<thrust/device_vector.h>
 #include<thrust/copy.h>
 メモリ確保
 host_vector CPUにメモリを確保
 device_vector GPUにメモリを確保
 CPU側の配列の初期化とGPUへのコピー
 thrust::copy(host_vec.begin(), host_vec.end(), 
device_vec.begin());
Thrustサンプル
2015/07/22GPGPU実践プログラミング54
 データのソート
 thrust::sort(device_vec.begin(), 
device_vec.end());
 GPUから読み戻し
 thrust::copy(device_vec.begin(), 
device_vec.end(), host_vec.begin());
モンテカルロ法
2015/07/22GPGPU実践プログラミング55
 乱数を用いて数値シミュレーションや数値計算を行う手
法の総称
 得られる解が必ずしも正しいとは保証されていない
 円周率の計算
 正方形の中に乱数を使って点を打ち,円の
内側に入っている点を数えることで円の面
積を求める
 点の数が無限に多く,点の座標が重複しな
いなら面積(や円周率)が正しく求められる
0 1
1
0
モンテカルロ法による円周率計算
2015/07/22GPGPU実践プログラミング56
 円の面積 r2
 正方形の面積 4r2
 面積比 a=r2/4r2
 円周率 =4a
 円と正方形の面積比を点の数で近似
2r
r
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<curand.h>
#define N 1024
int main(){
int i,inside;
float *x, *y, *x_d, *y_d;
float pi;
curandGenerator_t gen;
cudaMalloc((void**)&x_d, N*sizeof(float));
cudaMalloc((void**)&y_d, N*sizeof(float));
curandCreateGenerator(&gen, 
CURAND_RNG_PSEUDO_DEFAULT);
curandGenerateUniform(gen, x_d, N); //x座標
curandGenerateUniform(gen, y_d, N); //y座標
x = (float *)malloc(N*sizeof(float));
y = (float *)malloc(N*sizeof(float));
cudaMemcpy(x, x_d, N*sizeof(float),
cudaMemcpyDeviceToHost);
cudaMemcpy(y, y_d, N*sizeof(float),
cudaMemcpyDeviceToHost);
inside=0;
for(i=0;i<N;i++){
if( (x[i]*x[i] + y[i]*y[i]) <=1.0f )
inside++;
}
pi = 4.0f*(float)inside/N;
printf("%f¥n",pi);
curandDestroyGenerator(gen);
cudaFree(x_d);
cudaFree(y_d);
free(x);
free(y);
return 0;
}
モンテカルロ法による円周率計算
2015/07/22GPGPU実践プログラミング57
montecarlo.cu
モンテカルロ法によって得られた円周率
2015/07/22GPGPU実践プログラミング58
点の数 円周率 相対誤差
21 2.000000 0.36338
22 2.000000 0.36338
23 2.500000 0.204225
24 3.000000 0.04507
25 3.125000 0.005282
26 3.062500 0.025176
27 3.031250 0.035123
28 2.984375 0.050044
29 3.164062 0.007152
210 3.171875 0.009639
点の数 円周率 相対誤差
211 3.160156 0.005909
212 3.171875 0.009639
213 3.133301 0.002639
214 3.157227 0.004976
215 3.14917 0.002412
216 3.152649 0.003519
217 3.152374 0.003432
218 3.147675 0.001936
219 3.142609 0.000323
220 3.142635 0.000332
モンテカルロ法によって得られた円周率
2015/07/22GPGPU実践プログラミング59
サンプル数 N
相対誤差
O(N−0.5)
その他GPU向けライブラリ
2015/07/22GPGPU実践プログラミング60
 MAGMA
 http://icl.cs.utk.edu/magma/
 NVIDIA GPU向け(CUDA)の線形代数ライブラリ
 CPUも同時に使うハイブリッド型のライブラリであるため,
GPU単体より高速
 BLAS, LAPACKに準ずるような形で関数形が定められて
いる
 cuBLASに取り込まれている関数もある
 ソースコードが配布されており,無料で入手できる
その他GPU向けライブラリ
2015/07/22GPGPU実践プログラミング61
 cuBLAS‐XT
 https://developer.nvidia.com/cublasxt
 cuBLASライブラリのマルチGPU向け実装
 CUDA 6.0, 6.5から利用可能

2015年度GPGPU実践プログラミング 第15回 GPU最適化ライブラリ