Keplerの命令実行

NVIDIAのGPUは1つの命令を32個の演算ユニットで並列に実行するというやり方を取っており、これはKeplerでも踏襲されている。この32個の同一命令を3実行するグループをワープ(Warp)と呼んでいる。なお、このWarpはSFに出てくる宇宙船の空間ジャンプではなく、横糸という意味である。各コアで次々と実行されていく命令の繋がりがThread(縦糸)で、それと直交方向であるのでWarpという命名である。

このワープの32個の演算であるが、32個のコアで同時に行うのではなく、SMXの構成図で縦方向に並んだ16個のコアで2サイクルにわたって同じ命令を実行するという方法で実現されていると考えられる。

そして、各ペアは1つのワープの命令を実行し続けるのではなく、最初の2サイクルはWarp0、次はWarp10、Warp4、Warp9などと次々に異なるワープの命令を実行していく。

16コアグループでの命令実行の様子

この図ではWarp0、10、4しか書かれていないが、KeplerのSMXは最大64個のワープを、このように2サイクルごとに切り替えて実行することができる。このワープの実行順序を制御しているのがワープスケジューラである。

GK110のSMXの構成図には縦方向16個のコア列が12列と、DP Unit列が4列存在する。筆者の推測であるが、コア列とコア列のペア、あるいはコア列とDP Unit列のペアを作ると8つのペアができ、4個のワープスケジューラは2ペアずつを受け持っていると考えられる。そして、偶数サイクルにはワープスケジューラは2個のディスパッチャで第1ペアのそれぞれの列に命令を発行し、奇数サイクルには第2ペアに命令を発行するというように2つのペアに交互に命令を発行する。命令の発行は交互であるが、それぞれのペアは同じ命令を2サイクル続けて実行するので、命令が途切れてしまうことはない。

LDSTユニットを使う命令やSFUを使う命令が出てくると、ディスパッチャはその命令をコアやDP Unitではなく、LDSTユニットやSFUに送り、レジスタファイルの読み出し、書き込みポートもそれらのユニットに接続するように切り替える。LDSTユニットやSFUは2列あるので、右側の列は右側の2つのワープスケジューラを担当し、左側の列は左側の2個のワープスケジューラを担当すると考えられる。

Keplerではシャッフル命令を新設し、アトミック命令を強化

ワープによる計算の結果は、それぞれのスレッドを実行したコアに割り当てられたレジスタに格納され、同じワープの中の他のスレッドからは直接参照することはできない。これまでのGPUでは、計算結果を、一旦、シェアードメモリに書き込み、ロード命令でレジスタに読み込むことで同一ワープ内のスレッド間のデータ参照を実現していた。これに対してKeplerでは同一ワープ内のレジスタの入れ替えを行うShuffle(SHFL)命令が新設された。

新設されたSHFL命令でのレジスタ入れ替えパターンの例

このSHFL命令を使うと、他のスレッドのレジスタのデータを読むためのシェアードメモリへの書き込み、読み出しを省くことができ、実行時間を短縮することができる。SHFL命令を使うことによりFFT(Fast Fourier Transform)処理では6%性能が向上したという。

SHFL命令を使用してワープ内のレジスタの合計を求める例

また、ワープ間の同期に使用するアトミック命令はFermiでの実行速度が速かったものが2倍、遅かったものが10倍高速化された。また、演算種別として64bitのMin/Maxやand/or/xor論理演算が追加されている。従来はAtomicオペレーションは時間が掛るので、最内ループで使用するにはオーバヘッドが大きかったが、この高速化により、最内ループでも使用できる性能になったという。

Atomic命令は2~10倍の高速化と64bitのMin/Maxと論理演算を追加

Keplerのレジスタ

通常の32bit長の整数や単精度浮動小数点データはレジスタファイルから読み出されて、コアに供給される。A×B+Cの積和演算では入力として3つのデータの読み出しが必要であり、その演算結果をレジスタファイルに書き戻す必要がある。さらに1個のワープスケジューラに含まれる2つの命令ディスパッチャは同じスレッドの連続する命令を発行するので、これらの命令を実行するコアは同じグループのレジスタを使わなければならない。これらの条件を総合して考えると、Keplerのレジスタファイルは6R2W(6 Read 2 Write)構成になっており、次の図のように演算器と接続されていると推測される。

Keplerのレジスタファイルと演算コアの接続(推定)

64bit長の倍精度演算を行うDP Unitでは、2コア分の読み出しポートと書き込みポートを使用する必要がある。これを前世代のFermi GPUでは、32bitずつ2サイクル掛けて64bitを扱うという方法を取っていたが、Keplerでは、両方のディスパッチャに対応する2ポートを束ねて読み出し、書き込みを行い、DP Unitに毎サイクル、データの供給と結果の格納ができる構造になっていると考えられる。倍精度演算を行う命令を実行する場合には、ペアの相手方のレジスタポートも使ってしまうので、倍精度演算命令のペアとしてディスパッチされる命令はレジスタの読み出しポートを使わない命令である必要がある。なお、倍精度演算ではペアの相手の書き込みポートも使ってしまうが、バッファなどで書き込みタイミングをずらせてポートの使用を平準化すれば問題は解消できると思われる。

FermiのSMでは、32,768×32bitのレジスタファイルを32コアで共用していた。これがKeplerのSMXでは65,536×32bitのレジスタファイルを192コアと64DP unitの合計256ユニットで共用することになった。Keplerでは、レジスタ数は2倍でレジスタを共有するユニット数は8倍であるので、コアあたりのレジスタ数は1/4の256エントリに減少していると推測される。

一方、1つのスレッドで使えるレジスタの最大数は、FermiやGK104では63個であるが、GK110では255個に拡張されている。レジスタが足りない場合、レジスタからキャッシュやシェアードメモリにデータを追い出し、必要になったら読み込むという動作が必要となり性能が下がる。レジスタ数が多くなるとこのようなムダが少なくなり、CUDA QCD fp64プログラムの場合、性能が5.3倍アップしたという。しかし、コアあたりのレジスタ総数が256個であると、1つのスレッドで255個のレジスタを使ってしまうと、他のワープのスレッドが使えるレジスタは1個になってしまい現実的ではないと思われる。