キャッシュコヒーレンスの問題

CPUの場合は、マルチコアのチップの各コアのキャッシュはコヒーレンスが保たれており、常に、どのコアから見ても同じアドレスのデータの値は同じである。これはその時点のメモリデータを使って、どのコアでも仕事を引き継げるし、すべてのコアにおいて、アドレスで一意にデータを指定でき、複数のコアでの分業も容易であるという大きな利点がある。

しかし、MSIプロトコルなどを使ってコヒーレンシを実現するためには、メモリへの書き込みやキャッシュをミスした読み出しに際して、他のプロセサコアのキャッシュに最新のデータがあるかどうかをチェックして、もしあれば、矛盾が起きないように処理をしてもらう必要がある。このスヌープという動作の依頼と応答のメッセージの送受の回数は、依頼を送信するスレッドの数と受信するキャッシュの数(コアの数)の積に比例する。

GK110 GPUの場合は、SMの数は15個で、これがキャッシュの数となる。スレッドの数は最大で64(Warp)×32スレッド×15(SM)=30,720であるが、コアレスでキャッシュアクセスの回数が減り、Warp数も平均的には半分程度と考えると、1500アクセス程度であろう。とすると、32Warp×1命令のメモリアクセス命令の実行で1500回のスヌープがそれぞれのSMのL1Dキャッシュに押し寄せる。32Warpの実行には50サイクル掛かるとしても、1サイクルに30回のスヌープが必要になる。1つのタグマッチ回路では1サイクルに1つのスヌープしか処理できないので、これを捌くにはタグマッチ回路を30倍に増強する必要がある。また、1500スヌープ×15(SM)=22,500個のスヌープ要求を送るネットワークも大変である。

一方、1スレッドあたり6変数分の容量という微小なキャッシュが、SM間でコヒーレントになったからと言って、どの程度、SM間の通信に寄与するかというのも疑問である。このため、GPUのSMのL1Dキャッシュは、MSIプロトコルのようなハードウェアによる厳密なコヒーレンシの維持という方法は使っていない。

NVIDIAはFermi GPUから、L1Dキャッシュを採用したが、L1Dキャッシュは(実体は1つしかないので)1つのSMで動作するスレッドの間ではコヒーレンシが保たれている。しかし、 SM1はアドレスAにD1というデータを書き込み、SM2はアドレスAにD2という値を書き込むと、同じアドレスAでも実体であるL1Dキャッシュは別々であるので、SMによって見える値が違うということが起こる。

正確にはKepler以降のGPUでは、グローバルなストアはL1Dキャッシュには書き込まれず、直接L2キャッシュに書き込まれる。しかし、SM1とSM2がアドレスAの内容をアクセスしてL1Dキャッシュに取り込み、その後、SM1がアドレスAに異なる値を書き込むと同じことが起こる。

図3-26 GPUのL1データキャッシュのコヒーレンシの実現

このため、SM1がアドレスAに書き込んだD1という値が、他のSMでも見えるようにするためには、図3-26に示すように、SM1のキャッシュのアドレスAの内容をグローバルメモリ(実際にはコヒーレンシが保たれているL2キャッシュ)に書き出し、それをSM2に読み込んでもらうという方法で、コヒーレンシを実現する必要がある。この操作はハードウェアが自動的に行うのではなく、SM1で動いているプログラムが必要に応じてグローバルメモリへの書き出しを実行し、他のSMがアドレスAをアクセスする場合には、L1Dキャッシュではなく、グローバルメモリから読み出しを行う必要があることを通知しておくという方法で実現される。

なお、AMDのGCN(Graphics Core Next)アーキテクチャのGPUでは1つのWavefrontに含まれる64スレッド全部のストア命令の実行が終わると、ハードウェアが自動的にL2キャッシュへの書き込みを行ってくれるが、コヒーレンスを実現するためのL2キャッシュからの読み出しは、ソフトウェアで記述する必要がある。

命令キャッシュ

NVIDIAのGPUもAMDのGPUも1次命令キャッシュを装備している。命令キャッシュはリードオンリーでGPUコアによる書き込みは行われないので、コヒーレンシを気にする必要はないので実装は容易である。

NVIDIAのGPUでは、L1Iキャッシュはブロック図には書かれているが、キャッシュ容量やラインサイズ、way数などの情報はまったく公開されていない。一方、AMDのGCN GPUは2011年のDeveloper Summitの発表によると、32KBの容量で、ラインサイズは64B、4way Set Associativeと発表されている。

NVIDIAのGPUもAMDのGPUも、命令読み出しがL1IキャッシュをミスするとL2キャッシュをアクセスする構造になっており、NVIDIAのKepler GPUのL1IキャッシュもAMDと同程度の16KB~64KB程度のサイズと思われる。