キャッシュの実装が世代ごとに変わるNVIDIAのGPU

L1Dキャッシュを持つ初代のFermi GPUでは、グローバルやローカル変数をL1Dキャッシュに割り当てることができ、通常のデータキャッシュとして使用することができた。しかし、ハードウェア的にはSM間のL1Dキャッシュのコヒーレンスをとるハードウェアはなく、グローバルメモリ経由のコヒーレンシの実現はGPUドライバが行っていたと推定される。

このため、L1Dキャッシュをうまく使えば性能を改善することができるが、普通のCPUのL1Dキャッシュのように、あまり気にせずに使ってしまうと、グローバルメモリへの書き出しと、他のSMのグローバルメモリの読み込みが頻繁に起こって、逆に性能が低下するということが発生してしまう。

第2世代のKepler GPUでは、ハードウェア的にはL1Dキャッシュは存在するのであるが、NVIDIAは、L1Dキャッシュはコンパイラが作り出す一時変数の格納だけに使い、一般のデータの格納はさせないという動作をデフォールトに変更した。

性能の観点から、コンパイラは、出来るだけGPRレジスタファイルを使って演算を行っていくコードを作るが、各スレッドが使えるレジスタファイルエントリ数には制限があるので、ソースコードによってはレジスタ数が足りなくなることがある。このような場合は、使用中でありデータを保持する必要はあるが、すぐに使うわけではないという変数を保持しているレジスタエントリをグローバルメモリに書き出してレジスタを空けるSpill処理を行って、空いたレジスタを新たな用途に使う。そして、以前、追い出したデータが必要になると、空きレジスタがあれば、そこに退避したデータ読み込むFill処理を行う。また、空きレジスタが無ければ、どれかのレジスタをSpillして、そこにFillする。

一般にSpillされたデータは、それほど遠くない将来にFillされるので、L1DキャッシュをSpillしたデータの格納に使うのは都合が良い。また、SM1のスレッドからSpillされたデータは他のSMで使われることは有り得ない。従って、Spillデータのキャッシュへの書き込みを他のSMのキャッシュに通知する必要はない。従って、コヒーレンシ維持のためのグローバルメモリへの書き込みと他のSMがグローバルメモリを読む必要もない。用途は制限されるがコヒーレンシ維持のためにドライバが介入する必要もなく、性能の見通しも良いので、現実的な解と言える。

最近のMaxwell GPUでも、L1Dキャッシュをデフォールトではグローバル変数の格納に使わないという点は変わっていないが、ハードウェア的にはシェアードメモリと一体化して作るのではなく、テクスチャキャッシュと一体化するという実装に変更された。テクスチャキャッシュはグラフィックスでは貼り付ける壁紙のパターンを格納するメモリであるが、従来からリードオンリーのデータを格納するキャッシュとしても利用されてきた。しかし、テクスチャキャッシュと一体化したことが原因なのか、MaxwellではL1Dキャッシュはリードオンリーになり、レジスタのSpill/FillにはL2キャッシュが使われることになった。

図3-20図3-25を見比べると、シェアードメモリとキャッシュでは、行アドレスの供給方法が大きく異なる。シェアードメモリでは各バンクにアドレスデコーダが必要であるが、キャッシュの場合はバンクごとにタグマッチ回路を設けられないので、32バンク共通の1つのデコーダがあれば良い。このため、シェアードメモリとL1Dキャッシュを一体にするFermiやKeplerでは、回路的にはある程度の無駄が出ていると思われる。

これと比べると、テクスチャキャッシュはキャッシュであるので、タグマッチ回路を持っており、これを拡張してL1Dキャッシュにも利用できると思われる。また、バンクごとにデコーダを必要としないので、データメモリ部のビット密度を高めることができると考えられる。

MaxwellのSMの構成。L1キャッシュとテクスチャキャッシュが一体化している