長岡技術科学大学 電気電子情報工学専攻 出川智啓
第11回 GPUでの並列
プログラミング(ベクトル和)
今回の内容
GPGPU実践基礎工学
 CUDAによるプログラミング(前回の復習)
 ベクトル和
 CUDAによるベクトル和の実装
 1スレッド実行
 並列(マルチスレッド)実行
 GPU‐CPU間のデータ転送
2 2015/11/18
CUDA
 Compute Unified Device Architecture
 NVIDIA社製GPU向け開発環境
 Windows, Linux, Mac OS Xで動作
 2007年頃発表
 C/C++言語+独自のGPU向け拡張
 専用コンパイラ(nvcc)とランタイムライブラリ
 いくつかの数値計算ライブラリ(線形代数計算,FFTなど)
GPGPU実践基礎工学3 2015/11/18
CUDAによるプログラミング
 CPUをホスト(Host),GPUをデバイス(Device)と表現
 CPU(ホスト)
 処理の流れやGPUを利用するための手続きを記述
 プログラムの書き方は従来のC言語と同じ
 利用するGPUの決定,GPUへのデータ転送,GPUで実行する関
数の呼び出し等
GPGPU実践基礎工学4 2015/11/18
CUDAによるプログラミング
 CPUをホスト(Host),GPUをデバイス(Device)と表現
 GPU(デバイス)
 処理する内容を関数として記述
 引数は利用可能,返値は利用不可(常にvoid)
 関数はkernelと呼ばれる
 関数呼び出しはlaunch, invokeなどと呼ばれる
GPGPU実践基礎工学5 2015/11/18
CUDAプログラムの実行
 実行時の流れ(CPU視点)
1.利用するGPUの初期化やデータの転送などを実行
2.GPUで実行する関数を呼び出し
3.GPUから結果を取得
初期化の指示
初期化
カーネルの実行指示
カーネルを実行
結果の取得
実行結果をコピー
time
CPUとGPUは非同期
CPUは別の処理を実行可能
GPGPU実践基礎工学
必要なデータのコピー
メモリに書込
6 2015/11/18
ベクトル和C=A+Bの計算
 配列要素に対して計算順序の依存性がなく,最も単純に
並列化可能
 配列a, b, cの配列要素番号iが同じ
・・・
・・・
・・・c[i]
a[i]
b[i]
+ + + + + +
GPGPU実践基礎工学7 2015/11/18
#define N (1024*1024)
void init(float *a, float *b,
float *c){
int i;
for(i=0; i<N; i++){
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
}
void add(float *a, float *b,
float *c){
int i;
for(i=0; i<N; i++)
c[i] = a[i] + b[i];
}
int main(void){
float a[N],b[N],c[N];
init(a,b,c);
add(a,b,c);
return 0;
}
CPUプログラム(メモリの静的確保)
GPGPU実践基礎工学
vectoradd.c
8 2015/11/18
#include<stdlib.h>
#define N (1024*1024)
#define Nbytes (N*sizeof(float))
void init(float *a, float *b, 
float *c){
int i;
for(i=0; i<N; i++){
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
}
void add(float *a, float *b, 
float *c){
int i;
for(i=0; i<N; i++)
c[i] = a[i] + b[i];
}
int main(void){
float *a,*b,*c;
a = (float *)malloc(Nbytes);
b = (float *)malloc(Nbytes);
c = (float *)malloc(Nbytes);
init(a,b,c);
add(a,b,c);
free(a);
free(b);
free(c);
return 0;
}
CPUプログラム(メモリの動的確保)
GPGPU実践基礎工学9
vectoradd_malloc.c
2015/11/18
CPUプログラム(メモリの動的確保)
GPGPU実践基礎工学
 malloc/free
 指定したバイト数分のメモリを確保
 stdlib.hをインクルードする必要がある
 sizeof
 データ型1個のサイズ(バイト数)を求める
printf("%d, %d¥n", sizeof(float), sizeof(double));
実行すると4,8と表示される
10
#include<stdlib.h>
int *a;
a = (int *)malloc( sizeof(int)*100 );
free(a);
2015/11/18
CPUプログラム(メモリの動的確保)
2015/11/18GPGPU実践基礎工学11
 defineディレクティブ
 コンパイルの際にプリプロセッサが文字列を別の文字列に置
き換える
#include<stdlib.h>
#define N 1024
int *a;
a = (int *)malloc( sizeof(int)*N );
#include<stdlib.h>
#define N 1024
int *a;
a = (int *)malloc( sizeof(int)*1024 );
プリプロセス
GPUへの移植
GPGPU実践基礎工学12
 ソースファイルの拡張子を.cから.cuに変更
 GPUの都合を反映(前回の講義資料参照)
 関数の前に修飾子__global__をつけた
 GPUで実行する関数という目印にするため
 関数の返値をvoidにした
 GPUのハードウェア構造に適したプログラムを作るため
 関数呼出の際に関数名と引数の間に<<<1,1>>>を付けた
 GPUのハードウェア構造に適したプログラムを作るため
 関数には1スレッドが処理する内容を書き,実行時の並列度を指定
 カーネルを呼び出した後に同期を取る関数を呼んだ
 GPUで実行した結果を正しく得るため
2015/11/18
GPUへの移植(メモリの取り扱い)
GPGPU実践基礎工学13
 ベクトルa,b,cを確保し,a,bの値を読んでcに書き込む
 ホスト(CPU)にはプロセッサとメモリが存在
 デバイス(GPU)にもプロセッサとメモリが存在
 デバイスからホストのメモリは(原則)直接読み書きできない
float *a,*b,*c;
a=(float *)malloc(...);
b=(float *)malloc(...);
c=(float *)malloc(...);
for(i=0;i<N;i++)
printf("%f+%f=%f¥n",
a(i),b(i),c(i));
a
b
c
__global__ void add(){
for(i=0;i<N;i++)
c(i) = a(i)+b(i)
}
2015/11/18
GPUへの移植(メモリの取り扱い)
GPGPU実践基礎工学14
 ベクトルa,b,cを確保し,a,bの値を読んでcに書き込む
 ホスト(CPU)にはプロセッサとメモリが存在
 デバイス(GPU)にもプロセッサとメモリが存在
 デバイスからホストのメモリは(原則)直接読み書きできない
 GPUのメモリを確保し,カーネルから利用
for(i=0;i<N;i++)
printf("%f+%f=%f¥n",
a(i),b(i),c(i));
a
b
c
__global__ void add(){
for(i=0;i<N;i++)
c(i) = a(i)+b(i)
}
2015/11/18
GPUのメモリの動的確保
2015/11/18GPGPU実践基礎工学15
 cudaMalloc
 GPUのメモリ上に指定したバイト数分のメモリを確保
 cudaMalloc( (void **)&ポインタ変数名, バイト数);
 cudaFree
 cudaMallocで確保したメモリを解放
 cudaFree(ポインタ変数名);
int *a;
//mallocを使う場合
//a = (int *)malloc( sizeof(int)*100 );
cudaMalloc( (void **)&a, sizeof(int)*100);
cudaFree(a);
#include<stdlib.h>
#define N (1024*1024)
#define Nbytes (N*sizeof(float))
void init(float *a, float *b, 
float *c){
int i;
for(i=0; i<N; i++){
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
}
void add(float *a, float *b, 
float *c){
int i;
for(i=0; i<N; i++)
c[i] = a[i] + b[i];
}
int main(void){
float *a,*b,*c;
a = (float *)malloc(Nbytes);
b = (float *)malloc(Nbytes);
c = (float *)malloc(Nbytes);
init(a,b,c);
add(a,b,c);
free(a);
free(b);
free(c);
return 0;
}
CPUプログラム(メモリの動的確保)
GPGPU実践基礎工学16
vectoradd_malloc.c
2015/11/18
#define N (1024*1024)
#define Nbytes (N*sizeof(float))
__global__ void init(float *a,float *b,
float *c){
int i;
for(i=0; i<N; i++){
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
}
__global__ void add(float *a,float *b,
float *c){
int i;
for(i=0; i<N; i++)
c[i] = a[i] + b[i];
}
int main(void){
float *a,*b,*c;
cudaMalloc((void **)&a, Nbytes);
cudaMalloc((void **)&b, Nbytes);
cudaMalloc((void **)&c, Nbytes);
init<<< 1, 1>>>(a,b,c);
add<<< 1, 1>>>(a,b,c);
cudaDeviceSynchronize();
cudaFree(a);
cudaFree(b);
cudaFree(c);
return 0;
}
GPUプログラム(1スレッド実行版)
GPGPU実践基礎工学
vectoradd_1thread.cu
17 2015/11/18
#define N (1024*1024)
#define Nbytes (N*sizeof(float))
__global__ void init(float *a,float *b,
float *c){
int i;
for(i=0; i<N; i++){
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
}
__global__ void add(float *a,float *b,
float *c){
int i;
for(i=0; i<N; i++)
c[i] = a[i] + b[i];
}
int main(void){
float *a,*b,*c;
cudaMalloc((void **)&a, Nbytes);
cudaMalloc((void **)&b, Nbytes);
cudaMalloc((void **)&c, Nbytes);
init<<< 1, 1>>>(a,b,c);
add<<< 1, 1>>>(a,b,c);
cudaDeviceSynchronize();
cudaFree(a);
cudaFree(b);
cudaFree(c);
return 0;
}
GPUプログラム(1スレッド実行版)
GPGPU実践基礎工学18 2015/11/18
vectoradd_1thread.cu
GPUカーネルの
目印
並列実行の度
合を指定
GPUカーネルの
目印
GPUのメモリに
確保
確保したメモリ
を解放
GPUプログラムへの変更
2015/11/18GPGPU実践基礎工学19
 変更点
 mallocをcudaMallocに,freeをcudaFreeに変えた
変更の理由
2015/11/18GPGPU実践基礎工学20
 変更点
 mallocをcudaMallocに,freeをcudaFreeに変えた
 変更によって実現されること
 CPUとGPUのハードウェア制約を回避できる
 変更が必要な理由
 GPUはPCI‐Exバスを経由してホストと接続されている
 GPUはホストと別に独立したメモリを持っている
 GPUはホストメモリ(CPU側のメモリ)に直接アクセスできないため,
GPUが持っているメモリを利用
実行結果
GPGPU実践基礎工学21
 実行時間の確認
 CUDAの実行環境に組み込まれたプロファイラを利用
 環境変数CUDA_PROFILEを1に設定する事で実行時間を測定
 使い方
1. 環境変数の設定 $ export CUDA_PROFILE=1
2. プログラムの実行 $ ./a.out
3. プロファイル結果(標準はcuda_profile_0.log)の確認
2015/11/18
実行結果
GPGPU実践基礎工学22
 プロファイルの一連の流れ
 method カーネルや関数(API)の名称
 gputime GPU上で処理に要した時間(s単位)
 cputime CPUで処理(=カーネル起動)に要した時間
実際の実行時間=cputime+gputime
 occupancy GPUがどれだけ効率よく利用されているか
‐bash‐3.2$ nvcc vectoradd_1thread.cu プログラムをコンパイル(実行ファイルa.outが作られる)
‐bash‐3.2$ export CUDA_PROFILE=1 環境変数CUDA_PROFILEを1にしてプロファイラを有効化
‐bash‐3.2$ ./a.out プログラムを実行(cuda_profile_0.logというファイルが作られる)
‐bash‐3.2$ cat cuda_profile_0.log cuda_profile_0.logの内容を画面に表示
# CUDA_PROFILE_LOG_VERSION 2.0
# CUDA_DEVICE 0 Tesla M2050
# TIMESTAMPFACTOR fffff5f8a8002b58
method,gputime,cputime,occupancy
method=[ _Z4initPfS_S_ ] gputime=[ 201039.484 ] cputime=[ 17.000 ] occupancy=[ 0.021 ]
method=[ _Z3addPfS_S_ ] gputime=[ 205958.375 ] cputime=[ 6.000 ] occupancy=[ 0.021 ]
2015/11/18
実行結果
GPGPU実践基礎工学23
 計算が正しく行われているかの確認
 配列c[]の値が全て3.0になっていれば正しい
 printfを使って表示
 大量に画面表示されて煩わしい
 GPUのカーネルには実行時間の制限がある
 配列c[]の値の平均を計算
 平均が3.0になっていれば正しく実行できているだろうと推察
 配列c[]の平均をGPUで計算するのは難しい
 CPUで配列c[]の平均を計算
 CPUはGPUのメモリを直接読み書きできない
 専用の命令を使ってGPUからCPUへコピー
2015/11/18
CPUとGPUのやりとり
GPGPU実践基礎工学
 cudaMemcpy
 GPU上のメモリを指定した方向にコピーする
 cudaMemcpy(転送先アドレス, 転送元アドレス, バイト数, 
転送の方向);
 転送の方向
 cudaMemcpyHostToDevice CPUからGPUへコピー
 cudaMemcpyDevideToDevice GPUからGPUへコピー
 cudaMemcpyDevideToHost GPUからCPUへコピー
 cudaMemcpy(  ,  ,バイト数, cudaMemcpy???To???);
24 2015/11/18
#define N (1024*1024)
#define Nbytes (N*sizeof(float))
__global__
void init(float *a,float *b,float *c){
for(int i=0; i<N; i++){
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
}
__global__
void add(float *a,float *b, float *c){
for(int i=0; i<N; i++)
c[i] = a[i] + b[i];
}
int main(void){
float *a,*b,*c;
float *host_c, sum=0.0f;
cudaMalloc((void **)&a, Nbytes);
cudaMalloc((void **)&b, Nbytes);
cudaMalloc((void **)&c, Nbytes);
host_c = (float *)malloc(Nbytes);
init<<< 1, 1>>>(a,b,c);
add<<< 1, 1>>>(a,b,c);
cudaMemcpy(host_c,c,Nbytes,
cudaMemcpyDeviceToHost);
for(int i=0;i<N;i++)
sum += host_c[i];
printf("average:%f¥n",sum/N);
cudaFree(a);
cudaFree(b);
cudaFree(c);
free(host_c);
return 0;
}
GPUプログラム(cudaMemcpy利用)
GPGPU実践基礎工学
vectoradd_copy.cu
25 2015/11/18
CUDAでカーネルを作成するときの制限
GPGPU実践基礎工学26
 利用するデータの場所の把握
 データがホストメモリにあるかデバイスメモリにあるか
 CPUにあるデータを基にGPUで計算を行う場合
 GPUで計算した結果をCPUで確認する場合
 転送を明示的に指定
 カーネルの引数
 値を渡すことができる
 GPUのメモリを指すアドレス
 CPUのメモリを指すアドレスも渡すことは可能
 そのアドレスを基にホスト側のメモリを参照することは不可能
2015/11/18
#define N (1024*1024)
#define Nbytes (N*sizeof(float))
__global__ void add(float *a, float x, 
float *b, float y,  float *c){
for(int i=0; i<N; i++)
c[i] = x*a[i] + y*b[i];
}
int main(void){
float *a,*b,*c;
float *host_a, *host_b, *host_c;
float x=1.0f, y=2.0f;
cudaMalloc((void **)&a, Nbytes);
cudaMalloc((void **)&b, Nbytes);
cudaMalloc((void **)&c, Nbytes);
host_a = (float *)malloc(Nbytes);
host_b = (float *)malloc(Nbytes);
host_c = (float *)malloc(Nbytes);
for(int i=0; i<N; i++){
host_a[i] = 1.0;
host_b[i] = 2.0;
}
cudaMemcpy(a,host_a,Nbytes,
cudaMemcpyHostToDevice);
cudaMemcpy(b,host_b,Nbytes,
cudaMemcpyHostToDevice);
add<<< 1, 1>>>(a, x, b, y, c);
cudaMemcpy(host_c,c,Nbytes,
cudaMemcpyDeviceToHost);
cudaFree(a);
cudaFree(b);
cudaFree(c);
free(host_a);
free(host_b);
free(host_c);
return 0;
}
GPUプログラム
GPGPU実践基礎工学
vectoradd_copy_twoway.cu
27 2015/11/18
CUDAでカーネルを作成するときの制限
GPGPU実践基礎工学28
 x,yはCPU側のメモリに存在
 値渡し
 CPU→GPUへ値がコピーされる
 host_a,host_b,host_c
もCPU側のメモリに存在
 ポインタ渡し(変数のアドレス
を渡す)
 GPUは渡されたメモリアドレス
が分かってもCPUのメモリに
アクセスできない
__global__ void add(float *a, float x, 
float *b, float y,  float *c){
for(int i=0; i<N; i++)
c[i] = x*a[i] + y*b[i];
}
int main(void){
float *a,*b,*c;
float *host_a,host_b,host_c;
float x=1.0, y=2.0;
:
add<<< 1, 1>>>(a, x, b, y, c);
//add<<< 1, 1>>>(host_a, x, 
host_b, y,host_c);
:
}
2015/11/18
GPUで並列に処理を行うには
 GPUは低性能の演算器(CUDAコア)を多数搭載
 マルチスレッドで並列処理することで高い性能を達成
 どのように並列処理を記述するか?
 関数呼出の際に関数名と引数の間に<<<1,1>>>を付けた
 GPUには数百から数千のCUDAコアが搭載されており,それらが協調
して並列処理を実行
 1スレッドが実行する処理を書くことでカーネルの作成を簡略化
 並列処理の度合いはカーネル呼出の際に指定する
GPGPU実践基礎工学29 2015/11/18
GPUで並列に処理を行うには
 配列サイズN=8
 総スレッド数を8として並列実行する状況を想定
GPGPU実践基礎工学30
c[i]
a[i]
b[i]
+ + + + + + + +
i=   0    1   2    3   4    5   6    7
2015/11/18
GPUによる並列化の方針
 forループをスレッドの数だけ分割
 各スレッドが少量のデータを処理
i=0
c[i] = a[i] + b[i];スレッド0
c[i]
a[i]
b[i]
+ + + +
スレッド
0
スレッド
2
スレッド
1
スレッド
3
i=1
c[i] = a[i] + b[i];スレッド1
i=2
c[i] = a[i] + b[i];スレッド2
i=3
c[i] = a[i] + b[i];スレッド3
スレッドの番号
に応じて決定
1スレッドが実
行する処理
GPGPU実践基礎工学31 2015/11/18
カーネルの書き換え
 1スレッドが実行する処理になるよう変更
 1スレッドがある添字 i の要素を担当
#define N (8)
#define Nbytes (N*sizeof(float))
__global__ void init(float *a, float *b, float *c){
int i=...;
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
__global__ void add(float *a, float *b, float *c){
int i=...;
c[i] = a[i] + b[i];
}
GPGPU実践基礎工学32
1スレッドがあるiの担当となり,変数
の初期化と足し算の計算を実行
2015/11/18
カーネルの書き換え
 1スレッドが実行する処理になるよう変更
 どのようにiを決定するか?
#define N (8)
#define Nbytes (N*sizeof(float))
__global__ void init(float *a, float *b, float *c){
int i=0;
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
__global__ void add(float *a, float *b, float *c){
int i=0;
c[i] = a[i] + b[i];
}
全てのスレッドがi=0の
要素を計算してしまう
GPGPU実践基礎工学33 2015/11/18
GPUの並列化の階層
 GPUのハードウェアの構成に対応させて並列性を管理
 並列化の各階層における情報を利用
GPU
Streaming 
Multiprocessor
CUDA 
Core
ハードウェア構成
並列に実行する
処理
スレッドの集
まり
スレッド
並列化の階層
Grid
Block
Thread
CUDA
GPGPU実践基礎工学34 2015/11/18
GPUの並列化の階層
 グリッド-ブロック-スレッドの3階層
 各階層の情報を参照できる変数
 x,y,zをメンバにもつdim3型構造体
 グリッド(Grid)
 gridDim グリッド内にあるブロックの数
 ブロック(Block)
 blockIdx ブロックに割り当てられた番号
 blockDim ブロック内にあるスレッドの数
 スレッド(Thread)
 threadIdx スレッドに割り当てられた番号
GPGPU実践基礎工学35 2015/11/18
Hello Threads(Fermi世代以降)
 <<<,>>>内の数字で表示される内容が変化
2015/11/18GPGPU実践基礎工学36
#include<stdio.h>
__global__ void hello(){
printf("gridDim.x=%d, blockIdx.x=%d,
blockDim.x=%d, threadIdx.x=%d¥n",
gridDim.x, blockIdx.x, blockDim.x, threadIdx.x);
}
int main(void){
hello<<<2,4>>>();
cudaDeviceSynchronize();
return 0;
}
<<<,>>>内の数字を変えると画面表
示される内容が変わる
<<<,>>>内の数字とどのパラメータが
対応しているかを確認
・・・
各スレッドが異なるiを参照するには
 CUDAでは並列化に階層がある
 全体の領域(グリッド)をブロックに分割
 ブロックの中をスレッドに分割
<<<2, 4>>>
ブロックの数 1ブロックあたりの
スレッドの数
ブロックの数×1ブロックあたりのスレッドの数=総スレッド数
2    × 4          = 8 
GPGPU実践基礎工学37
[block] [thread/block] [thread]
2015/11/18
各スレッドが異なるiを参照するには
 N=8, <<<2, 4>>>で実行
c[i]
a[i]
b[i]
+ + + + + + + +
gridDim.x=2
blockIdx.x=0 blockIdx.x=1
blockDim.x=4blockDim.x=4threadIdx.x=
0    1   2    3 0    1   2    3
threadIdx.x=
GPGPU実践基礎工学38
i=   0    1   2    3   4    5   6    7
2015/11/18
各スレッドが異なるiを参照するには
 N=8, <<<2, 4>>>で実行
c[i]
a[i]
b[i]
+ + + + + + + +
gridDim.x=2
blockIdx.x=0 blockIdx.x=1
blockDim.x=4blockDim.x=4threadIdx.x=
0    1   2    3 0    1   2    3
threadIdx.x=
i=   0    1   2    3   4    5   6    7
=  blockIdx.x*blockDim.x + threadIdx.x
GPGPU実践基礎工学39 2015/11/18
各スレッドが異なるiを参照するには
 N=8, <<<1, 8>>>で実行
c[i]
a[i]
b[i]
+ + + + + + + +
gridDim.x=1
blockIdx.x=0
blockDim.x=8threadIdx.x=
0    1   2    3 4 5 6    7
i=   0    1   2    3   4    5   6    7
=  blockIdx.x*blockDim.x + threadIdx.x
2015/11/18GPGPU実践基礎工学40
各スレッドが異なるiを参照するには
 N=8, <<<4, 2>>>で実行
c[i]
a[i]
b[i]
+ + + + + + + +
gridDim.x=4
blockIdx.x=0
blockDim.x=2threadIdx.x=
0    1   0 1 0    1   0    1
2015/11/18GPGPU実践基礎工学41
blockIdx.x=1
blockDim.x=2
blockIdx.x=2
blockDim.x=2
blockIdx.x=3
blockDim.x=2
=  blockIdx.x*blockDim.x + threadIdx.x
i=   0    1   2    3   4    5   6    7
カーネルの書き換え
 1スレッドが実行する処理になるよう変更
 1スレッドがある添字 i の要素を担当
#define N (8)
#define Nbytes (N*sizeof(float))
__global__ void init(float *a, float *b, float *c){
int i=blockIdx.x*blockDim.x + threadIdx.x;
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
__global__ void add(float *a, float *b, float *c){
int i=blockIdx.x*blockDim.x + threadIdx.x;
c[i] = a[i] + b[i];
}
GPGPU実践基礎工学42 2015/11/18
#define N (1024*1024)
#define Nbytes (N*sizeof(float))
__global__ void init(float *a,
float *b, float *c){
int i = blockIdx.x*blockDim.x
+ threadIdx.x;
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
__global__ void add(float *a,
float *b, float *c){
int i = blockIdx.x*blockDim.x
+ threadIdx.x;
c[i] = a[i] + b[i];
}
int main(void){
float *a,*b,*c;
cudaMalloc((void **)&a, Nbytes);
cudaMalloc((void **)&b, Nbytes);
cudaMalloc((void **)&c, Nbytes);
init<<< N/256, 256>>>(a,b,c);
add<<< N/256, 256>>>(a,b,c);
cudaFree(a);
cudaFree(b);
cudaFree(c);
return 0;
}
GPUで並列実行するプログラム
GPGPU実践基礎工学
vectoradd.cu
43 2015/11/18
処理時間の比較
 配列の要素数 N=220
 1ブロックあたりのスレッド数 256
 GPUはマルチスレッドで処理しないと遅い
 GPUを使えばどのような問題でも速くなるわけではない
 並列に処理できるようプログラムを作成する必要がある
implementation
Processing time [ms]
init add
CPU (1 Thread) 4.15 4.55
GPU (1 Thread) 201 206
GPU (256 Threads) 0.108 0.112
GPGPU実践基礎工学44 2015/11/18
各階層の値の設定
 設定の条件
 GPUの世代によって設定できる上限値が変化
 確認の方法
 pgaccelinfo
 deviceQuery
 GPU Computing SDKに含まれているサンプル
 CUDA Programming Guide
 https://docs.nvidia.com/cuda/cuda‐c‐programming‐
guide/#compute‐capabilities
 階層の値によって実行時の性能が変化
 GPUの一番基本的なチューニング
2015/11/18GPGPU実践基礎工学45
pgaccelinfoの実行結果
Device Number:                 0
Device Name:                   Tesla M2050
Device Revision Number:        2.0
Global Memory Size:            2817982464
Number of Multiprocessors:     14
Number of Cores:               448
Concurrent Copy and Execution: Yes
Total Constant Memory:         65536
Total Shared Memory per Block: 49152
Registers per Block:           32768
Warp Size:                     32
Maximum Threads per Block:     1024
Maximum Block Dimensions:      1024, 1024, 64
Maximum Grid Dimensions:       65535 x 65535 x 65535
Maximum Memory Pitch:          2147483647B
Texture Alignment:             512B
Clock Rate:                    1147 MHz
Initialization time:           4222411 microseconds
Current free memory:           2746736640
Upload time (4MB):             2175 microseconds ( 829 ms pinned)
Download time:                 2062 microseconds ( 774 ms pinned)
Upload bandwidth:              1928 MB/sec (5059 MB/sec pinned)
Download bandwidth:            2034 MB/sec (5418 MB/sec pinned)
2015/11/18GPGPU実践基礎工学46
pgaccelinfo実行結果
 Revision Number:       2.0
 Global Memory Size:            2817982464
 Warp Size:                     32
 Maximum Threads per Block:     1024
 Maximum Block Dimensions:      1024, 1024, 64
 Maximum Grid Dimensions:       65535 x 65535 x 65535
GPUの世代
(どのような機能を有しているか)
実
行
時
の
パ
ラ
メ
ー
タ
選
択
の
際
に
重
要
 各方向の最大値
 1ブロックあたりのスレッド数は最大1024
 (1024, 1, 1), (1, 1024, 1)
 (32, 32, 1), (4, 4, 64)など
2015/11/18GPGPU実践基礎工学47
#define N (1024*1024)
#define NT (256) //この数字を変更
#define NB (N/NT)
#define Nbytes (N*sizeof(float))
__global__ void init(float *a,
float *b, float *c){
int i = blockIdx.x*blockDim.x
+ threadIdx.x;
a[i] = 1.0;
b[i] = 2.0;
c[i] = 0.0;
}
__global__ void add(float *a,
float *b, float *c){
int i = blockIdx.x*blockDim.x
+ threadIdx.x;
c[i] = a[i] + b[i];
}
int main(void){
float *a,*b,*c;
cudaMalloc((void **)&a, Nbytes);
cudaMalloc((void **)&b, Nbytes);
cudaMalloc((void **)&c, Nbytes);
init<<< NB, NT>>>(a,b,c);
add<<< NB, NT>>>(a,b,c);
cudaFree(a);
cudaFree(b);
cudaFree(c);
return 0;
}
並列度の変更によるチューニング
GPGPU実践基礎工学48
 #defineでNB,NTを定義,変更して実行
vectoradd_param.cu
2015/11/18

2015年度GPGPU実践基礎工学 第11回 GPUでの並列 プログラミング(ベクトル和)