ディレクトリベースのコヒーレンス機構

これまで述べたスヌープを使用するキャッシュコヒーレンス機構は、各プロセサが他の全プロセサにスヌープを送る。したがって、各プロセサが受け取って処理をする必要があるスヌープの回数は、システムに含まれるプロセサ(正確には、コヒーレンスを維持するキャッシュの単位であり、チップ内の全コアに共通のInclusionの3次キャッシュを持つ場合は、チップ上のコア数とは無関係にチップ数であるが、一方、Non Inclusion Cacheの場合は、チップ内の各コアのキャッシュもスヌープする必要があり形態によって異なる)に比例して増加する。結果として、コモンバスの場合は、バスを通過する要求や応答の数はプロセサ数の2乗に比例して増加する。

このため、1つのコモンバスで接続できるプロセサ数は、実用的には4個程度が上限である。コモンバスでなく、アドレスバスのインタリーブやデュプリケートタグを使い、データバスをクロスバとするなどの対策をとるとより多くの要求を捌けるようになり、64チップのSunのStarfireや、商品化された最大規模のシステムである富士通のPrimePower2500の128チップのシステムなどがあるが、スヌープ方式では、これより大きなシステムを作るのは難しい。また、前述のように、スヌープフィルタを用いてスケーラビリティを改善することができるが、やはり、スヌープをブロードキャストしてキャッシュコヒーレンシを維持する方式で、大規模なシステムを作るには限界がある。

ということで、スタンフォード大のDASHプロジェクトなどで、多数のプロセサでメモリ空間を共用する分散メモリ方式が研究された。

プロセサ数の2乗に比例して処理する要求や応答が増えてしまうのは、要求するアドレスのキャッシュラインがどのプロセサに格納されているかが不明であるので、闇雲に、全プロセサに問い合わせを送ってしまうからである。したがって、各プロセサが、要求するアドレスのキャッシュラインをどのプロセサが持っているかを知っており、それらのプロセサだけに要求を送るようにすれば良いという考えが出てきた。しかし、各プロセサが、他のすべてのプロセサがどのアドレスのキャッシュラインを持っているかを把握しようとすると、各プロセサのキャッシュ状態の変更を全プロセサに通知する必要がある。これにはプロセサ数の2乗に比例する通信が必要となってしまうので、問題の解決にはならない。

このため、ホームノードという概念を導入する。各ノードは、プロセサとメモリを持ち、自分のノードのメモリのデータがどのノードのキャッシュに保持されているかを一元的に管理するという機能を持つ。そして、あるノードのプロセサがキャッシュミスを起こしてデータが必要となると、そのアドレスのメモリが接続されたノード(これをそのアドレスのホームノードと呼ぶ)に要求を送る。

ホームノードは、要求されたアドレスのデータがどのプロセサのキャッシュにも格納されていない場合は、自分のメモリを読んでデータを要求元に送り返すと同時に、そのアドレスのキャッシュラインが要求元のプロセサに格納されているという情報を記憶する。この情報を記憶する構造をディレクトリと呼ぶ。

図9.18 ディレクトリ方式でのメモリの読み出し

プロセサP3がキャッシュミスを起こしてメモリをアクセスすると、そのアドレスからP0がホームノードであることが分かる。しかし、図9.18に示したリング構造の接続ではP3からP0への直接のパスは存在しないので、(1)でP3はP2にメモリの読み出し要求を送る。そして、P2は自分宛のメッセージではないので、(2)で読み出し要求をP0に転送する。

ホームノードのP0は、自分宛の読み出し要求を受け取ると、(3)でディレクトリ(とメモリ)を読み、そのアドレスのデータが他のノードにキャッシュされているかをチェックする。この例では、P1がそのアドレスのデータをキャッシュしていることが判明し、(4)でホームノードP0はP1に対してキャッシュラインのシェア指令を送る。また、同時に、暫定の結果という情報をつけて要求元のP3にメモリから読んだデータを送る。

このシェア指令を受けたP1は、そのキャッシュラインに書き込みが行われている場合は、キャッシュから最新のデータを読み出し、(5)で要求元のP3とホームノードのP0に送る。

要求元のP3は、(5)でホームノードのP0からの暫定のデータと、P1からの書き換え後のデータを受け取る。この場合はP1からのデータを優先して自分のキャッシュに格納する。一方、P1のキャッシュラインが書き換えられていない場合は、P1はP3にデータを送らないので、P3は、(5)でP2経由で受け取った暫定データをキャッシュに格納する。

また、P0は(5)でP1から書き換えられたデータを受け取るとメモリに書き込み、P1とP3がそのアドレスのデータをキャッシュして持っているとディレクトリに記入する。

このように、ディレクトリ方式では、必要なノードにだけ指令を送る。この方式でも、各ノードのメモリが全ノードにキャッシュされてしまうと2乗に比例する通信回数が必要になってしまうが、一般には、キャッシュされるノード数は一定の範囲に収まるので、ほぼ、プロセサ数に比例した通信回数となり、2乗に比例するブロードキャスト・スヌープ方式と比較するとスケーラビリティーが高く、多くのプロセサを持つシステムを構築できる。大規模なディレクトリ方式のシステムの例は、Silicon Graphics(SGI)のOriginサーバで、 Origin 2000システムはディレクトリ方式により、最大1024 CPUのキャッシュコヒーレントなシステムを実現している。