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

NVIDIA Fermiのマルチスレッディングアーキテクチャ



●レジスタモンスターから変わるNVIDIA GPU

 GPUはレジスタモンスターだ。近年のGPUは、膨大な数のレジスタを内部に備える。例えば、NVIDIAのGeForce GTX 280(GT200)は、GPU全体で491,520本の32-bitレジスタを持つ。チップ全体で見るとレジスタの量(データ領域)は1,920KB。PC向けCPUのL2キャッシュ並の量だ。CPUがレジスタよりはるかに多くのキャッシュを搭載するのとは逆に、GPUはメモリよりはるかに多くのレジスタを搭載するのが一般的だ。

 理由は簡単で、GPUではマルチスレッディングでメモリレイテンシを隠蔽することが有効だったからだ。GPUもCPUと同じでオフチップのDRAMメモリへのアクセスのレイテンシが非常に長い。NVIDIA GPUの場合は、一回アクセスすると最低でも200サイクル、通常、400~600サイクルもの間演算コアが待たされてしまう。そのため、CPUもGPUも、メモリレイテンシを隠蔽して、演算コアを稼働させ続けないと、パフォーマンスが出ない。

 この問題を、CPUはキャッシュメモリを膨大に搭載して、再利用するデータや命令をチップ内に載せてしまうことで解決している。一般的なCPUのプログラムでは、データや命令の局所性が高いケースが多いからだ。

 しかし、グラフィックス処理では、依存性のない膨大な数の並列実行可能なタスクがある。そのため、GPUでは、メモリのレイテンシは、キャッシュよりむしろスレッドを切り替えることで隠蔽してきた。膨大な数のスレッドが並列実行できれば、あるスレッドがメモリアクセスを待つ間に、別なスレッドを走らせて、レイテンシを隠蔽できるからだ。そのためには、切り替えるスレッド分のレジスタを物理的に備えなければならない。GPUは数万のスレッドをインフライトで制御するために、数十万のレジスタを必要としている。

 しかし、NVIDIAの次世代GPUアーキテクチャ「Fermi(フェルミ)」には、こうした従来のGPUのセオリーが当てはまらない。NVIDIAは、GT200からFermiへとアーキテクチャを進化させるに当たって、チップトータルで見たレジスタの量はほぼ増やさないかった。その代わり、シェアードメモリを大きく増やし、キャッシュを新設した。スレッドを切り替えるよりも、オンチップのメモリを使って、外部メモリへのアクセスを減らすことで、メモリレイテンシの問題を解決する。それがFermiで示された、NVIDIA GPUの進化の方向性だ。

 この思想は、GPUの命令&スレッド制御にも明瞭に現れている。GPU全体で立ち上げられるスレッドの数は、GT200世代では30,720スレッドだったのが、Fermiでは24,576スレッドと逆に減っている。Fermiでは演算リソースは増えて、演算パフォーマンスが上がっているので、相対的にメモリレイテンシ対策を強化しなければならないはず。なのに、立ち上げられるスレッド数の上限を減らす。ここに、Fermiの設計思想が明確に現れている。

Fermiの概要

●GPUのレジスタ数とマルチスレッディングの関係

 ラフに言うと、GeForce 8800(G80)やGT200のアーキテクチャは、1スレッドが使うレジスタ数が少なく、メモリレイテンシの隠蔽は主に膨大な数のスレッドの切り替えで行なうアプリケーションに最適化されていた。これに最も当てはまるのは、伝統的なグラフィックスタスクだ。

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

 つまり、NVIDIAはFermi系で、内部のマルチスレッディングやメモリの構造を、より汎用アプリケーションに最適な形で再編したことになる。

 これをもう少し詳しく説明すると次のようになる。GPUでは、通常のCPUのように、スレッド当たりのレジスタ数の割り当ては固定されていない。動的にスレッドに、物理的なレジスタ数が割り当てられる。しかし、GPUが備える物理的なレジスタ数は決まっている。そのため、スレッド当たりのレジスタ数と、立ち上げることができるスレッド数にはトレードオフが生じる。立ち上げるスレッド数が増えれば、1スレッドが使えるレジスタ数が減る。

 NVIDIA GPUでは、32スレッドずつ束ねたスレッドバッチ「Warp」を、ベクタプロセッサとしての実行単位としている。NVIDIA GPUではプロセッサクラスタである「Streaming Multiprocessor(SM)」毎に、Warpを制御している。SMの中で、何個までのWarpを立ち上げられるかは、GPUアーキテクチャ世代によって異なる。

 また、SMの中のレジスタの量もGPUアーキテクチャ世代で異なる。そのため、同じスレッド当たりレジスタを使うカーネルプログラムを走らせた場合でも、何個のWarpを立ち上げられるかは、GPU世代によって変わってくる。レジスタリソースが少ないと、レジスタの割り当ての制約「レジスタプレッシャ(Register Pressure)」となり、立ち上げられるWarpが減って、場合によってはストールが生じてしまう。

Fermiのダイ
G80のWarp実行の仕組み

●マルチスレッディングでメモリレイテンシを隠蔽しやすいG80/GT200

 G80/GT200では、SMに演算コアである「Streaming Processor(SP)」が8個搭載されており、8スレッド並列で処理する。そのため、SPパイプは4サイクルでWarp 1つ分の32スレッドの命令を発行する。SPの他にSpecial Function Unit(SFU)と共有の乗算ユニットのパイプもあり、こちらも8スレッド並列で4サイクルで1 Warpの命令を発行できる。

G80のStreaming Multiprocessor(SM)
G80の2命令発行の仕組み

 G80では、SM当たりのレジスタ数は8,192本だった。そのため、32レジスタを各スレッドに割り当てると、立ち上げることができるWarp数はたった8個、スレッド数は256スレッドとなる。

G80のWarpとレジスタ

 ここで、プログラム中のメモリロード命令の後に、ロードに依存しない命令が平均で3命令続くとする。実際にはもっと間隔が開く可能性が高いが、最悪のケースで3命令分しかロードを前に出せないと想定する。32レジスタ/スレッドの場合は、8個のWarpがそれぞれ4サイクルで命令発行を行ない、4命令間隔で依存命令が来るとしたら、平均で128サイクルのメモリレイテンシしか隠蔽できない。これはSPの演算パイプだけの計算で、乗算(MUL)ユニットがもし並列で動けばさらに短縮される。つまり、隠蔽できるサイクルは、単純化すると、4サイクル×立ち上がっているWarp数÷IPC(Instruction-per-Clock)×依存命令間隔となる。ただし、G80の時は、2命令発行は、かなり限定されていた。

G80のWarpスケジュールとメモリの関係

 G80の場合は、SM内で立ち上げることができる最大のアクティブWarp数は24 Warp。24 Warpを確保すると、4命令間隔でロード依存の命令が来るとしても平均で384サイクルも隠蔽できるようになる。しかし、その場合、G80ではスレッド当たりのレジスタ数は10本になってしまう。x86並の窮屈なレジスタで、G80のネイティブ命令が、レジスタ間演算命令中心であることを考えるとこれは苦しい。実際に汎用コンピューティングでは、上限の24Warpは立ち上がらないケースがほとんどだと言う。

 GT200では、SM当たりのレジスタ本数が16,384本と2倍になり、インフライトで制御できる最大Warp数も32 Warpに増えた。1スレッド32レジスタを割り当てた場合も、16 Warpを立ち上げられる。GT200では、ロード命令の後にロードに依存する命令が4命令間隔で現れるとして、32レジスタ/スレッド時に256サイクルを隠蔽できる計算になる。最大の32 Warpの場合には、16レジスタ/スレッドで、512サイクルを隠蔽可能だ。こちらも、乗算パイプへの並列命令発行が順当に行けば、さらにサイクルは減る。

 こうしたG80/GT200のアーキテクチャを見ると、基本の発想がわかる。まずG80では、グラフィックスのように1スレッド当たりのレジスタ数が少ない場合に、多数のWarpによるマルチスレッディングでメモリレイテンシを隠蔽しやすい。しかし、プログラムの使うレジスタ本数が増えると、マルチスレッディングだけではメモリレイテンシを隠蔽しきれなくなる。GT200ではそれが緩和されたが、基本の発想は、まだG80の延長にあった。

GT200の2命令発行の仕組み
GT200でのWarpとスレッディング
GT200のSM

●Fermiでは32レジスタ/スレッドで32Warpを立ち上げ可能に

 ではFermiではどうなのか。Fermiの場合は、SMの中に演算コアであるCUDAコアが32個あり、16個ずつのグループに分かれている。2グループがピークで、2サイクルで2個のWarpを並列実行できる。他にSpecial Function Unit(SFU)があるが、G80/GT200の時のような乗算だけのパイプはない。各SMでは、最大で48 Warpをインフライトでスケジュール可能だ。レジスタは128KBで、32-bit 32,768個となる。どの数字も、GT200より増えているため、一見いいように見える。

FermiのSM

 実際、3世代のGPUアーキテクチャの、スレッド当たりのレジスタ数とWarp数をグラフ化してみると、世代によって着実に伸びていることがわかる。下のグラフの横軸がWarp数、縦軸がスレッド当たりのレジスタ数だ。G80では32レジスタ/スレッドで立ち上げられるWarp数は8 Warpだったが、GT200では16 Warpに、そしてFermiでは32 Warpへと倍々に増えている。

Warpとレジスタの関係
FermiのWarpとスレッディング

 しかし、実際のFermiの実行形態を考えると、これでメモリレイテンシの隠蔽が伸びるわけではない。演算リソースが増えているからだ。まず、Fermiでは、演算コアは16並列のCUDAコアが、倍精度演算以外は制約なく並列に稼働できる。さらに、1 Warpの命令発行は4サイクルではなく2サイクルで行なわれる。つまり、演算の並列性は2倍かそれ以上に増え、命令発行のレイテンシのサイクルは1/2に減っている。

FermiのWarpスケジュール

 そのため、マルチスレッディングだけでメモリレイテンシを隠蔽しようとすると、Fermiでは、より多くのWarpを立ち上げなければならない計算になる。例えば、32レジスタ/スレッドの場合、32 Warpが立ち上がるが、32 Warpがあっても、演算パイプが2並列でフルに稼働したとすれば128サイクルしか隠蔽できない。下の図のように、Fermiの場合、2サイクル置きに、ほぼ2命令を発行できてしまうので、隠蔽できる時間が短くなる。単純に言うと、Fermiでのマルチスレッディングによるメモリレイテンシ隠蔽は、理論上はG80と近い程度のレベルになっている。

FermiのWarpスケジュールとメモリレイテンシ

 さらに、Fermiでは倍精度浮動小数点演算の性能が極めて高いため、倍精度中心のアプリケーションも出てくる可能性がある。倍精度演算では32-bitレジスタを2個使うため、スレッド当たりのレジスタ割当数は実質半減する。ただし、倍精度演算では下の図のように命令発行サイクルが半分に落ちるため、単精度演算の場合とメモリレイテンシ隠蔽は同レベルになる。このあたりは、微妙にバランスが取れている。

Fermiにおける倍精度演算時のWarpスケジュールとメモリレイテンシ

 いずれにせよ、Fermiでは、マルチスレッディングだけにメモリレイテンシの隠蔽を頼ると、性能が出にくいことが予想できる。

●設計フィロソフィが変わったFermiのマルチスレッディング

 しかし、これを別な側面から見ると、この件はFermiの欠陥などではなく、Fermiアーキテクチャの当然の選択であることがわかって来る。なぜなら、Fermiではメモリ階層が深くなり、レジスタ以外の内部メモリの量が大きく増えているからだ。NVIDIA GPUの各世代の実行リソースとメモリ量、マルチスレッディング機能を示したのが下の図だ。

NVIDIA GPUの各世代の実行リソースとメモリ量、マルチスレッディングの推移

 Fermiでは、GPU全体の積和算の演算コアはG80から4倍に、GT200からは2倍以上に増えた。しかし、マルチスレッディングで制御できるWarp数は、GPU全体で見るとG80の2倍程度にしか増えていない。GT200と比べると、GPU全体で960 Warpから768 Warpへと、むしろ減っている。Fermiのレジスタリソースは2MBで、1.9MBだったGT200とほぼ同じに留まる。

 ところが、レジスタと外部メモリの間のメモリ階層についてはFermiで大幅に強化されている。例えば、GT200では、シェアードメモリは480KBとレジスタの1/4しかなく、L2キャッシュも、リードオンリでプロセッサの演算結果をキャッシュすることができなかった。それに対してFermiでは、L2キャッシュはリード&ライト可能なキャッシュとなり、演算コアの処理結果を他のコアで再利用することができる。L1キャッシュも同様だ。

 こうしたFermiの合計のオンチップメモリの量は、レジスタメモリの量に迫る。そのため、オンチップで演算結果を再利用する「プロデューサ-コンシューマ局所性(Producer-Consumer Locality)」の活用と「ローカルワークセットアップデイト(Local Working-set Update)」が、より効率的に行なえる。

 インフライトで立ち上げることができるWarp数をGT200より減らしたことも、この流れに沿っている。そもそも、汎用的なGPUコンピューティングアプリケーションの多くでは、レジスタプレッシャがあるため、これまでもG80/GT200の上限のWarp数までは立ち上げることはできなかった。Fermiでは、そうした汎用アプリケーションの状況に合わせて、上限のWarp数を演算リソースに対して相対的に減らしたと見られる。

 明白なのは、NVIDIAはFermiでは、マルチスレッディングのリソースを強化するのをとりあえず止めたことだ。GPUに搭載する新たなSRAMを、レジスタではなく、シェアードメモリやキャッシュに振り分けた。メモリ階層を厚くし、レイテンシの短いオンチップメモリを活用させることで、効率を上げようとしている。

●NVIDIAの選択はメモリ階層の改良

 Fermiは、プログラムから明示的に使うシェアードメモリだけでなく、ハードウェアで制御するキャッシュも2階層備える。そのため、シェアードメモリを使いにくいアプリケーションタイプでも、データの局所性の活用がオンチップでできるようになった。シェアードメモリとキャッシュの2本立ては、どっちつかずとも見えるが、GPUの汎用性を高めるためには必要だと判断したという。例えば、レイトレーシングでは、レイのトレースでどの座標を参照するか事前に予測しにくいため、ハードウェアキャッシュが有効となる。

Fermiのメモリ階層

 こうしたFermiアーキテクチャは、伝統的なグラフィックスのようにデータをストリームで処理してメモリにはき出すアーキテクチャでは、有利とは言えない。伝統的なグラフィックスパイプの効率だけを考えるなら、シェアードメモリとL1/L2キャッシュに増やした分のSRAMをレジスタに割り当てて、マルチスレッディングで立ち上げられるWarp数を増やした方が有効なケースが多いと予想されるからだ。例えば、レジスタを3.7MBに増やして、立ち上げられるWarp数の上限を2,000程度にすれば、その方が旧来のグラフィックスパイプでは高速になった可能性がある。

 しかし、そのアプローチを続けても、レジスタ使用量の多い汎用アプリケーションでの性能を上げ続けるのが難しいのも確かだ。プロセス技術の進化とともにコンピューティング性能を上げて行くGPUにとって、相対的に低速なオフチップメモリへのアクセスは今後も最大のボトルネックとなる。それを吸収するには、メモリ階層を深くする方が得策とNVIDIAは判断したようだ。