GPUのメモリ階層の詳細 (様々なメモリの利用) 長岡技術科学大学 電気電子情報工学専攻 出川智啓 今回の内容 コンスタントメモリ ページロックホストメモリ ゼロコピーホストメモリ ベクトル和による性能比較 311 先端GPGPUシミュレーション工学特論 2015/05/14 メモリの種類 オフチップメモリ(GPUのチップ外部のメモリ) 低速アクセス,大容量 CPUから直接アクセス可能 ローカルメモリだけはアクセス不可 グローバルメモリ ローカルメモリ テクスチャメモリ コンスタントメモリ 容量 大 小 大 小 速度 低速 低速 高速* 高速* 読み書き可 読み書き可 読み込み可 読み込み可 GPUからの 読み書き CPUからの アクセス 全てのスレッドが同じ アドレスにアクセス可 能** 読み書き可 各スレッドが異なるアド レスにアクセス 全てのスレッドが同じ アドレスにアクセス可 能** 全てのスレッドが同じ アドレスにアクセス 読み書き不可 書き込み可 書き込み可 *キャッシュが効く場合 312 先端GPGPUシミュレーション工学特論 **スレッドごとに異なるアドレス にアクセスすることも可能 2015/05/14 コンスタントメモリ GPU全体で同じメモリに アクセス コンスタントキャッシュを 利用することで,効率的 な読み込みが可能 GPU全体で64kB GPU Chip SM SM 共有 メモリ L1キャッ シュ レジ スタ レジ スタ レジ スタ レジ スタ CUDA CUDA CUDA CUDA Core Core Core Core ローカル メモリ ホスト メモリ L1キャッ シュ レジ スタ レジ スタ 共有 メモリ レジ スタ レジ スタ CUDA CUDA CUDA CUDA Core Core Core Core L2キャッシュ コンスタントメモリ テクスチャメモリ ローカル グローバルメモリ メモリ 313 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリの利用 修飾子 __constant__ を付けて宣言 メモリは読込専用 CPUからは変更可能 専用のメモリ転送命令でコピー cudaMemcpyToSymbol CPU上のメモリをコンスタントメモリにコピーする cudaMemcpyToSymbol(転送先変数名, 転送元アドレス, バイト数, オフセット, 方向); オフセット,方向は無くてもよい 方向はHostToDeviceのみ 314 GPUからメモリを変更できないためCPUから読む必要がない 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリの利用 cudaError_t cudaMemcpyToSymbol( const char * const void * size_t size_t enum cudaMemcpyKind symbol, src, count, offset = 0, kind=cudaMemcpyHostToDevice ) Parameters: symbol src count offset kind ‐ ‐ ‐ ‐ ‐ Symbol destination on device Source memory address Size in bytes to copy Offset from start of symbol in bytes Type of transfer http://docs.nvidia.com/cuda/cuda‐runtime‐api/ 315 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリの宣言 サイズは静的に決定 __constant__ 型 変数名; __constant__ 型 変数名[要素数]; 配列としても宣言可能 要素数はコンパイル時に確定している必要がある cudaMalloc()やcudaFree()は不要 グローバル変数として宣言し,複数のカーネルから アクセスすることが多い 316 読込専用なので許される 書込可能なメモリでは厳禁 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス コンスタントメモリへ高速にアクセスできる要因 1. ブロードキャストによるデータの分配 16スレッド(Half Warp)単位でアクセスし,1回の読込を 他の15スレッドにブロードキャストできる グローバルメモリからの読込よりもメモリ転送量を節約 2. コンスタントメモリキャッシュ 317 コンスタントメモリはキャッシュされる 他のHalf Warpがキャッシュされたデータへアクセスして もメモリ転送量は増加しない 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス メモリ転送量の増加を抑制 メモリ読込による実行速度低下を回避 コンスタントメモリへのアクセスの制約 318 1回の読込をブロードキャストできる=Half Warpは読込 命令を同時に処理できない Half Warpの各スレッド全てが異なるコンスタントメモリを 参照すると,読込が逐次的になる 読込命令の処理に16倍の時間を要する 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス Warpが同じメモリアドレスにアクセス 1スレッドの読込をブロードキャストによっ て残りのスレッドが共有 T0 A0 T1 T2 T3 T4 T5 他のWarpも同じメモリアドレスにアク セス データがキャッシュされているため,コン スタントメモリから直接読むより高速 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 319 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス Half Warpが異なるメモリアドレス にアクセス 320 読込が逐次化される 処理に16倍の時間がかかる おそらくグローバルメモリへのアクセ スよりも遅くなる 先端GPGPUシミュレーション工学特論 T0 A0 T1 A1 T2 A2 T3 A3 T4 A4 T5 A5 T6 A6 T7 A7 T8 A8 T9 A9 T10 A10 T11 A11 T12 A12 T13 A13 T14 A14 T15 A15 2015/05/14 コンスタントメモリ利用の例 ベクトル和 ベクトルAとBの値が全て同じ コンスタントメモリにデータを一つ置き,全スレッドが参照 ・・・ a[i] + + + + a + + b[i] ・・・ b c[i] ・・・ c[i] 321 先端GPGPUシミュレーション工学特論 ・・・ 2015/05/14 GPUプログラム(グローバルメモリ利用) #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]; } 322 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); return 0; } vectoradd.cu 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリ(単純な置き換え) int i; #define N (8*1024) //64kBに収める #define Nbytes (N*sizeof(float)) #define NT (256) #define NB (N/NT) __constant__ float a[N],b[N]; __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[i] + b[i]; } int main(void){ float *c; float *host_a,*host_b; 323 host_a=(float *)malloc(Nbytes); host_b=(float *)malloc(Nbytes); cudaMalloc((void **)&c,Nbytes); for(i=0;i<N;i++){ host_a[i] = 1.0f; host_b[i] = 2.0f; } cudaMemcpyToSymbol (a,host_a,Nbytes); cudaMemcpyToSymbol (b,host_b,Nbytes); init<<< NB, NT>>>(c); add<<< NB, NT>>>(c); return 0; } vectoradd_constant.cu 先端GPGPUシミュレーション工学特論 2015/05/14 実行時間 入力配列サイズ N = 213 スレッド数 NT = 256 324 カーネル 実行時間 [ms] vectoradd 7.65×10‐3 vectoradd_constant 1.01×10‐2 各スレッドがコンスタントメモリの異なるアドレスにアクセス すると,グローバルメモリよりも遅くなる 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリ(同一アドレス参照) #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; 325 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; } vectoradd_broadcast.cu 先端GPGPUシミュレーション工学特論 2015/05/14 実行時間 入力配列サイズ N = 213 スレッド数 NT = 256 326 カーネル 実行時間 [ms] vectoradd 7.65×10‐3 vectoradd_constant 1.01×10‐2 vectoradd_broadcast 7.55×10‐3 各スレッドがコンスタントメモリの同一アドレスにアクセスす ると高速化 先端GPGPUシミュレーション工学特論 2015/05/14 実行時間 入力配列サイズ N = 220 スレッド数 NT = 256 327 カーネル 実行時間 [ms] vectoradd 0.116 vectoradd_broadcast 0.042 データ転送量が多くなるとコンスタントメモリが著しく高速化 定数を参照する場合,#defineで定義した方が高速に実 行できるので使いどころが難しい 先端GPGPUシミュレーション工学特論 2015/05/14 ホストメモリへの効率的なアクセス ホスト(CPU)とデバイス (GPU)のやりとり GPUでの処理を高速化し 続けると,ホスト‐デバイス 間のメモリコピーがボトル ネック化 ホスト-デバイス間 の転送の高速化 GPU Chip SM SM 共有 メモリ L1キャッ シュ レジ スタ レジ スタ レジ スタ レジ スタ CUDA CUDA CUDA CUDA Core Core Core Core ローカル メモリ ホスト メモリ L1キャッ シュ レジ スタ レジ スタ 共有 メモリ レジ スタ レジ スタ CUDA CUDA CUDA CUDA Core Core Core Core L2キャッシュ コンスタントメモリ テクスチャメモリ ローカル グローバルメモリ メモリ 328 先端GPGPUシミュレーション工学特論 2015/05/14 ページロック(ピン)メモリ OSによって管理されるメモリのうち,ページアウトしな いことが保証されているメモリ OSが配列の物理アドレスを記憶 GPUが物理アドレスを知れば,ダイレクトメモリアクセ ス(DMA)を使ってホストとデータを交換できる 329 先端GPGPUシミュレーション工学特論 2015/05/14 OSによる記憶管理 記憶管理 メモリのアドレスをプロセス固有の仮想アドレスに変換して 割り付け 仮想記憶方式を採用 330 メモリの物理的な大きさに依存せず,また不連続なメモリ領 域を連続に見せかける方式 1個の記憶装置を占有しているようにプログラム可能 先端GPGPUシミュレーション工学特論 2015/05/14 仮想記憶方式 仮想的な記憶装置上のアドレス メモリ上のアドレス 仮想アドレス,論理アドレス 仮想アドレスと実アドレス の変換はOSが管理 実アドレス,物理アドレス 多重仮想記憶 331 システム内に複数の仮想アドレス空間を形成し,プロセス ごとに割り当て 現在の計算機の主流 CPUとGPUも仮想アドレス空間が異なる 先端GPGPUシミュレーション工学特論 2015/05/14 ページング方式 仮想メモリ空間と物理メモリ空間を一定サイズの ページと呼ばれる単位に分割して管理 ページテーブル 仮想アドレスから実アドレスの対応表 仮想アドレスから実アドレスへの変換はページ単位で実行 CPU _____ _____ _____ _____ _____ _____ _____ _____ _____ メモリ _____ _____ _____ 332 ページ テーブル _____ _____ _____ プロセスA ページ OS _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ 先端GPGPUシミュレーション工学特論 スレッド メモリ 2015/05/14 ページアウト(印刷用) 物理メモリ上にない仮想メモリを参照 補助記憶装置(ハードディスクなど)に退避されたデータ ページフォルトという割り込みがかかり,OSに制御が移行 ページフォルトがおこると膨大な時間がかかる OSは物理メモリ上のアドレスを追い出す(ページアウト) 必要なページを補助記憶装置から物理メモリ上に読み込む ハード ディスク 356 CPU _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ メモリ _____ _____ _____ _____ _____ ページ テーブル _____ _____ _____ _____ プロセスA ページ OS _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ 先端GPGPUシミュレーション工学特論 スレッド メモリ ページアウト 2015/05/14 ページロック(ピン)メモリ ページアウトされない事がOSによって保証 ピンで刺された紙のように固定されている pinned memory そのメモリの物理アドレスにアクセスしても安全 全てのメモリをページロックにすると,他のプログラ ムが起動できなくなる可能性がある CPU _____ _____ _____ _____ _____ _____ _____ _____ _____ メモリ _____ _____ _____ 334 ページ テーブル _____ _____ _____ プロセスA ページ OS _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ 先端GPGPUシミュレーション工学特論 スレッド メモリ 2015/05/14 CUDAによるメモリ転送 ページング可能なメモリを使ってもDMA転送を行う 1. 2. ページング可能なシステムバッファからページロックされ たステージングバッファへコピー ステージングバッファからGPUへDMAでコピー ページロックメモリを使う事で約2倍の転送速度が 期待される 335 先端GPGPUシミュレーション工学特論 2015/05/14 ページロックホストメモリの確保と解放 cudaHostAlloc() ページロックされたホストメモリを確保 cudaHostAlloc((void **)&ホスト変数名, サイズ, cudaHostAllocDefault); cudaHostAllocで確保されたメモリはmallocで確保され たメモリと同様に利用可能 cudaHostAllocDefault以外にもフラグがあり,ページロックメモリ の利用方法に応じて選択可能 cudaFreeHost() 336 cudaHostAllocで確保されたホストメモリを解放 cudaFreeHost(ホスト変数のアドレス); 先端GPGPUシミュレーション工学特論 2015/05/14 ページング可能メモリを使ったコピー #include<stdio.h> #include<stdlib.h> #define N (1024*1024*64) #define Nbytes (N*sizeof(float)) //GPU→CPUはコメントを外す //cudaMemcpy(data,dev_data,Nbytes, // cudaMemcpyDeviceToHost); cudaEventRecord(stop, 0); cudaEventSynchronize(stop); cudaEventElapsedTime (&elapsed_time_ms, start,stop); int main(){ float *data,*dev_data; cudaEvent_t start,stop; float elapsed_time_ms = 0.0f; cudaEventCreate(&start); cudaEventCreate(&stop); printf("%e ms¥n", elapsed_time_ms); data = (float *)malloc(Nbytes); cudaEventDestroy(start); cudaEventDestroy(stop); free(data); cudaFree(dev_data); return 0; cudaMalloc( (void **)&dev_data, Nbytes); cudaEventRecord(start, 0); cudaMemcpy(dev_data,data,Nbytes, cudaMemcpyHostToDevice); 337 } copy_pagable.cu 先端GPGPUシミュレーション工学特論 2015/05/14 ページロックホストメモリを使ったコピー #include<stdio.h> #include<stdlib.h> #define N (1024*1024*64) #define Nbytes (N*sizeof(float)) //GPU→CPUはコメントを外す //cudaMemcpy(data,dev_data,Nbytes, // cudaMemcpyDeviceToHost); cudaEventRecord(stop, 0); cudaEventSynchronize(stop); cudaEventElapsedTime (&elapsed_time_ms, start,stop); int main(){ float *data,*dev_data; cudaEvent_t start,stop; float elapsed_time_ms = 0.0f; cudaEventCreate(&start); cudaEventCreate(&stop); printf("%e ms¥n", elapsed_time_ms); cudaHostAlloc( (void **)&data, Nbytes, cudaHostAllocDefault); cudaEventDestroy(start); cudaEventDestroy(stop); cudaFreeHost(data); cudaFree(dev_data); return 0; cudaMalloc( (void **)&dev_data, Nbytes); cudaEventRecord(start, 0); cudaMemcpy(dev_data,data,Nbytes, cudaMemcpyHostToDevice); 338 } copy_pagelocked.cu 先端GPGPUシミュレーション工学特論 2015/05/14 データ転送の性能 入力配列サイズ N = 226 メモリ 実行時間 [ms] 転送速度 [GB/s] CPU to GPU / GPU to CPU CPU to GPU / GPU to CPU ページング可能 99.1 / 117 2.5 / 2.1 ページロック 44.8 / 42.1 5.6 / 5.9 ページロックメモリの利用により転送速度が約2倍に向上 相対的に遅かったGPU→CPUの転送が特に高速化 339 先端GPGPUシミュレーション工学特論 2015/05/14 CUDA4.0以降でのページロックメモリ 実用的にはCUDA4.1以降 CUDA4.0では適用できるメモリに制約がある mallocで宣言してcudaHostRegisterでページロッ クメモリとして割当 cudaHostRegister(ホスト変数アドレス, サイズ, フラグ) フラグの種類 340 cudaHostRegisterDefault cudaHostRegisterPortable cudaHostRegisterMapped ページロックにするだけならcudaHostRegisterDefault cudaHostUnregisterによって割当から解放 先端GPGPUシミュレーション工学特論 2015/05/14 ページング可能メモリを使ったコピー #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) cudaMemcpy(dev_data,data,Nbytes, cudaMemcpyHostToDevice); //cudaMemcpy(data,dev_data,Nbytes, // cudaMemcpyDeviceToHost); cudaEventRecord(stop, 0); cudaEventSynchronize(stop); cudaEventElapsedTime (&elapsed_time_ms, start,stop); int main(){ float *data,*dev_data; cudaEvent_t start,stop; float elapsed_time_ms = 0.0f; cudaEventCreate(&start); cudaEventCreate(&stop); printf("%e ms¥n", elapsed_time_ms); cudaEventDestroy(start); cudaEventDestroy(stop); free(data); cudaFree(dev_data); return 0; data = (float *)malloc(Nbytes); cudaMalloc( (void **)&dev_data, Nbytes); cudaEventRecord(start, 0); 341 } copy_pagable.cu 先端GPGPUシミュレーション工学特論 2015/05/14 HostRegisterによる割当 #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); } 342 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; copy_hostregister.cu 先端GPGPUシミュレーション工学特論 2015/05/14 データ転送の性能 入力配列サイズ N = 226 メモリ ページング可能 実行時間 [ms] 転送速度 [GB/s] CPU to GPU / GPU to CPU 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 343 フラグによって性能が変わるが,どれも大して高速化しない 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーホストメモリ ページロックホストメモリを使うとメモリがページアウ トされず,アドレスが固定 GPUからOSを介さずCPUのメモリをコピーできた GPUから書き込む事はできないのか? デバイスからホストメモリを直接読み書きできるよう アドレスをマッピングすれば可能 344 CPU‐GPU間のメモリコピーを要求しない ゼロコピー GPUにおける根本的な制約「GPUはホストメモリを直接参照 できない」を回避 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーメモリ利用の可否 cudaGetDevicePropertiesを利用 使い方はcudaSetDeviceの時と同じ cudaDeviceProp型構造体のメンバcanMapHostMemory を参照 #include<stdio.h> int main(void){ int deviceCount = 0,dev; cudaDeviceProp deviceProp; //ゼロコピーメモリが利用できるなら //supportと表示 if(deviceProp.canMapHostMemory==1) printf("supports¥n"); else printf("doesn't support¥n"); //GPUの数を確認 cudaGetDeviceCount(&deviceCount); for(dev=0;dev<deviceCount;dev++){ //情報を取得するGPUの選択 cudaSetDevice(dev); cudaGetDeviceProperties (&deviceProp, dev); 345 } return 0; } 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーメモリの確保 cudaHostAlloc() cudaHostAlloc((void **)&ホスト変数名, サイズ, cudaHostAllocWriteCombined|cudaHostAllocMapped) cudaHostAllocMapped GPUからアクセスすることを明記 346 cudaHostAllocWriteCombined ホストメモリのキャッシュ管理をOSから切り離し,CUDAが書 き込むことを明記 ホストからの読込は非常に低速 ホストは書込に限定(読み込む場合にはフラグを使わない) 先端GPGPUシミュレーション工学特論 2015/05/14 ホストメモリとデバイスメモリの対応付け CPUとGPUでは仮想メモリ空間が異なる cudaHostAlloc()はCPUのポインタが返される CPUのポインタからGPUで利用できるポインタを取得 cudaHostGetDevicePointer() cudaHostGetDevicePointer((void **)デバイス変数, (void *)ホストポインタ変数, 0); 最後の0はとりあえず入れておかなければならない ドキュメント*にも"At present, Flags must be set to 0."と 書かれている *http://docs.nvidia.com/cuda/samples/0_Simple/simpleZeroCopy/doc/CUDA2.2PinnedMemoryAPIs.pdf 347 先端GPGPUシミュレーション工学特論 2015/05/14 その他の準備 複数のGPUを搭載している場合は利用するGPUを指定 ホストメモリをデバイスから直接読み書きできるよう アドレスのマッピングを許可 cudaSetDevice(); cudaSetDeviceFlags(cudaDeviceMapHost); CUDAの関数を実行する前に上二つの命令を呼ぶ 348 呼ばないとcudaHostGetDevicePointer()がエラーを返す 先端GPGPUシミュレーション工学特論 2015/05/14 初期化と転送も含めたベクトル和 #define N (1024*1024) #define Nbytes (N*sizeof(float)) #define NT (256) #define NB (N/NT) float *a,*b,*c; float *dev_a,*dev_b,*dev_c; 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; 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); //イベント記録の準備 c[i] = a[i] + b[i]; } int main(){ 349 vectoradd_host.cu 先端GPGPUシミュレーション工学特論 2015/05/14 初期化と転送も含めたベクトル和 //開始イベント発生時間の記録 //ホストで初期化 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); //終了イベント発生時間の記録 //イベント同期,時間差の計算 cudaFreeHost(a); cudaFreeHost(b); cudaFreeHost(c); cudaFree(dev_a); cudaFree(dev_b); cudaFree(dev_c); return 0; } cudaThreadSynchronize(); //結果表示,イベントの破棄 vectoradd_host.cu 350 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーによるベクトル和 #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; 351 float *dev_a,*dev_b,*dev_c; cudaSetDevice(0); cudaSetDeviceFlags(cudaDeviceMapHost); //イベント記録の準備 cudaHostAlloc( (void **)&a, Nbytes, cudaHostAllocWriteCombined | cudaHostAllocMapped); cudaHostAlloc( (void **)&b, Nbytes, cudaHostAllocWriteCombined | cudaHostAllocMapped); cudaHostAlloc( (void **)&c, Nbytes, cudaHostAllocWriteCombined | cudaHostAllocMapped); vectoradd_zerocopy.cu 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーによるベクトル和 //CPUとGPUのアドレスをマッピング cudaHostGetDevicePointer(&dev_a,a,0); cudaHostGetDevicePointer(&dev_b,b,0); cudaHostGetDevicePointer(&dev_c,c,0); //開始イベント発生時間の記録 //転送無しでカーネルから読み書き init<<<NB,NT>>>(dev_a,dev_b,dev_c); add <<<NB,NT>>>(dev_a,dev_b,dev_c); //終了イベント発生時間の記録 //イベント同期,時間差の計算 cudaThreadSynchronize(); //結果表示,イベントの破棄 cudaFreeHost(a); cudaFreeHost(b); cudaFreeHost(c); return 0; } vectoradd_zerocopy.cu 352 先端GPGPUシミュレーション工学特論 2015/05/14 実行時間(初期化とベクトル和+転送) 入力配列サイズ N = 220 スレッド数 NT = 256 353 カーネル 実行時間 [ms] 全てCPUで実行 8.74 全てGPUで実行 0.249 vectoradd_host 7.88 vectoradd_zerocopy 3.44 −ベクトル和 +転送 ゼロコピーは全てCPUで実行するよりは早い 全てGPUで実行するよりかなり遅い 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーの使いどころ 演算量の多いカーネル 通常はデータ転送→カーネル実行 ゼロコピーはカーネルの実行中にデータを転送 既にコピーされたデータで大量の計算を行うことでデータの コピーにかかる時間を隠蔽 GPUのメモリ利用の節約 制約 GPUからホストメモリにアクセスしてもキャッシュされない 複数回の読み書きではPCI‐Ex経由の転送が複数発生 354 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーの使いどころ ノートPC等でGPUがシステムのチップセットに組み込ま れている場合* GPUとCPUがメインメモリを物理的に共有 ゼロコピーを使うと常にパフォーマンスが向上 ページロックメモリを利用しすぎるとシステムの性能が低下 GPUがチップセットに組み込まれているかの確認 cudaGetDeviceProperties()を利用 cudaDeviceProp型構造体のメンバintegratedを参照 trueならintegrated GPU, falseならdiscrete GPU *最近はこういう製品がない 355 先端GPGPUシミュレーション工学特論 2015/05/14
© Copyright 2024 Paperzz