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

既存の CPU 向け並列化プログラミング環境と CUDA

3.4 GPUPC GEMM Library の評価

4.1.2 既存の CPU 向け並列化プログラミング環境と CUDA

4.1 既存のCPU向け並列化プログラミング環境を用いたGPGPUプログラミング

の提案 83

タの送受信を行うはCPUによる制御が必要なため,既存のCPU向けプログラムと 同様の記述や動作が行えない可能性もある.

以上のように,GPGPUの持つ並列性は既存のCPU向け並列化プログラミング 環境を利用して記述できることが期待できる.

さらに,CPU-GPU間での通信をMPIで記述し,GPU上の演算器群間での並列 処理をOpenMPで記述し,演算器群内の演算器間での並列処理をSIMD関数で記述 するなど,既存のCPU向け並列化プログラミング環境を複数組み合わせて利用す ることも考えられる.これによりGPUの持つ並列性を適切かつ容易に記述できる 可能性がある一方,複数の並列化プログラミング環境を組み合わせたプログラミン グはアプリケーションプログラマにとっての実装の手間を増加させてしまうこと考 えられる.

4.1 既存のCPU向け並列化プログラミング環境を用いたGPGPUプログラミング

の提案 84

CUDA

CUDAはGPUをデータ並列計算機として活用するためのハードウェアとソフト ウェアのアーキテクチャであり,アプリケーションプログラマに対してC/C++を 拡張した言語仕様のプログラミング言語によるGPGPUプログラム開発環境を提供 している.CPU上で実行される実行ファイルについてはCUDAの提供するライブ ラリをリンクする必要がある以外は既存のCPU向けプログラムと同様に(gccなど を用いて)作成することが可能であり,GPU上で実行される実行ファイルは専用の コンパイラ(nvcc)を用いて生成する必要がある.

ここではCUDAと既存のCPU向け並列化プログラミング環境との対応付けの検 討に向けて,CUDAの実行モデルと演算器やメモリの配置および標準的なC/C++

とのプログラム記述の違いを確認する.

CUDAにおけるGPUのハードウェアモデルを図36に示す.GPUはStreaming Multiprocessor(以下SM)と呼ばれる演算ユニット(演算器群)を複数個(GPUによっ て異なるが,現在はGPU1台あたり1から30個)搭載しており,さらにSM内には Scalar Processor(以下SP)と呼ばれる演算器を8個搭載している.

これに対して並列実行の単位としてはGrid,Block,Threadがある.

GridはCPUがGPUに演算を実行させる際の単位である.GPUへの演算の指定 は関数単位で行う必要があるため,GPUに対する1関数呼び出しを1Gridと考えれ ば良い.

Gridは複数のBlock(以下CUDA Blockと呼ぶ)から構成され,さらにCUDA Block は複数のThread(以下CUDA Threadと呼ぶ)から構成される.CPUからGPUに 処理を行わせる際には,1Gridの実行において使用するCUDA Blockの数とCUDA BlockあたりのCUDA Threadの数(各CUDA Blockで異なる数とすることは不可能) を指定する.これによりCU DABlock×CU DAT hread数 個のタスクが生成され,

CUDA Block単位でSMに割り当てられる.各CUDA Block内のCUDA Threadは 同一SM内のSPによって並列実行される.なお,CUDA Block数やCUDA Thread 数はGrid内で変更することはできない.Grid実行時にはCUDA Blockごとおよび CUDA Threadごとに一意に割り当てられたIDを取得することができるため,この IDを利用してデータ並列処理を行うのがCUDAにおける並列プログラムの基本と なる.

4.1 既存のCPU向け並列化プログラミング環境を用いたGPGPUプログラミング

の提案 85

図 36: CUDAのハードウェアモデル

4.1 既存のCPU向け並列化プログラミング環境を用いたGPGPUプログラミング

の提案 86

物理的なSMやSPの数以上のCUDA BlockやCUDA Threadが割り当てられた 場合は時分割実行される.CUDAにおけるタスクスイッチのコストは小さいため,

SMにはSP数を上回るCUDA Thread数を割り当て,時間のかかるメモリアクセス が起きた際にタスクスイッチが行われメモリアクセスのレイテンシが隠蔽されるよ うに実装するのが良いとされている.適切な1CUDA BlockあたりのCUDA Thread 数の目安としては256程度が良いとされているが,対象プログラムに含まれる命令 の数やメモリアクセスの種類と頻度等により最適な値は上下する.

GPU内の各SMは独立に処理を行う事ができる一方,同一SM内のSPは同時に 同種の演算しか行う事ができない.そのため同一SM内のSPが異なる分岐パスを 辿るようなプログラムを記述した場合は,正しい実行結果を返すことは可能である もの,条件が成立しない命令についてはマスク処理を行うため性能が低下する.

一方,CUDAにおけるGPUを用いた計算モデルは以下の処理を繰り返すモデル である.

1. CPUがメインメモリ上のデータをGPU上のメモリへと転送する

2. CPUからGPUに対して関数(Grid)実行を指示し,GPUが演算を開始する 3. GPUの演算が終了した後に,CPUからGPUへの指示によってGPU上のメ

モリからメインメモリへ演算結果などのデータが転送される

メインメモリとGPU上のメモリの間におけるデータの送受信については,CPU上 のプログラムからGPU上のメモリのアドレスを直接参照することはできず,メモ リの確保や転送を行うAPI関数を介して行う必要がある.またGPU上には複数種 類の異なる特徴を持つメモリが存在し,全てのメモリをCPUから操作できるわけ ではない.

GPU上のメモリは図37のように分類され,各メモリはそれぞれ以下のような特 徴を持っている.

1. GlobalMemory:全CUDA Blockおよび全CUDA Threadから共有メモリとし て読み書き可能.(高レイテンシ・低速・大容量)

2. ConstantMemory:全CUDA Blockおよび全CUDA Threadから読み取り専用 のメモリとして扱うことができる.(高レイテンシ・キャッシュされる・GPU 全体で64KB)

4.1 既存のCPU向け並列化プログラミング環境を用いたGPGPUプログラミング

の提案 87

3. TextureMemory:全CUDA Blockおよび全CUDA Threadから読み取り専用 のメモリとして扱うことができる.(高レイテンシ・キャッシュされる・大容量) 4. SharedMemory:各CUDA Blockが独立に持ち同一CUDA Block内のCUDA

Threadからは共有メモリとして読み書き可能.(オンチップ・低レイテンシ・

高速・SMごとに16KB)

5. 各CUDA Threadが独立に持つRegister.(低レイテンシ・高速・SMごとに8192 本または16384本)

6. 各CUDA Threadが独立に持つLocalMemory.(GlobalMemoryと同等)

GlobalMemory,ConstantMemory,TextureMemoryについては,専用のメモリ操作 関数を用いることでCPUからGridを実行する前後に読み書きすることができるが,

その他のメモリについてはCPUからアクセスすることはできない.

CUDAにおけるプログラム記述では変数宣言時に接頭語を付加することでその変数 がどのメモリとして扱われるかを指定する. device をつけることでGlobalMemoy,

constant でConstantMemory, shared でSharedMemoryとして扱われる.

Tex-tureMemoryは専用の関数と構造体を用いて利用する.接頭語をつけずに関数内で

宣言された変数はRegisterとして扱われ,Registerの数を超えた分は自動的に Lo-calMemoryとして扱われる.

変数と同様に,関数についても接頭語を利用して振る舞いを指定する.

global をつけた関数(Global関数) はCPUから呼び出されてGPU上で実行さ れる関数として, device をつけた関数(Device関数)はGPU上で実行される関数 ( global 関数もしくは device関数 )から呼び出されてGPU上で実行される関数 として扱われる.これらの関数はGPU上で実行可能なバイナリのみが生成される.

host をつけた関数(Host関数) はCPU上で実行される関数から呼び出されて CPU上で実行される関数として扱われ,接頭語のない関数もこれと同じ扱いとなる.

host と device を同時につけた場合は, device をつけた関数と同様にGPU上 で実行可能なバイナリが生成されるとともに, host をつけた関数と同様にCPU 上でも実行可能となる.

CPUから行うGlobal関数の呼び出しは,関数の終了を待たずに制御が戻る非同

期呼び出しである.そのためマルチスレッド化など手間のかかるプログラム記述を

4.1 既存のCPU向け並列化プログラミング環境を用いたGPGPUプログラミング

の提案 88

図 37: CUDAのメモリモデル

4.1 既存のCPU向け並列化プログラミング環境を用いたGPGPUプログラミング

の提案 89

行うことなくCPUとGPUによる並列処理を記述することができる.CPU上で専 用の関数を実行することで,Global関数の終了を確認する(待つ)ことができる.

GPU上で実行される関数については,C/C++言語と比べて再帰呼び出しやstatic 変数が使えないといった制限がある.またCUDA特有の処理として,同一CUDA Block内の全CUDA Threadで同期をとる関数や,GPU全体でのアトミック実行を 保証するatomic関数が提供されている.

このように,CUDAを用いてプログラムを作成するには,GPUのアーキテクチャ や実行モデル,プログラムの記述方法を把握し,手間のかかるプログラミングを行 わなくてはならずアプリケーションプログラマにとっては手間が大きい.特にGPU を最大限に活用して高い性能を得るためには,GPU上に搭載されている多数の演算 器と様々なメモリを活用する必要があるため,アプリケーションプログラマにとっ てのプログラミングの手間は非常に大きなものとなる.

なお本研究では複数のGPUを搭載した環境向けの実装を行っていないため詳細 については触れないことにするが,CUDAは複数のGPUにも対応している.

以下,CUDAと既存のCPU向け並列化プログラミング環境との対応付けを検討 する.

SIMDプログラミング

SIMD処理は多数のデータに同じ演算を行うものである(図38-1)ことから,同時 に同じ種類の演算しか行うことができない同一SM内のSPによるCUDA Threadレ ベルの並列処理にも,CUDA Blockレベルの並列処理にも対応付けることができる と考えられる.

特にSIMD化は細粒度の並列化に適していることから,SIMD演算を多数のCUDA ThreadによるSharedMemory を用いた並列計算へと対応付けることで,高速なShared-Memoryを有効に活用した高速処理による高い性能が期待できる (図38-2).

一方で,GlobalMemoryを共有メモリと見なしてCUDA Blockレベルの並列性を 記述し,SIMD関数へのデータセットをCPUからGPUへのデータ転送,SIMD関 数の実行をGPU上での演算とみなし,GPU全体でSIMD演算を行うことも考えら れる (図38-3).この対応付けでは,アプリケーションプログラマに対してCUDAプ