第5回 GPUのメモリ階層の詳細 (様々なメモリの利用) 長岡技術科学大学 電気電子情報工学専攻 出川智啓 今回の内容 コンスタントメモリ ページロックホストメモリ ゼロコピーホストメモリ ベクトル和による性能比較 392 先端GPGPUシミュレーション工学特論 2015/05/14 メモリの種類 オフチップメモリ(GPUのチップ外部のメモリ) 低速アクセス,大容量 CPUから直接アクセス可能 ローカルメモリだけはアクセス不可 グローバルメモリ ローカルメモリ テクスチャメモリ コンスタントメモリ 容量 大 小 大 小 速度 低速 低速 高速* 高速* 読み書き可 読み書き可 読み込み可 読み込み可 全てのスレッドが同 じメモリにアクセス 各スレッドが異なる メモリにアクセス 全てのスレッドが同 じメモリにアクセス 全てのスレッドが同 じメモリにアクセス 読み書き可 読み書き不可 書き込み可 書き込み可 GPUからの 読み書き CPUからの アクセス 393 先端GPGPUシミュレーション工学特論 *キャッシュが効く場合 2015/05/14 コンスタントメモリ GPU全体で同じメモリに アクセス コンスタントキャッシュを 利用することで,効率的 な読み込みが可能 キャッシュはオンチップ GPU Chip SM SM 共有 メモリ L1キャッ シュ レジ スタ レジ スタ レジ スタ レジ スタ CUDA CUDA CUDA CUDA Core Core Core Core L1キャッ シュ レジ スタ レジ スタ GPU全体で64kB レジ スタ ローカル ローカル メモリ メモリ ・・・ ローカル ローカル メモリ メモリ ・・・ コンスタントメモリ ホスト メモリ テクスチャメモリ グローバルメモリ 394 レジ スタ CUDA CUDA CUDA CUDA Core Core Core Core L2キャッシュ 共有 メモリ 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリの利用 修飾子 __constant__ を付けて宣言 メモリは読込専用 CPUからは変更可能 専用のメモリ転送命令でコピー cudaMemcpyToSymbol 395 CPU上のメモリをコンスタントメモリにコピーする cudaMemcpyToSymbol(転送先変数名, 転送元アドレス, バイト数, オフセット, 方向); オフセット,方向は無くてもよい オフセットを省略すると0が使われる 方向を省略するとcudaMemcpyHostToDeviceが使われる 先端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/ 396 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリの宣言 サイズは静的に決定 __constant__ 型 変数名; __constant__ 型 変数名[要素数]; 配列としても宣言可能 要素数はコンパイル時に確定している必要がある cudaMalloc()やcudaFree()は不要 グローバル変数として宣言し,複数のカーネルから アクセス 397 読込専用のメモリならではの使い方 書込可能なメモリでは厳禁 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス (Tesla世代) コンスタントメモリへ高速にアクセスできる要因 1. ブロードキャストによるデータの分配 16スレッド(Half Warp)単位でアクセスし,1回の読込を 他のスレッドにブロードキャストできる 16スレッドが同じアドレスにアクセスすると最も効率がよい 2. コンスタントメモリキャッシュ 398 SMごとに存在する独自のオンチップキャッシュ 他のHalf Warpがキャッシュされたデータへアクセスして も,コンスタントメモリからの読込が発生しない 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス (Tesla世代) オフチップメモリ(DRAM)からの読込量を抑制 オフチップメモリ(DRAM)からの読込による実行速度 低下を回避 コンスタントメモリへのアクセスの制約 399 Half Warp内のスレッド全てが異なるコンスタントメモリの アドレスを参照すると,読込が逐次化 読込命令の処理に最悪で16倍の時間を要する 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス (Tesla世代) Half Warp内のスレッド全てが同じ メモリアドレスにアクセス 1スレッドの読込をブロードキャストによっ て残りのスレッドが共有 メモリアドレス 0 0 1 2 3 4 5 6 他のHalf Warpも同じメモリアドレス にアクセス データがキャッシュされているため, キャッシュから高速に読み出し 7 8 9 10 11 12 13 14 15 Half Warp内でのスレッドID 400 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス (Tesla世代) Half Warp内のスレッド全てが異 なるメモリアドレスにアクセス 読込が逐次化 読込処理の時間は,Half Warpがアク セスするコンスタントメモリアドレスの数 に比例 最悪で処理に16倍の時間がかかる 401 おそらくグローバルメモリアクセスよりも遅 くなる 先端GPGPUシミュレーション工学特論 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 2015/05/14 コンスタントメモリへのアクセス (Fermi世代) コンスタントメモリへ高速にアクセスできる要因 1. ブロードキャストによるデータの分配 32スレッド(Warp)単位でアクセスし,1回の読込を他のス レッドにブロードキャストできる 32スレッドが同じアドレスにアクセスすると最も効率がよい 2. コンスタントメモリキャッシュ 402 SMごとに存在する独自のオンチップキャッシュ 他のWarpがキャッシュされたデータへアクセスしても,コン スタントメモリからの読込が発生しない 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス (Fermi世代) オフチップメモリ(DRAM)からの読込量を抑制 オフチップメモリ(DRAM)からの読込による実行速度 低下を回避 コンスタントメモリへのアクセスの制約 403 Warp内のスレッド全てが異なるコンスタントメモリのアドレ スを参照すると,読込が逐次化 読込命令の処理に最悪で32倍の時間を要する 先端GPGPUシミュレーション工学特論 2015/05/14 コンスタントメモリへのアクセス (Fermi世代) Warp内のスレッド全てが同じコンスタントメモリアドレ スにアクセス 1スレッドの読込をブロードキャストによって残りのスレッド が共有 他のWarpも同じメモリアドレスにアクセス データがキャッシュされているため,キャッシュから高速に 読み出し 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 0 404 16 32 48 64 80 96 先端GPGPUシミュレーション工学特論 11 2 2015/05/14 コンスタントメモリへのアクセス (Fermi世代) Warp内のスレッド全てが異なるコンスタントメモリアド レスにアクセス 読込が逐次化 読込処理の時間は,Warpがアクセスするコンスタントメモリ アドレスの数に比例 最悪で処理に32倍の時間がかかる おそらくグローバルメモリアクセスよりも遅くなる 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 0 405 16 32 48 64 80 96 先端GPGPUシミュレーション工学特論 11 2 2015/05/14 コンスタントメモリ利用の例 ベクトル和 ベクトルAとBの値が全て同じ コンスタントメモリにデータを一つ置き,全スレッドが参照 ・・・ a[i] + + + + a + + b[i] ・・・ b c[i] ・・・ c[i] 406 先端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]; } 407 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; } 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; 408 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 409 カーネル 実行時間 [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; 410 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 411 カーネル 実行時間 [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 412 カーネル 実行時間 [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キャッシュ ローカル ローカル メモリ メモリ ・・・ ローカル ローカル メモリ メモリ ・・・ コンスタントメモリ ホスト メモリ テクスチャメモリ グローバルメモリ 413 先端GPGPUシミュレーション工学特論 2015/05/14 ページロック(ピン)メモリ OSによって管理されるメモリのうち,ページアウトしな いことが保証されているメモリ OSが配列の物理アドレスを記憶 GPUが物理アドレスを知れば,ダイレクトメモリアクセ ス(DMA)を使ってホストとデータを交換できる 414 先端GPGPUシミュレーション工学特論 2015/05/14 OSによる記憶管理 記憶管理 メモリのアドレスをプロセス固有の仮想アドレスに変換して 割り付け 仮想記憶方式を採用 415 メモリの物理的な大きさに依存せず,また不連続なメモリ領 域を連続に見せかける方式 1個の記憶装置を占有しているようにプログラム可能 先端GPGPUシミュレーション工学特論 2015/05/14 仮想記憶方式 仮想的な記憶装置上のアドレス メモリ上のアドレス 仮想アドレス,論理アドレス 仮想アドレスと実アドレス の変換はOSが管理 実アドレス,物理アドレス 多重仮想記憶 416 システム内に複数の仮想アドレス空間を形成し,プロセス ごとに割り当て 現在の計算機の主流 CPUとGPUも仮想アドレス空間が異なる 先端GPGPUシミュレーション工学特論 2015/05/14 ページング方式 仮想メモリ空間と物理メモリ空間を一定サイズの ページと呼ばれる単位に分割して管理 ページテーブル 仮想アドレスから実アドレスの対応表 仮想アドレスから実アドレスへの変換はページ単位で実行 CPU _____ _____ _____ _____ _____ _____ _____ _____ _____ メモリ _____ _____ _____ 417 ページ テーブル _____ _____ _____ プロセスA ページ OS _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ 先端GPGPUシミュレーション工学特論 スレッド メモリ 2015/05/14 ページアウト 物理メモリ上にない仮想メモリを参照 補助記憶装置(ハードディスクなど)に退避されたデータ ページフォルトという割り込みがかかり,OSに制御が移行 ページフォルトがおこると膨大な時間がかかる OSは物理メモリ上のアドレスを追い出す(ページアウト) 必要なページを補助記憶装置から物理メモリ上に読み込む ハード ディスク 419 CPU _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ メモリ _____ _____ _____ _____ _____ ページ テーブル _____ _____ _____ _____ プロセスA ページ OS _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ 先端GPGPUシミュレーション工学特論 スレッド メモリ ページアウト 2015/05/14 ページロック(ピン)メモリ ページアウトされない事がOSによって保証 ピンで刺された紙のように固定されている pinned memory そのメモリの物理アドレスにアクセスしても安全 全てのメモリをページロックにすると,他のプログラ ムが起動できなくなる可能性がある CPU _____ _____ _____ _____ _____ _____ _____ _____ _____ メモリ _____ _____ _____ 420 ページ テーブル _____ _____ _____ プロセスA ページ OS _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ 先端GPGPUシミュレーション工学特論 スレッド メモリ 2015/05/14 CUDAによるメモリ転送 ページング可能なメモリを使ってもDMA転送を行う 1. 2. ページング可能なシステムバッファからページロックされ たステージングバッファへコピー ステージングバッファからGPUへDMAでコピー ページロックメモリを使う事で約2倍の転送速度が 期待される 421 Fermi世代のGPUでは,10MBを超えるデータを転送すると きにページロックメモリを使用すると効率的 先端GPGPUシミュレーション工学特論 2015/05/14 ページロックホストメモリの確保と解放 cudaMallocHost() ページロックされたホストメモリを確保 cudaMallocHost((void **)&ホスト変数名, サイズ); cudaMallocHostで確保されたメモリはmallocで確保さ れたメモリと同様に利用可能 cudaFreeHost() 422 cudaMallocHostで確保されたホストメモリを解放 cudaFreeHost(ホスト変数のアドレス); 先端GPGPUシミュレーション工学特論 2015/05/14 ページロックホストメモリの確保 (もう一つの方法) cudaHostAlloc() cudaHostAlloc((void **)&ホスト変数名,サイズ,フラグ); フラグ cudaHostAllocDefault cudaHostAlloc()の働きをcudaMallocHost()と同じにする 確保したメモリの解放はcudaFreeHost()を利用 後述のゼロコピーホストメモリを確保するために利用 423 cudaHostAllocDefault以外のフラグを指定 フラグについては後述 cudaHostAllocで統一した方が楽かもしれない 先端GPGPUシミュレーション工学特論 2015/05/14 ページング可能メモリを使ったコピー #include<stdio.h> #include<stdlib.h> #define N (1024*1024*64) #define Nbytes (N*sizeof(float)) cudaMemcpyHostToDevice); //GPU→CPUはコメントを外す //cudaMemcpy(data,dev_data,Nbytes, // cudaMemcpyDeviceToHost); int main(){ float *data,*dev_data; cudaEvent_t start,stop; float elapsed_time_ms = 0.0f; cudaEventCreate(&start); cudaEventCreate(&stop); 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; data = (float *)malloc(Nbytes); cudaMalloc( (void **)&dev_data, Nbytes); } cudaEventRecord(start, 0); cudaMemcpy(dev_data,data,Nbytes, 424 copy_pagable.cu 先端GPGPUシミュレーション工学特論 2015/05/14 ページロックホストメモリを使ったコピー #include<stdio.h> #include<stdlib.h> #define N (1024*1024*64) #define Nbytes (N*sizeof(float)) cudaMemcpyHostToDevice); //GPU→CPUはコメントを外す //cudaMemcpy(data,dev_data,Nbytes, // cudaMemcpyDeviceToHost); int main(){ float *data,*dev_data; cudaEvent_t start,stop; float elapsed_time_ms = 0.0f; cudaEventCreate(&start); cudaEventCreate(&stop); 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; cudaHostAlloc( (void **)&data, Nbytes, cudaHostAllocDefault); cudaMalloc( (void **)&dev_data, Nbytes); } cudaEventRecord(start, 0); cudaMemcpy(dev_data,data,Nbytes, 425 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の転送が特に高速化 426 先端GPGPUシミュレーション工学特論 2015/05/14 CUDA4.0以降でのページロックメモリ 実用的にはCUDA4.1以降 CUDA4.0では適用できるメモリに制約がある mallocで宣言してcudaHostRegisterでページロッ クメモリとして割当 cudaHostRegister(ホスト変数アドレス, サイズ, フラグ) フラグ 427 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) 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); int main(){ float *data,*dev_data; cudaEvent_t start,stop; float elapsed_time_ms = 0.0f; cudaEventCreate(&start); cudaEventCreate(&stop); cudaEventDestroy(start); cudaEventDestroy(stop); free(data); cudaFree(dev_data); return 0; data = (float *)malloc(Nbytes); cudaMalloc( (void **)&dev_data, Nbytes); } copy_pagable.cu 428 先端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); 429 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; 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 430 フラグによって性能が変わるが,どれも大して高速化しない 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーホストメモリ ページロックホストメモリを使うとメモリがページアウ トされず,アドレスが固定 GPUからOSを介さずCPUのメモリをコピーできた GPUから書き込む事はできないのか? デバイスからホストメモリを直接読み書きできるよう アドレスをマッピングすれば可能 431 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); 432 } return 0; } 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーメモリの確保 cudaHostAlloc() cudaHostAlloc((void **)&ホスト変数名,サイズ,フラグ); フラグ(|(or)で複数指定可能) cudaHostAllocMapped GPUのアドレス空間にマッピングされたホストメモリを宣言 cudaHostAllocWriteCombined ホストメモリのキャッシュ管理をOSから切り離す PCI‐Ex経由のデータ転送を高速化できる可能性がある ホストからの読込は非常に低速 ホストが書き,GPUが読むバッファとしての利用に適する 433 cudaHostAllocPortable 先端GPGPUシミュレーション工学特論 2015/05/14 ホストメモリとデバイスメモリの対応付け cudaHostAlloc()はCPUのポインタを返す CPUとGPUでは仮想メモリ空間が異なる 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 434 先端GPGPUシミュレーション工学特論 2015/05/14 その他の準備 複数のGPUを搭載している場合は利用するGPUを指定 ホストメモリをデバイスから直接読み書きできるよう アドレスのマッピングを許可 cudaSetDevice(); cudaSetDeviceFlags(cudaDeviceMapHost); CUDAの関数を実行する前に上二つの命令を呼ぶ 435 呼ばないとcudaHostGetDevicePointer()がエラーを返す 先端GPGPUシミュレーション工学特論 2015/05/14 初期化と転送も含めたベクトル和 #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; 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); c[i] = a[i] + b[i]; } int main(){ float *a,*b,*c; 436 vectoradd_host.cu 先端GPGPUシミュレーション工学特論 2015/05/14 初期化と転送も含めたベクトル和 printf("%e ms¥n",elapsed_time_ms); 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); 437 cudaFreeHost(a); cudaFreeHost(b); cudaFreeHost(c); cudaFree(dev_a); cudaFree(dev_b); cudaFree(dev_c); return 0; vectoradd_host.cu 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーによるベクトル和 #define N (1024*1024) #define Nbytes (N*sizeof(float)) #define NT (256) #define NB (N/NT) cudaEvent_t start,stop; float elapsed_time_ms = 0.0f; cudaEventCreate(&start); cudaEventCreate(&stop); __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]; } cudaSetDevice(0); cudaSetDeviceFlags(cudaDeviceMapHost); int main(){ float *a,*b,*c; float *dev_a,*dev_b,*dev_c; 438 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); } 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; 439 vectoradd_zerocopy.cu 先端GPGPUシミュレーション工学特論 2015/05/14 実行時間(初期化とベクトル和+転送) 入力配列サイズ N = 220 スレッド数 NT = 256 440 カーネル 実行時間 [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経由の転送が複数発生 441 先端GPGPUシミュレーション工学特論 2015/05/14 ゼロコピーの使いどころ ノートPC等でGPUがシステムのチップセットに組み込ま れている場合* GPUとCPUがメインメモリを物理的に共有 ゼロコピーを使うと常にパフォーマンスが向上 ページロックメモリを利用しすぎるとシステムの性能が低下 GPUがチップセットに組み込まれているかの確認 cudaGetDeviceProperties()を利用 cudaDeviceProp型構造体のメンバintegratedを参照 trueならintegrated GPU, falseならdiscrete GPU *最近はこういう製品がない 442 先端GPGPUシミュレーション工学特論 2015/05/14
© Copyright 2024 ExpyDoc