メニーコアのキャッシュコヒーレンス

マルチコアのキャッシュコヒーレンスの説明のところでは、スヌープを全部のコアに放送(ブロードキャスト)するというやり方を説明したが、このやり方では、コア数に比例して放送の回数が増加する。しかし、メニーコアチップの場合、コア数に比例してリングやメッシュネットワークのバンド幅を増やすことは難しい。また、各コアのキャッシュから見ると、全体のコア数に比例してスヌープの頻度が上がり、メニーコアになるとスヌープが間に合わなくなってしまう。つまり、スヌープを全コアに放送してキャッシュコヒーレンシを維持するという方法はスケーラビリティーが悪く、メニーコアではうまく行かない。

輸出ディレクトリを使うキャッシュコヒーレンス

キャッシュコヒーレンスを維持するためには、本来、そのアドレスのデータをキャッシュに格納しているコアだけにスヌープを送れば良いのであるが、どのコアがアクセスされるアドレスのデータを持っているかが分からないので、すべてのコアにスヌープを送るという実装になっている。これを、対象となるデータを持っているコアだけにスヌープを送るという方法にすれば、各コアが処理するスヌープの頻度が大幅に減少する。

この方法でも、全てのコアが同じアドレスのデータをシェアしてしまうと、全コアにスヌープを送る必要があるが、平均的には、同じアドレスのデータをキャッシュに入れているコア数はそれほど多くは無い。また、キャッシュコヒーレンシを維持するシステムの総コア数が増加しても、同一アドレスのデータをシェアするコア数はそれほど増加しない。

どのコアがどのアドレスのデータをキャッシュに入れているかは、図6.3に示すように、メモリに輸出ディレクトリを設け、メモリブロック単位で、そのアドレスのデータをどこのコアに輸出したかを記憶させれば良い。このブロックの単位としてはキャッシュラインのサイズとするのが一般的であるが、複数のキャッシュラインをまとめて1つのエントリでカバーする粗いエントリとして、輸出ディレクトリに必要なメモリを節約するということも可能である。

コアからのメモリのアクセスがプライベートキャッシュをミスすると、メモリへデータを取りに行くことになるが、この時、メモリは、読み出しデータを送るとともに、輸出ディレクトリのエントリに輸出先のコア番号(あるいはコアID)を記録する。

このように輸出ディレクトリを作ればWriteの事前準備としてInvalidateを行う際には、輸出ディレクトリを参照して、過去にそのアドレスのデータを送ったことがあるコアだけにスヌープを送れば済む。また、Readの際にModifiedのキャッシュラインがあるかどうかを調べるためのスヌープも、輸出ディレクトリを見れば、どのコアにそのアドレスのキャッシュラインがあり得るかが分かる。

図6.3 メモリのブロックごとにどのコアのキャッシュにデータを輸出したかを記憶する輸出ディレクトリのエントリを設ける

輸出ディレクトリの記憶形式は、図6.4に示すように、コア数が比較的少ない場合は、ビットマップで持つという方法がある。コア数がこれより多くなると、ビットマップではディレクトリに必要なメモリが大きくなってしまうので、幾つかの輸出したコア番号のリストを記憶する欄と追加情報を設けるという方法が使われる。

図6.4 輸出情報の記録形式。全コア数のビットマップを使う形式と、幾つかのコアIDを記憶する形式がある

後者の場合は、大部分のケースではすべての輸出先が記憶できる程度の数の欄を確保しておき、輸出先の欄があふれてしまうと、メモリ上に作ったリストに輸出先のコアIDを記憶し、リストの先頭アドレスを追加情報で示すというような方法が採られる。この方法は、スヌープを送るノード番号が直接得られるというメリットがあるが、ディレクトリの管理は面倒である。一方、ビットマップの方は、ビットがセットされているノード番号をスヌープの宛先のノード番号にエンコードしてパケットに埋め込む必要がある。