キャッシュ一貫性(コヒーレンシー)とは:定義とマルチコアの問題・対策
キャッシュ一貫性(コヒーレンシー)の定義からマルチコアで起きる問題と実践的対策を図解で分かりやすく解説。
キャッシュは、あるリソースにアクセスする際のパフォーマンスを向上させるために使用することができます。図のように、同じリソースに対してこのようなキャッシュが複数あると、問題が発生することがあります。キャッシュコヒーレンスまたはキャッシュコヒーレンシーとは、リソースのすべてのキャッシュが同じデータを持ち、キャッシュ内のデータが意味を持つようにするための仕組み(すなわちデータの整合性)を指します。キャッシュコヒーレンスは、より広い概念であるメモリコヒーレンス(メモリ整合性)の一部分です。
共通のメモリ資源のキャッシュが多数存在する場合、キャッシュ内のデータが意味をなさなくなったり、あるキャッシュが他のキャッシュと同じデータを持たなくなったりする問題が発生することがあります。この問題が発生する一般的なケースは、マルチプロセッシングシステムのCPUのキャッシュです。図にあるように、あるコア(トップクライアント)が以前に読み込んだメモリブロックのコピーを持っていて、別のコア(ボトムクライアント)がそのメモリブロックを変更した場合、トップクライアントは知らないうちに古い(無効な)値を参照してしまう可能性があります。キャッシュコヒーレンスの仕組みは、このような競合を管理し、キャッシュとメモリの間の一貫性を維持するためにあります。
なぜ問題になるのか(直感的な例)
- コアAが変数Xを読み取りキャッシュに置く。
- コアBが同じ変数Xを書き換える。
- コアAは自分のキャッシュに古い値が残っているため、新しい値を見ないまま処理を続ける可能性がある。
このような状況は、プログラムの正しさ(特に並列処理での同期)を損ね、バグや予期しない振る舞いを生じさせます。したがって、ハードウェアとソフトウェアの両方で対策が必要です。
代表的なキャッシュコヒーレンスプロトコル
キャッシュコヒーレンスを保つために、ハードウェアは様々なプロトコルを使います。代表的なもの:
- MSI(Modified, Shared, Invalid)— 基本的な3状態プロトコル。書き込み可能な状態(Modified)、共有状態(Shared)、無効状態(Invalid)を持つ。
- MESI(Modified, Exclusive, Shared, Invalid)— MSIにExclusive状態を加え、読み込みの最適化を行う。多くの商用プロセッサで採用。
- MOESIなど派生形— Ownership状態などを追加して、コア間でのデータ転送を最適化する。
これらのプロトコルは、各キャッシュラインの状態を管理し、読み書きの際にどのようなアクション(例:他キャッシュへの無効化通知、更新のブロードキャスト)を行うかを決めます。
書き込みの処理方式:Write-Invalidate と Write-Update
- Write-Invalidate:あるコアがデータを書き換える際、他のコアの該当キャッシュラインを「無効化」する。最も一般的でネットワークトラフィックを抑えやすい。
- Write-Update(Write-Broadcast):書き換えた新しい値を他コアに送って更新させる。頻繁な書き込みが共有されるケースでは有利だが、更新のオーバーヘッドが大きくなる。
プロトコルの実装方式:Snooping と Directory ベース
- Snooping(スヌーピング)方式:バス上のトランザクションを各キャッシュが監視(snoop)して状態を更新する。バスが共有される小〜中規模のシステムで有効。
- Directory(ディレクトリ)方式:各メモリブロックに対してどのキャッシュがコピーを持っているかを管理するディレクトリを用いる。大規模・分散共有メモリ(NUMAなど)でスケーラブル。
よくある問題:フォールスシェアリング(False Sharing)
フォールスシェアリングは、異なるスレッドが独立した変数AとBを更新しているにもかかわらず、それらが同じキャッシュラインに格納されているために無駄な無効化や転送が発生する現象です。これにより性能が大幅に低下することがあります。対策としては、データの配置(パディングやアラインメント)やスレッド設計の見直しが有効です。
キャッシュコヒーレンスとメモリ一貫性(Consistency)の違い
コヒーレンスは単一のメモリ位置(アドレス)に着目し、その位置を読み書きする全てのプロセッサが同じ順序でその位置の更新を観測できることを保証します。一方、メモリ一貫性(メモリコンシステンシーモデル)は複数のメモリ位置にまたがる操作の順序保証(例えばリード/ライトの順序)について定義します。実用上、両方が正しく動作することで並列プログラムの正当性が保たれます。
ハードウェア・ソフトウェアでの対策とプログラミング上の注意点
- ハードウェア:上記のコヒーレンスプロトコル、インターコネクト設計(リング、メッシュ、専用コヒーレンスコントローラ)で整合性を保つ。
- OS/ランタイム:ページの配置やスケジューリングで局所性を高め、コヒーレンス負荷を軽減する。
- プログラマ/ライブラリ:
- 原子操作(atomic)やメモリフェンス(memory barrier)を正しく使う。
- 共有データの粒度を見直し、ロックの粒度やスレッド間のデータ分離を行う。
- フォールスシェアリングを避けるために構造体のパディングやアラインメントを調整する。
- 高レベル並列ライブラリ(スレッドプール、並列アルゴリズム)を利用して誤りを減らす。
設計上のトレードオフ
- 一貫性を厳密に保つほど通信オーバーヘッドやレイテンシが増える。
- 小〜中規模ではスヌーピング+Write-Invalidateが有効だが、大規模クラスタや分散共有メモリではディレクトリ方式やソフトウェアベースの調整が必要。
- 性能最適化とプログラムの単純さ(正しさ)はしばしばトレードオフになるため、設計時に優先度を明確にする必要がある。
まとめ
キャッシュコヒーレンスは、マルチコアやマルチプロセッサ環境でデータの正しさを保つための重要な仕組みです。ハードウェアレベルのプロトコル(MSI/MESIなど)やスヌーピング/ディレクトリ方式、さらにソフトウェア側の設計(同期、データ配置、原子操作)が組み合わさって機能します。並列アプリケーションの性能最適化では、コヒーレンスオーバーヘッドやフォールスシェアリングを意識したデータ設計と同期手法の選択が重要です。

共有資源のマルチキャッシュ
定義
コヒーレントとは、同じメモリ位置への読み出しと書き込みの動作を定義するものです。以下の条件がすべて満たされていれば、キャッシュはコヒーレントです。
- プロセッサPが位置Xを読み出すとき、その位置に書き込んだ後、他のプロセッサがその位置に別の値を書き込まなければ、Pは自分が書き込んだ値を取得しなければなりません。これはモノプロセッサシステムでも同じで、メモリが書き込んだ値を保持できることを意味します。
- P1とPという2つのプロセッサ2があり、P1が値X1を書き込み、その後、P2が値X2を書き込んだとすると、P1が値を読み取る場合、その間に他の書き込みがなければ1、P2が書き込んだ値X2ではなく、自分が書き込んだ値Xを取得しなければなりません。これは、メモリの見方がコヒーレントであることを意味します。もしプロセッサが、Pが行った書き込みの後に、同じ古い値を読むことができる2ならば、記憶は首尾一貫していないことになる。
- メモリの特定の場所への書き込みは、一度に1回しかできません。複数の書き込みがある場合は、それらが次々と行われなければなりません。つまり、ある2つのプロセッサが、位置Xに2つの異なる値AとBをこの順番で受け取った場合、プロセッサは位置XをBと読んだ後、Aと読むことはできません。
これらの条件は、読み出しと書き込みが瞬時に行われると仮定して定義されています。しかし、コンピュータのハードウェアでは、メモリのレイテンシやその他のアーキテクチャの観点から、このようなことは起こりません。プロセッサXによる書き込みが行われた後、ごくわずかな時間内にプロセッサYからの読み出しが行われた場合、プロセッサXの書き込みがプロセッサYの読み出しによって確認されない可能性があります。メモリ一貫性モデルでは、書き込まれた値が、他のプロセッサが行う次の読み取り命令によって見られるべきタイミングを定義します。
キャッシュコヒーレンス機構
- ディレクトリベースのコヒーレンスメカニズムは、キャッシュされたブロックの中央ディレクトリを維持します。
- スヌーピングとは、各キャッシュがアドレスラインを監視して、自分のキャッシュ内にあるメモリロケーションへのアクセスを監視するプロセスです。あるキャッシュがコピーを持っている場所への書き込み操作が観測された場合、キャッシュコントローラは、スヌープされたメモリロケーションの自分のコピーを無効にする。
- スナーフィングとは、キャッシュコントローラーがアドレスとデータの両方を監視し、セカンドマスターがメインメモリーのロケーションを変更したときに、自分のメモリーロケーションのコピーを更新しようとすることです。
分散型共有メモリシステムは、これらのメカニズムを模倣することで、疎結合システムのメモリブロック間の一貫性を維持することができます。
一般的に研究されているコヒーレンスのタイプは、スヌーピングとディレクトリベースの2つです。それぞれにメリットとデメリットがあります。スヌーピングプロトコルは、十分な帯域幅があれば、すべてのトランザクションがすべてのプロセッサで見られるリクエスト/レスポンスであるため、高速になる傾向があります。欠点は、スヌーピングはスケーラブルではないことです。すべてのリクエストは、システム内のすべてのノードにブロードキャストする必要があります。システムが大きくなると、(論理的または物理的)バスのサイズとそれが提供する帯域幅も大きくならざるを得ません。一方、ディレクトリは、(3ホップのリクエスト/フォワード/レスポンスで)レイテンシが長くなる傾向がありますが、メッセージはポイント・ツー・ポイントであり、ブロードキャストではないため、使用する帯域幅は非常に少なくなります。このため、大規模なシステム(64プロセッサ以上)の多くは、このタイプのキャッシュコヒーレンスを使用しています。
質問と回答
Q:キャッシュコヒーレンスとは何ですか?
A:キャッシュコヒーレンスとは、あるリソースのすべてのキャッシュが同じデータを持ち、キャッシュ内のデータが一貫していること(データインテグリティ)のことです。
Q: キャッシュコヒーレンスの目的は何ですか?
A: キャッシュコヒーレンスの目的は、共通のメモリリソースの複数のキャッシュ間の競合を管理し、キャッシュとメモリ間の整合性を維持することです。
Q: キャッシュコヒーレンシがない場合、どのような影響が考えられますか?
A: キャッシュコヒーレンシがないと、キャッシュ内のデータが意味をなさなくなったり、あるキャッシュが他のキャッシュと同じデータを持たなくなったりして、不整合やエラーが発生する可能性があります。
Q:キャッシュコヒーレンスに問題が発生する一般的なケースは何ですか?
A:キャッシュコヒーレンシの問題が発生する一般的なケースは、マルチプロセッシングシステムにおけるCPUのキャッシュです。
Q: キャッシュコヒーレンスはどのように機能するのですか?
A: キャッシュ・コヒーレンスは、あるリソースのすべてのキャッシュが同じデータを持ち、様々な方法でキャッシュ内のデータが一貫していることを保証することで機能します。
Q:メモリコヒーレンスとはどういう意味ですか?
A: メモリコヒーレンスとは、共有メモリリソース全体のデータの一貫性を意味します。
Q: キャッシュ・コヒーレンスはどのようにパフォーマンスを向上させることができますか?
A: キャッシュ・コヒーレンスは、与えられたリソースへのアクセスをより速く、より効率的にすることでパフォーマンスを向上させることができます。
百科事典を検索する