web-dev-qa-db-ja.com

x86 / x86-64 CPU L1 / L2 / L3キャッシュとRAM

一般的な意味で、L1/L2(および現在はL3キャッシュ)が更新される方法と、マルチコアx86/x86-64 CPUで更新が伝播される方法を理解しようとしています。

4コアCPUと2ペアのL1/L2キャッシュを想定します。コアのペアは共通のL1/L2ペアを共有し、2つのL1/L2ペア間に相互接続があります。また、キャッシュラインは64ビット幅です。だから私たちは:

  1. (L1/L2)-0上のCore-0/Core-1
  2. (L1/L2)-1上のCore-2/Core-3
  3. (L1/L2)-0は(L1/L2)-1に接続されています

Xと呼ばれる64ビット整数変数に書き込むコア0で実行されているスレッドT0があり、コア3に変数Xの値を継続的に読み取る別のスレッドT1があるとします。論理的な競合状態はしばらく無視してください。

質問: XがCore-0のL1にキャッシュされていると仮定します。 T0がXに新しい値を書き込むとき、次の一連のイベントは正しいですか?

  1. Xの値がレジスタからL1-0にプッシュされます
  2. Xの値はL1-0からL2-0にプッシュされます
  3. Xの値はL2-0からL2-1にプッシュされます
  4. Xの値はL2-1からL1-1にプッシュされます
  5. Xの値はL1-0からRAMにプッシュされます

注:手順2と4は同時に実行される場合があります。

9
Giles Ramoni

まず、最近の(Nahalem以降5〜10年)Intel x86アーキテクチャが気になる場合は、キャッシュの説明が少しずれています。

各コアには、独自の128K L1キャッシュスプリット(64Kデータ/ 64Kコード)があります。その上に、各コアには独自のL2キャッシュがあり、基本的にL1とL3キャッシュの間のバッファーとして機能します。各ソケットには独自のL3キャッシュがあります(最大20MBだと思います)。 L1およびL2キャッシュは小さく、シンプルで、高速です。 L3キャッシュははるかに大きく複雑です(メモリコントローラーや他のソケットへのQPIブリッジなどの他のものと一緒にリング上にある小さなセグメントに分割されます)。 (ロードバッファーやストアバッファーなど、これをさらに複雑にするものは無視してください。)

また、キャッシュラインは64バイトです。

したがって、次のようになります(たとえば、CPUあたり4コアのデュアルソケットマシン上)。

Core 0 (socket 0) -> L1-0 -> L2-0 -> L3-0 -> QPI link to socket 1
Core 1 (socket 0) -> L1-1 -> L2-1 -> ^
Core 2 (socket 0) -> L1-2 -> L2-2 -> |
Core 3 (socket 0) -> L1-3 -> L2-3 -> +

Core 4 (socket 1) -> L1-4 -> L2-4 -> L3-1 -> QPI link to socket 0
Core 5 (socket 1) -> L1-5 -> L2-5 -> ^
Core 6 (socket 1) -> L1-6 -> L2-6 -> |
Core 7 (socket 1) -> L1-7 -> L2-7 -> +

ナハレム以前は、L2キャッシュはコアペア間で共有されていました。しばらくの間、多くのパフォーマンス作業を行う必要がなかったので、そこでの微妙さは本当にわかりません。

L3キャッシュには、その下のL1およびL2キャッシュが完全に含まれています。キャッシュには、すべてのメモリアドレスの「正しい」値が含まれています。

メインメモリよりも正確です。書き込みは、メモリ(ライトバックキャッシュ)に移動する前に、しばらくL3にとどまることができるためです。すべてのキャッシュは一貫しています。つまり、同じメモリ位置に対して2つの異なる値が存在することはありません。この一貫性は [〜#〜] mesif [〜#〜] と呼ばれるMESIプロトコルのバージョンによって維持されます(Intelの場合、AMDは異なるキャッシュ戦略を持ち、MEOSIを使用し、それらのキャッシュを異なる方法で配置します) 。

L1とL2はコア専用であるため、コヒーレンスはL3レベルでのみ管理する必要があります(これについて明確な答えを得ることができなかったと思います)。キャッシュ相互接続には4つのレーンがあります。データ、リクエスト、確認、およびスヌープです(他のメモリ操作を最新に保つため)。

今、私たちはあなたの質問に取り掛かることができます。

コア0のスレッドがアドレスを読み取っている場合、アドレスは、排他的、共有、または転送のいずれかの状態でL1-0およびL3-0に存在します(3つすべてがアドレスが変更されておらず、キャッシュされていることを示します)。今、Core-4はそれに書き込みたいと考えています。 Request/Read-For-Ownershipは、他のL3キャッシュ(L3-0)からキャッシュラインを送信し、他のキャッシュにコピーを無効としてマークさせます。 L1-4とL3-1(Exclusiveとマークされている)になります。

(ここで、ストアバッファーを無視すると、大幅に簡略化されます。)

Core-4は、レジスタからL1-4キャッシュに書き込みます。行をModified状態に遷移させます。これはL3-1キャッシュに伝播されます(完全に包括的であるため)。

今、Core-0はもう一度読みたいと考えています。 L1-0キャッシュはそのアドレスでは無効であるため、L3-0キャッシュをミスした読み取り要求を送信し、L3-1にキャッシュラインを送信させます。 L3-1はアドレスが共有されていることを示し、L3-0は回線をForwardとして保持します(最新のリクエスタがForwardingの責任を負います)。

泥だらけ?私が漠然としすぎているかもしれない言語の一部をクリーンアップするために、これにいくつかの編集があるかもしれません。

17
JasonN