GPUの1次キャッシュのコヒーレンシ
また、GPUはSMごとに1次キャッシュメモリを持っている。従って、1つのSM内で走るすべてのスレッドは同じ状態を見ることになり、矛盾は生じない。しかし、これをマルチコアのCPUの1次キャッシュと同じように、すべてのSMの1次キャッシュをコヒーレントに保とうとすると、他のすべてのスレッドからのスヌープを受け付ける必要がある。その時点でメモリアクセスをしようとするスレッドだけで良いが、それでもK40 GPUの場合15SMあり、各SMに32個のLoad/Storeユニットがあり、各スレッドのアクセスするアドレスはすべて異なるアドレスとなることがあり得るので、32threads×15SM=480スレッドのモリアクセスからのスヌープが1次キャッシュに押し寄せる可能性がある。
これらのスヌープを伝送するネットワーク、480アクセスを1サイクルで捌くタグなどは膨大なハードウェアを必要とし、このようなキャッシュを作るのは現実的ではない。
このため、NVIDIAのGPUでは、1次キャッシュのアクセスではスヌープを行わない。従って、SM1はストアを行って、キャッシュの内容はデバイスメモリとは異なっているが、SM2はストアを行っておらず、キャッシュの内容はデバイスメモリと同じ状態のままであるということが起こる。つまり、同じアドレスのメモリの内容がSM1とSM2では異なるという矛盾した状態が起こる。
NVIDIAのGPUでは、SM1とSM2の1次キャッシュを矛盾の無い状態にするには、ソフトウェアの介入が必要になる。図3-50の(1)に示すように、書き変えの含まれているSM1のキャッシュの内容をフラッシュしてデバイスメモリ(実際にはL2キャッシュであるが、論理的にはL2キャッシュとデバイスメモリは一体と考えて良い)に書き出す。そして、(2)で全SMの1次キャッシュの当該キャッシュラインを無効化する。
このようにすれば、他のSM(ここではSM2)が問題のアドレスをアクセスすると1次キャッシュの当該ラインは無効化されているので、キャッシュミスとなり(4)でデバイスメモリにアクセスすることになる。そして、書き変えられたSM1のキャッシュラインのデータが読み込まれることになる。そのデータが(5)でLD/STユニットに読込まれるので、矛盾の無いコヒーレンシが成り立った状態が実現される。
この方式では、1次キャッシュのアクセスに際して、他の1次キャッシュのスヌープは不要であり、多数のスヌープを可能にする膨大なハードウェアは必要ない。その代償は、1つのスレッドの書き込みの結果は、自動的には他のSMで走行するスレッドには伝わらず、書き込み結果が見えるようにするためには、プログラムの中で明示的に書きこまれた1次キャッシュの内容をフラッシュするという命令を実行する必要があるという点である。 AMDのGCNアーキテクチャのGPUでは、キャッシュライン全体に書き込みが行われると自動的フラッシュを行うという機能があるが、大してプログラミングが楽ということにはならないと思われる。
Kepler GPUは1つのメモリをシェアードメモリと1次キャッシュに分割使用
NVIDIAはFermi GPUで、SMに48KBのメモリを持たせ、16KB単位で、シェアードメモリと1次キャッシュに分割して使用できるという機構を導入した。前世代のGPUは16KBのシェアードメモリだけであったので、それを踏襲して最低16KBのシェアードメモリを持たせ、増えた32KBを1次データキャッシュとしても良いし、シェアードメモリを32KBに倍増し、16KBを1次データキャッシュとして使ってもよい。
しかし、Fermiの1次データキャッシュは、前述のように、キャッシュへの書き込みが他のSMの1次キャッシュに自動的に反映される構造にはなっていない。これではコヒーレントなキャッシュとして動作しないので、このようなケースではGPUドライバが介入してキャッシュをフラッシュするというような方法でコヒーレンシを保っていた。
論理的には、これでも正しい動作をするのであるが、ドライバが介入するのでは性能が上がらないので、NVIDIAはKepler GPUでは、1次データキャッシュはレジスタが不足して、その内容をメモリに書き出す必要があるというスピルーフィル(Spill-Fill)の場合だけに使うという用途に変更している。この使い方は使用頻度が高くキャッシュが効きやすい、一方、他のSMと同じメモリアドレスを使う必要性が無いので、ハードウェア的なコヒーレンス維持機構がなくても問題にならないという点で、うまい割り切りである。しかし、スピルーフィルだけなら、もう少しレジスタがあれば済むという話であり、一般的なデータキャッシュとして使えないのは残念という声もある。なお、C言語レベルのCUDAでは駄目であるが、抽象化アセンブラのPTXでプログラムを書けば、一般的なデータキャッシュとして使うという指定ができるようになっているようである。
なお、Maxwell GPUでは、シェアードメモリと1次データキャッシュは分離され、1次データキャッシュは描画用のテクスチャキャッシュと一体化されている。この構造にすると、シェアードメモリと1次キャッシュを同時にアクセスできるようになり、性能が上がるという。