More Related Content
Similar to 2015年度先端GPGPUシミュレーション工学特論 第5回 GPUのメモリ階層の詳細(様々なメモリの利用) (20)
2015年度先端GPGPUシミュレーション工学特論 第5回 GPUのメモリ階層の詳細(様々なメモリの利用)
- 3. メモリの種類
オフチップメモリ(GPUのチップ外部のメモリ)
低速アクセス,大容量
CPUから直接アクセス可能
ローカルメモリだけはアクセス不可
グローバルメモリ ローカルメモリ テクスチャメモリ コンスタントメモリ
容量 大 小 大 小
速度 低速 低速 高速* 高速*
GPUからの
読み書き
読み書き可
全てのスレッドが同
じメモリにアクセス
読み書き可
各スレッドが異なる
メモリにアクセス
読み込み可
全てのスレッドが同
じメモリにアクセス
読み込み可
全てのスレッドが同
じメモリにアクセス
CPUからの
アクセス
読み書き可 読み書き不可 書き込み可 書き込み可
2015/05/14先端GPGPUシミュレーション工学特論
*キャッシュが効く場合
3
- 4. コンスタントメモリ
GPU全体で同じメモリに
アクセス
コンスタントキャッシュを
利用することで,効率的
な読み込みが可能
キャッシュはオンチップ
GPU全体で64kB
2015/05/14先端GPGPUシミュレーション工学特論4
L2キャッシュ
コンスタントメモリ
テクスチャメモリ
GPU
Chip
レジ
スタ
レジ
スタ
レジ
スタ
レジ
スタ
CUDA
Core
CUDA
Core
CUDA
Core
CUDA
Core
L1キャッ
シュ
共有
メモリ
SM
レジ
スタ
レジ
スタ
レジ
スタ
レジ
スタ
CUDA
Core
CUDA
Core
CUDA
Core
CUDA
Core
L1キャッ
シュ
共有
メモリ
SM
グローバルメモリ
ホスト
メモリ
ローカル
メモリ
ローカル
メモリ
ローカル
メモリ
ローカル
メモリ
・・・
・・・
- 5. コンスタントメモリの利用
修飾子 __constant__ を付けて宣言
メモリは読込専用
CPUからは変更可能
専用のメモリ転送命令でコピー
cudaMemcpyToSymbol
CPU上のメモリをコンスタントメモリにコピーする
cudaMemcpyToSymbol(転送先変数名, 転送元アドレス,
バイト数, オフセット, 方向);
オフセット,方向は無くてもよい
オフセットを省略すると0が使われる
方向を省略するとcudaMemcpyHostToDeviceが使われる
2015/05/14先端GPGPUシミュレーション工学特論5
- 6. コンスタントメモリの利用
cudaError_t cudaMemcpyToSymbol(
const char * symbol,
const void * src,
size_t count,
size_t offset = 0,
enum cudaMemcpyKind kind = cudaMemcpyHostToDevice
)
Parameters
symbol ‐ Symbol destination on device
src ‐ Source memory address
count ‐ Size in bytes to copy
offset ‐ Offset from start of symbol in bytes
kind ‐ Type of transfer
2015/05/14先端GPGPUシミュレーション工学特論
http://docs.nvidia.com/cuda/cuda‐runtime‐api/
6
- 7. コンスタントメモリの宣言
サイズは静的に決定
__constant__ 型 変数名;
__constant__ 型 変数名[要素数];
配列としても宣言可能
要素数はコンパイル時に確定している必要がある
cudaMalloc()やcudaFree()は不要
グローバル変数として宣言し,複数のカーネルから
アクセス
読込専用のメモリならではの使い方
書込可能なメモリでは厳禁
2015/05/14先端GPGPUシミュレーション工学特論7
- 14. 0 16 32 48 64 80 96 11
2
コンスタントメモリへのアクセス
(Fermi世代)
Warp内のスレッド全てが同じコンスタントメモリアドレ
スにアクセス
1スレッドの読込をブロードキャストによって残りのスレッド
が共有
他のWarpも同じメモリアドレスにアクセス
データがキャッシュされているため,キャッシュから高速に
読み出し
2015/05/14先端GPGPUシミュレーション工学特論14
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
- 15. 0 16 32 48 64 80 96 11
2
コンスタントメモリへのアクセス
(Fermi世代)
Warp内のスレッド全てが異なるコンスタントメモリアド
レスにアクセス
読込が逐次化
読込処理の時間は,Warpがアクセスするコンスタントメモリ
アドレスの数に比例
最悪で処理に32倍の時間がかかる
おそらくグローバルメモリアクセスよりも遅くなる
2015/05/14先端GPGPUシミュレーション工学特論15
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
- 17. #define N (8*1024) //64kBに収める
#define Nbytes (N*sizeof(float))
#define NT (256)
#define NB (N/NT)
__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;
}
GPUプログラム(グローバルメモリ利用)
2015/05/14先端GPGPUシミュレーション工学特論
vectoradd.cu
17
- 20. #define N (8*1024) //64kBに収める
#define Nbytes (N*sizeof(float))
#define NT (256)
#define NB (N/NT)
__constant__ float a, b;
__global__ void init(float *c){
int i = blockIdx.x*blockDim.x
+ threadIdx.x;
c[i] = 0.0f;
}
__global__ void add(float *c){
int i = blockIdx.x*blockDim.x
+ threadIdx.x;
c[i] = a + b;
}
int main(void){
float *c;
float host_a,host_b;
host_a=1.0f;
host_b=2.0f;
cudaMalloc((void **)&c,Nbytes);
//host_a,host_bが配列ではないので
//アドレスを取り出すために&を付ける
cudaMemcpyToSymbol
(a,&host_a,sizeof(float));
cudaMemcpyToSymbol
(b,&host_b,sizeof(float));
init<<< NB, NT>>>(c);
add<<< NB, NT>>>(c);
return 0;
}
コンスタントメモリ(同一アドレス参照)
2015/05/14先端GPGPUシミュレーション工学特論
vectoradd_broadcast.cu
20
- 28. ページアウト
2015/05/14先端GPGPUシミュレーション工学特論
物理メモリ上にない仮想メモリを参照
補助記憶装置(ハードディスクなど)に退避されたデータ
ページフォルトという割り込みがかかり,OSに制御が移行
ページフォルトがおこると膨大な時間がかかる
OSは物理メモリ上のアドレスを追い出す(ページアウト)
必要なページを補助記憶装置から物理メモリ上に読み込む
スレッド
メモリ
プロセスA
OS
CPU
メモリ
ページ
ページ
テーブル
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
28
ハード
ディスク_____
_____
_____
_____
- 29. ページアウト
2015/05/14先端GPGPUシミュレーション工学特論
物理メモリ上にない仮想メモリを参照
補助記憶装置(ハードディスクなど)に退避されたデータ
ページフォルトという割り込みがかかり,OSに制御が移行
ページフォルトがおこると膨大な時間がかかる
OSは物理メモリ上のアドレスを追い出す(ページアウト)
必要なページを補助記憶装置から物理メモリ上に読み込む
スレッド
メモリ
プロセスA
OS
CPU
メモリ
ページ
ページ
テーブル
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
_____
29
ページアウト
ハード
ディスク
- 34. #include<stdio.h>
#include<stdlib.h>
#define N (1024*1024*64)
#define Nbytes (N*sizeof(float))
int main(){
float *data,*dev_data;
cudaEvent_t start,stop;
float elapsed_time_ms = 0.0f;
cudaEventCreate(&start);
cudaEventCreate(&stop);
data = (float *)malloc(Nbytes);
cudaMalloc( (void **)&dev_data,
Nbytes);
cudaEventRecord(start, 0);
cudaMemcpy(dev_data,data,Nbytes,
cudaMemcpyHostToDevice);
//GPU→CPUはコメントを外す
//cudaMemcpy(data,dev_data,Nbytes,
// cudaMemcpyDeviceToHost);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime
(&elapsed_time_ms, start,stop);
printf("%e ms¥n",elapsed_time_ms);
cudaEventDestroy(start);
cudaEventDestroy(stop);
free(data);
cudaFree(dev_data);
return 0;
}
ページング可能メモリを使ったコピー
2015/05/14先端GPGPUシミュレーション工学特論
copy_pagable.cu
34
- 35. #include<stdio.h>
#include<stdlib.h>
#define N (1024*1024*64)
#define Nbytes (N*sizeof(float))
int main(){
float *data,*dev_data;
cudaEvent_t start,stop;
float elapsed_time_ms = 0.0f;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaHostAlloc( (void **)&data,
Nbytes, cudaHostAllocDefault);
cudaMalloc( (void **)&dev_data,
Nbytes);
cudaEventRecord(start, 0);
cudaMemcpy(dev_data,data,Nbytes,
cudaMemcpyHostToDevice);
//GPU→CPUはコメントを外す
//cudaMemcpy(data,dev_data,Nbytes,
// cudaMemcpyDeviceToHost);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime
(&elapsed_time_ms, start,stop);
printf("%e ms¥n",elapsed_time_ms);
cudaEventDestroy(start);
cudaEventDestroy(stop);
cudaFreeHost(data);
cudaFree(dev_data);
return 0;
}
ページロックホストメモリを使ったコピー
2015/05/14先端GPGPUシミュレーション工学特論
copy_pagelocked.cu
35
- 38. #include<stdio.h>
#include<stdlib.h>
#define N (1024*1024*64)
#define Nbytes (N*sizeof(float))
#define ByteToGByte (1.0/(1024*1024*1024))
#define SecToMillisec (1.0/1000)
int main(){
float *data,*dev_data;
cudaEvent_t start,stop;
float elapsed_time_ms = 0.0f;
cudaEventCreate(&start);
cudaEventCreate(&stop);
data = (float *)malloc(Nbytes);
cudaMalloc( (void **)&dev_data,
Nbytes);
cudaEventRecord(start, 0);
cudaMemcpy(dev_data,data,Nbytes,
cudaMemcpyHostToDevice);
//cudaMemcpy(data,dev_data,Nbytes,
// cudaMemcpyDeviceToHost);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime
(&elapsed_time_ms, start,stop);
printf("%e ms¥n",elapsed_time_ms);
cudaEventDestroy(start);
cudaEventDestroy(stop);
free(data);
cudaFree(dev_data);
return 0;
}
ページング可能メモリを使ったコピー
2015/05/14先端GPGPUシミュレーション工学特論
copy_pagable.cu
38
- 39. #include<stdio.h>
#include<stdlib.h>
#define N (1024*1024*64)
#define Nbytes (N*sizeof(float))
#define ByteToGByte (1.0/(1024*1024*1024))
#define SecToMillisec (1.0/1000)
int main(){
float *data,*dev_data;
cudaEvent_t start,stop;
float elapsed_time_ms = 0.0f;
cudaEventCreate(&start);
cudaEventCreate(&stop);
data = (float *)malloc(Nbytes);
cudaMalloc( (void **)&dev_data,
Nbytes);
cudaHostRegister((void **)&data,
Nbytes,cudaHostRegisterDefault);
cudaEventRecord(start, 0);
cudaMemcpy(dev_data,data,Nbytes,
cudaMemcpyHostToDevice);
//cudaMemcpy(data,dev_data,Nbytes,
// cudaMemcpyDeviceToHost);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime
(&elapsed_time_ms, start,stop);
printf("%e ms¥n",elapsed_time_ms);
cudaEventDestroy(start);
cudaEventDestroy(stop);
free(data);
cudaFree(dev_data);
return 0;
}
HostRegisterによる割当
2015/05/14先端GPGPUシミュレーション工学特論
copy_hostregister.cu
39
- 40. データ転送の性能
2015/05/14先端GPGPUシミュレーション工学特論
入力配列サイズ N = 226
フラグによって性能が変わるが,どれも大して高速化しない
メモリ
実行時間 [ms]
CPU to GPU / GPU to CPU
転送速度 [GB/s]
CPU to GPU / GPU to CPU
ページング可能 99.1 / 117 2.5 / 2.1
cudaHostRegister
Default
76.4 / 85.2 3.3 / 2.9
cudaHostRegister
Portable
81.0 / 125 3.1 / 2.0
cudaHostRegister
Mapped
93.1 / 122 2.7 / 2.1
40
- 42. ゼロコピーメモリ利用の可否
2015/05/14先端GPGPUシミュレーション工学特論
cudaGetDevicePropertiesを利用
使い方はcudaSetDeviceの時と同じ
cudaDeviceProp型構造体のメンバcanMapHostMemory
を参照
42
#include<stdio.h>
int main(void){
int deviceCount = 0,dev;
cudaDeviceProp deviceProp;
//GPUの数を確認
cudaGetDeviceCount(&deviceCount);
for(dev=0;dev<deviceCount;dev++){
//情報を取得するGPUの選択
cudaSetDevice(dev);
cudaGetDeviceProperties
(&deviceProp, dev);
//ゼロコピーメモリが利用できるなら
//supportと表示
if(deviceProp.canMapHostMemory==1)
printf("supports¥n");
else
printf("doesn't support¥n");
}
return 0;
}
- 46. #define N (1024*1024)
#define Nbytes (N*sizeof(float))
#define NT (256)
#define NB (N/NT)
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 = blockIdx.x*blockDim.x
+ threadIdx.x;
c[i] = a[i] + b[i];
}
int main(){
float *a,*b,*c;
float *dev_a,*dev_b,*dev_c;
cudaEvent_t start,stop;
float elapsed_time_ms = 0.0f;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaHostAlloc((void **)&a, Nbytes,
cudaHostAllocDefault);
cudaHostAlloc((void **)&b, Nbytes,
cudaHostAllocDefault);
cudaHostAlloc((void **)&c, Nbytes,
cudaHostAllocDefault);
cudaMalloc((void **)&dev_a, Nbytes);
cudaMalloc((void **)&dev_b, Nbytes);
cudaMalloc((void **)&dev_c, Nbytes);
初期化と転送も含めたベクトル和
2015/05/14先端GPGPUシミュレーション工学特論
vectoradd_host.cu
46
- 47. cudaEventRecord(start, 0);
//ホストで初期化
init(a,b,c);
//転送
cudaMemcpy(dev_a, a, Nbytes,
cudaMemcpyHostToDevice);
cudaMemcpy(dev_b, b, Nbytes,
cudaMemcpyHostToDevice);
cudaMemcpy(dev_c, c, Nbytes,
cudaMemcpyHostToDevice);
//デバイスでベクトル和
add<<<NB,NT>>>(dev_a,dev_b,dev_c);
//転送
cudaMemcpy(c, dev_c, Nbytes,
cudaMemcpyDeviceToHost);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaDeviceSynchronize();
cudaEventElapsedTime
(&elapsed_time_ms, start,stop);
printf("%e ms¥n",elapsed_time_ms);
cudaFreeHost(a);
cudaFreeHost(b);
cudaFreeHost(c);
cudaFree(dev_a);
cudaFree(dev_b);
cudaFree(dev_c);
return 0;
}
初期化と転送も含めたベクトル和
2015/05/14先端GPGPUシミュレーション工学特論
vectoradd_host.cu
47
- 48. #define N (1024*1024)
#define Nbytes (N*sizeof(float))
#define NT (256)
#define NB (N/NT)
__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(){
float *a,*b,*c;
float *dev_a,*dev_b,*dev_c;
cudaEvent_t start,stop;
float elapsed_time_ms = 0.0f;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaSetDevice(0);
cudaSetDeviceFlags(cudaDeviceMapHost);
cudaHostAlloc( (void **)&a, Nbytes,
cudaHostAllocWriteCombined |
cudaHostAllocMapped);
cudaHostAlloc( (void **)&b, Nbytes,
cudaHostAllocWriteCombined |
cudaHostAllocMapped);
cudaHostAlloc( (void **)&c, Nbytes,
cudaHostAllocWriteCombined |
cudaHostAllocMapped);
ゼロコピーによるベクトル和
2015/05/14先端GPGPUシミュレーション工学特論
vectoradd_zerocopy.cu
48
- 49. //CPUとGPUのアドレスをマッピング
cudaHostGetDevicePointer(&dev_a,a,0);
cudaHostGetDevicePointer(&dev_b,b,0);
cudaHostGetDevicePointer(&dev_c,c,0);
cudaEventRecord(start, 0);
//転送無しでカーネルから読み書き
init<<<NB,NT>>>(dev_a,dev_b,dev_c);
add <<<NB,NT>>>(dev_a,dev_b,dev_c);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaDeviceSynchronize();
cudaEventElapsedTime
(&elapsed_time_ms, start,stop);
printf("%e ms¥n",elapsed_time_ms);
cudaFreeHost(a);
cudaFreeHost(b);
cudaFreeHost(c);
return 0;
}
ゼロコピーによるベクトル和
2015/05/14先端GPGPUシミュレーション工学特論
vectoradd_zerocopy.cu
49