• 検索結果がありません。

CUDA

カーネルには 1 スレッドが実行する処理を書く

 カーネル名と引数の間に <<<1,1>>> を付ける

2015/04/22 GPGPU実践プログラミング

92

Hello Thread ( Fermi 世代以降)

 GPU の各スレッドが画面表示

#include<stdio.h>

__global__ void hello(){

printf("Hello Thread¥n");

}

int main(void){

hello<<<1,1>>>();

cudaThreadSynchronize();

return 0;

}

画面表示(Fermi世代以降で可能)

コンパイル時にオプションが必要

‐arch=sm_20以降

・・・

カーネル実行

・・・

ホストとデバイスの同期をとる

CPUGPUは原則同期しないので,

同期しないとカーネルを実行した 直後にプログラムが終了

・・・

hellothread.cu

Hello Thread ( Fermi 世代以降)

 <<< >>> 内の数字で並列度が変わることの確認

2015/04/22 GPGPU実践プログラミング

94

#include<stdio.h>

__global__ void hello(){

printf("Hello Thread¥n");

}

int main(void){

hello<<<?,?>>>();

cudaThreadSynchronize();

return 0;

}

<<<>>>内の数字を変えると画面 表示される行数が変わる

<<<1,8>>>, <<<8,1>>>, 

<<<4,2>>>

・・・

hellothread.cu

CPU と GPU のやりとり

 GPU の想定される使い方

 ホスト

(CPU)

からデータを送り,デバイス

(GPU)

で計算し,

結果を受け取る

CPU

GPU

のデータのやり取りが必要

 GPU は原則データを返さない

PCI‐Ex

経由で描画情報を受け取り,画面に出力

 カーネルの返値が

void

の理由

CPU と GPU のやりとり

2015/04/22 GPGPU実践プログラミング

96

 CUDA 独自の命令と C 言語のポインタを利用

GPU

のメモリ上に計算に必要なサイズを確保

 確保したメモリのアドレスを

C

言語のポインタで格納

 ポインタの情報を基にデータを送受信

CPU と GPU のやり取り(単純な加算)

 int 型の変数 2 個を引数として受け取り, 2 個の和を返す

C

言語らしい書き方

#include<stdio.h>

int add(int a, int b){

return a + b;

}

int main(void){

int c;

c = add(6, 7);

printf("6 + 7 = %d¥n", c);

return 0;

}

引数で渡された変数の和を返す

・・・

関数呼び出し

・・・

add_naive.c

CPU と GPU のやり取り(単純な加算)

2015/04/22 GPGPU実践プログラミング

98

 関数の返値を void に変更し,メモリの動的確保を使用

#include<stdio.h>

#include<stdlib.h>

void add(int a, int b, int *c){

*c = a + b;

}

int main(void){

int c;

int *addr_c;

addr_c = (int *)malloc(sizeof(int));

add(6, 7, addr_c);

c = *addr_c;

printf("6 + 7 = %d¥n", c);

return 0;

}

引数で渡された変数の和を,cが指す アドレスに書き込み

・・・

引数にアドレスを追加

・・・

アドレスを基に結果を参照

・・・

add.c

CPU プログラム(メモリの動的確保)

 malloc

 指定したバイト数分のメモリを確保

stdlib.h

をインクルードする必要がある

 sizeof

 データ型

1

個のサイズ(バイト数)を求める

#include<stdlib.h>

int *a;

a = (int *)malloc( sizeof(int)*100 );

printf("%d, %d¥n", sizeof(float), sizeof(double));

実行すると4,8と表示される

CPU と GPU のやり取り(単純な加算)

2015/04/22 GPGPU実践プログラミング

100

 add.c の処理の一部を GPU の処理に置き換え

#include<stdio.h>

__global__ void add(int a, int b, int *c){

*c = a + b;

}

int main(void){

int c;

int *dev_c;

cudaMalloc( (void **)&dev_c, sizeof(int) );

add<<<1, 1>>>(6, 7, dev_c);

cudaMemcpy(&c, dev_c, sizeof(int), cudaMemcpyDeviceToHost);

printf("6 + 7 = %d¥n", c);

cudaFree(dev_c);

return 0;

}

__global__を追加

・・・

GPU上のメモリに確保される変数のアドレス

・・・

GPU上にint型変数 一個分のメモリを確保

・・・

↑GPUから結果をコピー メモリを解放

・・・

add.cu

CUDA でカーネルを作成するときの制限

 カーネルの引数

 値を渡すことができる

GPU

のメモリを指すアドレス

CPU

のメモリを指すアドレスも渡すことは可能

 そのアドレスを基にホスト側のメモリを参照することは不可能

 printf などの画面出力

Fermi

世代以降の

GPU

で,コンパイルオプションを付与

‐arch={sm_20|sm_21|sm_30|sm_32|sm_35|sm_50|sm_52}

 エミュレーションモード

新しい

CUDA

4.0

以降)では消滅

CPU プログラムの超簡単移植法

 とりあえず GPU で実行すればいいのなら・・・

 拡張子を .cu に変更

 GPU の都合を反映

 関数の返値を

void

にし,

__global__

を付ける

 関数名と引数の間に

<<<1,1>>>

を付ける

GPU

で使うメモリを

cudaMalloc

で確保

malloc

でメモリを確保していればそれを

cudaMalloc

に置き換え

GPU

からデータを受け取るために

cudaMemcpy

を追加

 最適化は追々考えればいい

2015/04/22 GPGPU実践プログラミング

102

カーネルの完成

Hello Thread ( Fermi 世代以降)

 <<< >>> 内の数字で並列度が変わる

 この情報を利用すれば並列処理が可能

#include<stdio.h>

__global__ void hello(){

printf("Hello Thread¥n");

}

int main(void){

hello<<<?,?>>>();

cudaThreadSynchronize();

return 0;

}

<<<>>>内の数字を変えると画面表示 される行数が変わる

<<<1,8>>>, <<<8,1>>>, 

<<<4,2>>>

・・・

hellothread.cu

GPU の並列化の階層

 GPU のハードウェアの構成に対応させて並列性を管理

 並列化の各階層における情報を利用

GPU

Streaming  Multiprocessor

CUDA  Core

ハードウェア構成

並列に実行する 処理

スレッドの集 まり

スレッド

並列化の階層

Grid

Block

Thread

CUDA

2015/04/22 GPGPU実践プログラミング

104

GPU の並列化の階層

 グリッド-ブロック-スレッドの 3 階層

 各階層の情報を参照できる変数

x,y,z

をメンバにもつ

dim3

型構造体

 グリッド( Grid )

gridDim

グリッド内にあるブロックの数

 ブロック( Block )

blockIdx

ブロックに割り当てられた番号

blockDim

ブロック内にあるスレッドの数

 スレッド( Thread )

threadIdx

スレッドに割り当てられた番号

Hello Threads ( Fermi 世代以降)

 <<< >>> 内の数字で表示される内容が変化

2015/04/22 GPGPU実践プログラミング

106

#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<<<?,?>>>();

cudaThreadSynchronize();

return 0;

}

<<<>>>内の数字を変えると画面表示 される内容が変わる

<<<>>>内の数字とどのパラメータが 対応しているかを確認

・・・

hellothreads.cu

GPU の構造とカーネルの書き方

 GPU はマルチスレッド

(メニースレッド)

で並列処理

 関数には 1 スレッドが実行する処理を書く

 関数を実行する際に並列処理の度合いを指定

 カーネルと引数の間に追加した <<<,>>> で並列処理の 度合を指定

<<<

グリッド内にあるブロックの数

,1

ブロックあたりのスレッド の数

>>>

プログラム実習

 以下のプログラムをコンパイルし,正しく実行できること を確認せよ

helloworld.c

helloworld.cu

hellothread.cu

hellothreads.cu

 hellothreads.cu については, <<<>>> 内の数字を変 更し,実行結果がどのように変わるか確認せよ

GPGPU実践プログラミング

108 2015/04/22

関連したドキュメント