Dynamic-Parallelismでプログラミングの負担を軽減

Keplerの前世代のFermiアーキテクチャでは、グリッドの実行を依頼する<<< >>>を含む文はCPUでしか実行できなかった。このため、GPUのカーネルの実行を依頼し、次にカーネルの実行の終了をGPUからCPUに通知し、次にどのカーネルを起動するかはCPUが判断するというプログラムにする必要がある。このため、カーネルの実行のたびに、 「GPU→CPU」の通信と「CPU→GPU」の通信が必要になる。

Dynamic-Parallelismは、GPUで実行するカーネルプログラムから直接、他のカーネルの実行を依頼することができるという機能で、GPU→CPUとCPU→GPUという通信のオーバヘッドを省くことができる。

Fermiでは、左の図のように、GPUで実行するカーネルごとにCPUからの起動、復帰が必要であったが、Keplerでは右の図のようにGPUの中で子供のカーネルを起動することができるようになった (出典:GTC2012でのNVIDIAのプレゼンテーション資料)

CUDAの文法的には、GPUで実行されるカーネルプログラムの中に<<< >>>を含む文が書けるようになっただけであるが、ハードウェア的には、SMXで実行中のカーネルからGrid Management Unitの子供のカーネルの実行を依頼するパスが追加されている。

Keplerでは、カーネルを実行しているSMXからGrid Management Unitの子供のカーネルの実行を依頼するパスが追加されている (出典:GTC2012でのNVIDIAのプレゼンテーション資料)

親カーネルのグリッドが多くの(レジスタ、シェアードメモリ、ワープなどの)資源を使ってしまっている場合、資源不足で子カーネルの実行が開始できないという状況が起こる心配があるが、この場合には、親カーネルをメモリに退避して資源を空けて子カーネルのグリッドを実行し、実行が終わったら親カーネルを復元して実行を続けることができるようになっている。

Dynamic-Parallelismの効果は、単に、CPUとGPU間のカーネルの起動、復帰が減るというだけではない。

マンデルブロー図形の生成は、Dynamic-Parallelismが無い場合は、領域内のすべての点について計算を行う必要があるが、Dynamic-Parallelismを使うと、カーネルの中で図形のありそうな部分を判定し、その部分だけで子カーネルを呼び出すという処理が可能になる (出典:GTC2012でのNVIDIAのプレゼンテーション資料)

このようなマンデルブロー図形を書く場合、最も簡単な方法は、図形の各点について計算を行って黒か白かを決める方法であるが、すべての点について計算を行うので、計算量が多いと問題がある。これに対して、境界付近かどうかをカーネルプログラムの中で判定し、境界付近の領域を担当するスレッドだけで領域を細分化して計算する子カーネルの起動を行わせるようにすると、右側の図のように、境界付近だけは細分化された格子で計算されるが、大部分の領域は粗い格子で計算されることになり、全体の計算量は大幅に減少する。

従来のFermi GPU対応のCUDAでもこのような計算量の削減が不可能という訳では無いが、そのためには複雑なプログラミングが必要になるのに対して、Dynamic-Parallelismを使うと素直なプログラムで済む。

燃焼シミュレーションの例。一定の格子サイズで計算する場合は、炎の無い部分も細かい格子で計算する必要があり計算量が多くなるが、Dynamic-Parallelismを使うと炎のある部分だけを細かい格子で計算すれば良いので、全体の計算量を減らせる (出典:GTC2012でのNVIDIAのプレゼンテーション資料)

上の図のジェットの燃焼状態のシミュレーションでは、炎のある部分は領域を細分化してシミュレーションする必要があるが、炎の無い領域は細かく計算する必要はない。しかし、FermiアーキテクチャのGPUではDynamic-Parallelismがサポートされておらず、炎の有り無しに拘わらず、均一の格子で分割してシミュレーションする必要があった。

これに対して、Kepler GPUでは、GPUで走るカーネルの中で、炎があり、細分化を必要とする格子領域に関しては領域を細分化して詳細なシミュレーションを行う子カーネルを起動し、その他の部分では細分化する子カーネルを起動しないというようにすれば、均等分割の格子に比べて、少ない計算量で、注目する部分についてはより精度の高い解析を行うことができるようになった。

このように、Dynamic-Parallelismを使うにはプログラムの書き換えが必要となるが、プログラムの見通しを悪くするような複雑なプログラミング手法を使わなくても、ムダな計算を省いて計算時間を短縮することができるので、実用的なメリットが大きい。

Compute Capability 3.5

NVIDIAのGPUでCUDAが動くようになったのは、2006年に発売されたGeForce 8800 GTXなどが最初の製品であるが、その後、多くの機能が追加されて、今日のKepler GPUに至っている。従って、CUDAでプログラミングができると言っても、各GPUのハードウェアの機能レベルで使える機能に差が出る。

このハードウェアの機能レベルを、NVIDIAはCompute Capabilityと呼んでいる。最初の世代であるGeForce 8800 GTXなどのCompute Capabilityは1.0であり、その後、1.1、1.2、1.3と改良が加えられた。そして、Fermi GPUが出て、Compute Capabilityは2.xとなり、Kepler GPUではCompute Capabilityは3.xとなっている。

Kepler GPUには、現在、「GK104」と「GK110」という2種類のチップがある。GK104は3Dグラフィックスや主に単精度演算を中心とする科学技術計算向けのGPUで、GeForce GTX 640~690グラフィックスボードや科学技術計算用のTesla K10アクセラレータに使用されており、これらの製品のCompute Capabilityは3.0となっている。

一方、GK110チップは倍精度浮動小数点演算性能が強化されたチップで、Top500 1位のTitanスパコンに搭載されているK20Xやその一般製品版のK20アクセラレータに使用されており、これらの製品のCompute Capabilityは3.5となっている。この0.5の差が、実は、Hyper-QとDynamic-Parallelismがサポートされているかどうかである。

コンシューマ製品であるGeForce GTX TitanではHyper-QとDynamic-Parallelismはサポートされないという情報もあったのであるが、これは間違いで、NVIDIAのWebに掲載されているCompute Capabilityの一覧でGeForce GTX TitanのCompute CapabilityはK20と同じ3.5であることが公表された。

これは、K20/K20Xを搭載するスパコンのプログラムをGeForce GTX Titanを搭載するPCやワークステーションで開発することができるということを意味している。また、スパコンで開発された各種シミュレーションのプログラムが、ハード全体の規模の違いはあるものの、GeForce GTX Titanを搭載するPCでも使えるということを意味している。

その意味で、GeForce GTX TitanはGPUコンピューティングを普及させようというNVIDIAの戦略の中で大きな位置を占める製品であると言える。また、999ドルというコンシューマ価格で1.3TFlopsの倍精度浮動小数点演算性能を持つGeForce GTX Titanは、各種シミュレーションなどの大量の数値計算を行う大学や企業の研究者には強い味方になると思われる。