後藤弘茂のWeekly海外ニュース

フラットで自由度が高いNVIDIA Fermiのアーキテクチャ



●NVIDIA GPUアーキテクチャの変遷

 NVIDIA GPUは、アーキテクチャの比重をグラフィックスから汎用コンピューティングへと移している。伝統的なグラフィックスでの性能より、汎用コンピューティングでの性能を重視する方向だ。G80では、まだプロセッサの構造はグラフィックスの性能を重視したものだった。しかし、次世代のFermiでは、より汎用コンピューティングへと最適化した。

 ただし、ここで言っている汎用コンピューティングの中には、新しい形でのグラフィックス処理も含んでいる。例えば、伝統的な3Dグラフィックスパイプに、レイトレーシングなどを取り込んだモデルだ。その意味では、NVIDIAは、汎用コンピューティングへの最適化によって、将来のグラフィックスタスクへの最適化を進めていると言える。

 NVIDIAの汎用コンピューティングへの傾斜を明瞭に示すのはGPUの演算プロセッサのスレッドスケジューリングのリソースだ。G80アーキテクチャは、1スレッドが使うレジスタ数が十数本と少なく、内部メモリは活かさず、メモリレイテンシの隠蔽は膨大な数のスレッドの切り替えだけで行なうアプリケーションに最適化されていた。これに最も当てはまるのは、伝統的なグラフィックスタスクだ。

 一方、Fermiアーキテクチャは、1スレッドが使うレジスタ数が20本以上と多く、メモリレイテンシはスレッディングで隠蔽するだけでなく、内部メモリを積極的に活用することで解消するアプリケーションに最適化されている。これは、汎用コンピューティング系のアプリケーションだ。

 G80以降のNVIDIAのGPUにはアーキテクチャの継続性があり、世代を通じて命令実行の基本的な仕組みを変えていない。G80からGT200、Fermiに至るまで命令のスケジューリングと発行、実行の根本的な仕組みは同じだ。しかし、その最適化がかなり異なり、それが各世代の方向性を示している。

●バーチャルなベクタ長を固定して物理レジスタ長を変える

 NVIDIA GPUは「Streaming Multiprocessor(SM)」と呼ばれるプロセッサクラスタ単位で、命令の実行を制御している。SMの中に、実行ユニット、命令ユニット、レジスタ、キャッシュが含まれる。要素だけを見るなら、SMがCPU的な意味での“コア”だと言ってもいい。しかし、NVIDIA GPUの命令発行の仕組みは、通常のCPUとは大きく異なっている。

NVIDIA GPUクラスタの進化

 以前の記事でも説明したが、NVIDIA GPUでは、32スレッドを束ねた「Warp(ワープ)」と呼ばれるスレッドバッチ単位でプログラムの実行を制御している。Warpの中の32のスレッドは、いずれも同じ命令を実行する。NVIDIA GPUは、ベクタ長が32のベクタプロセッサだと言える。

 NVIDIAは、バーチャルな論理上のベクタの長さを32に固定して、物理的なベクタユニットの長さはGPU世代によって最適化する方法を取っている。G80/GT200のSMに搭載されている単精度浮動小数点積和算ユニット「Streaming Processor(SP)」の数は8個なので、物理的なベクタサイズは8だ。これがFermiでは16へと倍増している。つまり、G80/GT200はバーチャルベクタ長が32で物理ベクタ長が8のマシン、Fermiは物理ベクタ長が16のマシンだ。

 同じ命令を32スレッドに渡って実行するため、NVIDIA GPUの命令ユニットは、複数サイクルに渡って同じ命令を発行する。G80/GT200系では8個の実行ユニットSPが4サイクルに渡って同じ命令を実行し、8 SP×4サイクル=32スレッドを実行する。Fermiでは16個の実行ユニットCUDAコアが2サイクルに渡って同じ命令を実行し、16 CUDAコア×2サイクル=32スレッドを実行する。

●動的にWarpの実行順序を入れ替えるスケジューリング

 G80の命令ユニットはマルチスレッド(マルチWarp)化されており、各Warpの実行順序を入れ替えるOut-of-Orderで実行することができる。1つのSMの中で実行される個々のWarpは、同じカーネルプログラムを走らせる。ただし、個々のWarpの命令ストリームの中では、プログラムは順番(In-Order)に実行される。

 古いGPUアーキテクチャでは、GPUのシェーダプロセッサ全体で同じカーネル中の同じ命令を同期して実行していた。しかし、現在のNVIDIAアーキテクチャでは、個々のWarp毎に異なる命令を実行できる。正確には、ある特定のサイクルで見ると、それぞれのWarpで、カーネルプログラムの実行している部分が異なる。例えば、Warp1がカーネルプログラム中の2番目の命令を実行している時に、Warp2は82番目の命令を実行するといった具合だ。

 各Warpでの命令ストリームはIn-Orderで、プログラム中の命令を順番に実行する。例えば、Warp1は命令2を実行したら次に命令3を実行する。しかし、命令ユニットは、多くのWarpをプールして置いて、リソースが利用可能になったWarpの命令からOut-of-Orderに実行する。例えば、Warp1の命令2を発行したら、その次はWarp24の命令38を発行すると言った具合だ。

G80でのWARPスケジューリングの仕組み

 G80以前のGPUは、1つのWarp(=スレッドバッチ)の命令をIn-Order実行し続けて、メモリアクセスなどでストールすると他のWarpに切り替えるという方式を取っていた。AMDのGPUは、現在もそれに近い方式を取っている。それに対して、NVIDIA G80以降のWarpスケジューリングは、よりダイナミックだ。

●命令ユニットを拡張しやすいNVIDIAアーキテクチャ

 NVIDIA方式の利点の1つは、命令発行がシンプルで効率的になる点だ。まず、1つのスレッドに対して命令ストリームの中から並列に実行できる命令を見つけて並列化する必要がない。x86 CPUがやっているような、Out-of-Order型の複雑な命令スケジューリングは必要ない。また、命令間の依存関係による制約も少ない。異なるWarpの演算命令は、依存性を持たないからだ。

 そのため、NVIDIAアーキテクチャでは、命令ユニットを増やして命令発行数を増やすことが比較的容易にできる。実際、Fermiでは2命令を同時発行する。しかし、2つの命令ユニットはそれぞれ異なるWarpの命令を発行するため、命令スケジューラが同時発行される命令間の依存性をチェックする必要がない。アーキテクチャ上の発展性が、Fermiで実現されたことになる。

 また、NVIDIA方式では、命令実行のレイテンシとメモリアクセスのレイテンシを同時に隠蔽できる。例えば、Warp1の命令2の実行には、命令発行から数えて7サイクルのレイテンシが必要だとする。すると、Warp1の命令3が命令2の結果に依存する場合、7サイクル後でなければ命令3を発行できない。実際には実行ユニットは1つのWarpに渡って複数サイクル命令を発行されるので、レイテンシは2~4倍に増える。しかし、NVIDIA方式の命令スケジューリングでは、充分な数のWarpを立ち上げることができれば、Warpスケジューリングを深くすることで、レイテンシを簡単に隠蔽できる。

●G80/GT200世代での命令発行の仕組み

 G80/GT200系の命令ユニットは、2サイクルに1回、1つのWarpの1つの命令を発行できる。G80ではSMに2つの実行パイプがあり、さらにSMの外にロードストアユニットがある。それらユニット群に対する命令発行を1つの命令ユニットが行なう。前回の記事で説明したように、G80/GT200系ではSPに対する1Warp分の命令発行には4サイクルかかる。そのため、2サイクル置きに命令を発行できれば、2つのユニットに命令を発行し続けることができることになる。

G80のStreaming Processor

 下の図の中では、○が個々のスレッドに対する命令を示し、32個の○でWarpを構成している。図の中で赤い矢印で示されているのが、命令発行のタイミングだ。例えば、図の左下で、SPに対してWarp1の命令2が発行されている。この命令発行には4サイクルが必要となる。各命令の実行サイクルは実際にはもっと長いが、パイプライン化されているので、命令発行は4サイクル置きに受け付ける。

G80での2命令発行の仕組み

 命令ユニット側は、2サイクル毎に命令を発行できる。そのため、図の例では、2サイクル後に、もう1つの実行パイプであるSpecial Function Unit(SFU)に対して命令を発行している。2個のSFUは8個の乗算ユニットとして使うこともできる。図中ではWarp15の乗算命令を、これも4サイクルかけて発行している。

 SFUに対して命令を発行を始めると2サイクル後に、再びSPに命令を発行できるようになる。図ではWarp24の命令38が発行されている。このようにして、命令ユニットは、2つの演算パイプに、互い違いに命令を発行することができる。

 しかし、SFUに対して超越関数などの命令を発行する場合、SFUは2スレッドしか並列実行できない。そのため、1Warp分の命令発行に16サイクルが必要となる。その間は、SFUの実行パイプが詰まってしまうので、SPの方のパイプにしか命令発行ができない。ロード/ストアユニットへの命令発行はできるはずだが、演算は詰まってしまう。図の例では、Warp3の命令を発行した後に空きができてしまっている。実際には、この他にもレジスタ帯域など物理的な制約がある。

 GT200での命令発行は、G80の延長になる。推定される図は下の通りだ。実行ユニットとして倍精度(DP)演算ユニットが増えたが、命令ユニットは増えていない。命令発行が増えなくても問題がないのは、倍精度演算ユニットが1サイクル1命令だからだ。1Warpの命令発行に32サイクルが必要となり、その間は命令発行ができない。そのため、命令スケジューリングに対するインパクトは小さい。

GT200のStreaming Processor
GT200での2命令発行の仕組み

●SMの構造が根本から変わった

 この構造は、Fermiでどうなったのか。Fermiの演算パイプは、SPを機能拡張したCUDAコアが16個づつのグループが2個、SFUは4個のグループ、ロード/ストアユニットは16個のグループ。この4つのグループが、それぞれ並列に異なるWarpの異なる命令を実行できる。

FermiのStreaming Processor

 CUDAコアのグループは16スレッド並列を実行できるため、1Warpの1命令を、2サイクルで発行する。命令発行では、ここが大きな違いで、1Warp分の命令発行が4サイクルから2サイクルのスループットになった。もちろん、命令実行のレイテンシはこれよりずっと長いが、命令発行のスループットが上がったためレイテンシも実質半減するはずだ。しかし、外部メモリアクセスのレイテンシは同じなので、Fermiではメモリレイテンシ対策をよりしっかり行なう必要がある。これについては後の記事で説明したい。

 SFUもFermiでは4ユニットなので、1Warpの命令発行に8サイクルとなる。また、G80/GT200では、SFUはRCP/RSQ/LG2/EX2/SIN/COSといった三角関数や平方根など演算だけでなく、ムーブなども割り当てられており、乗算にも使うことができた。しかし、Fermiでは、そうした複雑な構造がなく、超越関数などを除けば、全てCUDAコアで実行できるようになった。「以前はSFUの下に隠れて乗算ユニットがあったが、FermiではSFUの中に隠された機能はもはやない。ずっとフラットな演算リソースに変わった」とNVIDIAは説明する。

 Fermiでは、命令ユニット自体は2個に増やされており、それぞれの命令ユニットが2サイクル毎に1命令を発行できる。つまり、2サイクル毎に2命令が発行されるため、G80/GT200から倍増したことになる。ただし、演算ユニットに対する命令発行のサイクルは4サイクルから2サイクルに半減した。つまり、命令発行は2倍のペースになったものの、演算パイプを充足させるために必要な命令発行スループットも2倍になったので差し引きはゼロになる。

 下の図の例では、命令発行は赤の矢印で示されている。左のCUDAコアにWarp1の命令4が発行され、それと同じサイクルに右のCUDAコアにWarp48の命令が発行されている。2グループのCUDAコアに2命令が同時発行されるパターンだ。

FermiでのWARPスケジューリングの仕組み

 次に2サイクル後に、今度は右CUDAコアにWarp2の命令が発行され、同時にSFUにWarp6の命令が発行される。2つの演算パイプに命令発行を行なうと、同時に他のユニットには命令発行ができない。だから左のCUDAコアには命令発行ができずに遊んでしまう。SFUは8サイクルの命令発行スループットなので、いったん命令が発行されると、8サイクルは命令発行ができない。

 図の例では、次に2サイクル後に、Warp22の演算命令とWarp11のロード命令が発行されている。このサイクルにもCUDAコア群には片方にしか命令発行ができない。つまり、他のリソースに命令発行する時には、2つの演算パイプの片方が空いてしまう。もっとも、実際のプログラムでは、図で示したような例よりも、CUDAコアでの演算の比率がもっと高い場合が多いと思われる。そのため、2つのCUDAコアのパイプは、もっと埋まるだろう。

●倍精度演算の場合は1命令しか発行できない

 ただし、CUDAコアでの演算でも、倍精度浮動小数点演算の場合は条件が異なる。図中の2サイクル後には、今度はWarp32の倍精度演算命令21がCUDAコアに発行されている。この場合、ディスパッチャは2命令発行ができない。片方の演算パイプにだけしか命令を発行できず、他のユニットは空いてしまう。その理由について、NVIDIAのJohn Nickolls(ジョン・ニコルズ)氏(Director of Architecture, Nvidia)らは次のように説明する。

 「CUDAコアは、それぞれ単精度と倍精度の両方の浮動小数点演算と整数演算ができる。倍精度は単精度の1/2の比率となっている。命令発行の面から見た、倍精度演算の際の、単精度との唯一の違いは、同時に2命令の発行ができないことだ。倍精度では、2つの演算ユニットにまたがって演算を行なうわけではないが、一部のリソースを共有するからだ。具体的には、この比率は、レジスタ帯域自体の比率と合致している。単精度演算、倍精度演算、整数演算、SFU、ロード/ストアの全てが、帯域を共有する。実行帯域だけなら、実際にはもっと供給できるが、(実行ユニットに対してデータを)フィードすることができない」。

 倍精度演算では、レジスタフェッチの帯域が2倍必要になる。積和算では、それぞれのコアが3ソースオペランドで64bitsのデータを必要とする。16個のCUDAコアの合計では3,072bitsのレジスタアクセスとなる。加えてディスティネーションで各64bits、計1,024bitsの書き込みがある。こうして概観すると、実は重いのはデータ帯域であり、それが最終的に命令発行の制約も作り出していることがわかる。

●ピークの演算性能は出せないがピークのIPCは達成しやすい

 このように、単純なレジスタ間演算命令だけが連続するなら、Fermiの2命令発行で2つのCUDAコアグループを常にフルにできる。しかし、実際にはそう行かないため、ある程度のアイドル状態がCUDAコアの演算ユニットにできてしまう。ピークの演算能力をフルに達成することはできないケースは多いだろう。しかし、G80/GT200世代と比べると、Fermiのモデルはリソースがフラットで命令スケジューリングはずっとしやすい。

 G80/GT200系は、演算リソースの割り当てが窮屈で、同時発行できる組み合わせが限られていた。2命令発行と言っても、自由度が限られているため、演算パイプを充填するには、かなり工夫をする必要があった。そのため、常に2命令発行を維持して、ピーク性能を出すことは難しかった。

 Fermiアーキテクチャは、G80/GT200系と比べると、演算コアの役割分担は明瞭に分かれており、包括的なCUDAコアのアレイに集中している。リソースがずっとフラットで命令発行の自由度が高い。演算性能のピークは出せなくても、2命令が発行できる余裕がある。NVIDIAは次のように説明する。

 「フラットなリソースの分配のため、ほとんどのケースで2命令を発行できる。リソースがきちんと分離されているため、ピークのIPC(Instruction-per-Clock)を達成できる。もし、2つのCUDAコアグループに命令を発行したければできる。CUDAコアとSFUにもできる。どんな組み合わせもできる。ずっと効率的になった」。

 また、Fermiアーキテクチャからは、NVIDIAの今後のアーキテクチャの拡張の方向も見えてくる。例えば、命令発行を3並列にすれば、NVIDIAは演算リソースをさらにビジーにピーク稼働させることができる。ただし、その場合には付帯して、レジスタ帯域やメモリ帯域を拡張する必要がある。また、実行パイプが埋まる分だけ、メモリアクセスレイテンシの隠蔽の圧力も高まる。そのため、マルチスレッディングのためのレジスタなどのリソースも増やす必要がある。

 逆を言えば、Fermiのベースアーキテクチャに、そうした工夫を行なえば、さらにコアの効率化を図ることができる。コアの演算リソース自体は同じでも、よりビジーに働かせることで、性能をアップすることが可能になる。ヘッドルームを持ったアーキテクチャだと言うことができる。

Fermiのダイ写真
Fermiの概要