アドレス変換キャッシュの階層(TLB・PWC・ASID)
アドレス変換はなぜ遅延の支配要因にならないのか。L1/L2 TLBとページウォークキャッシュ、PCIDによるフラッシュ回避、シュートダウンのIPIコストを原理から押さえ、性能の勘所を掴めます。
- 1.TLBはL1(小・高速)とL2(大)の階層を持ち、ミス時もページウォークキャッシュ(PWC)が上位段の物理ベースをキャッシュして実メモリ参照回数を削る。
- 2.x86のPCIDは各TLBエントリにアドレス空間IDを付け、文脈切替でCR3を切り替えても全フラッシュを回避する。ただしKPTI併用時はカーネル/ユーザーで別IDが要る。
- 3.他コアのTLBを揃えるシュートダウンはIPIで全該当コアに割り込むため、コア数に対してほぼ線形にコストが伸びる同期点になる。
変換キャッシュは1段ではない
MMUとTLBの内部で見たとおり、TLB は「仮想ページ番号→物理フレーム番号+属性」をキャッシュし、ヒットすればテーブルウォークを丸ごと省きます。しかし現代の CPU では、変換に関わるキャッシュは TLB 1 段ではなく、複数の階層で構成されています。なぜ階層化するのかを押さえると、ミス時のコストが「最悪 4〜5 回のメモリ参照」よりずっと小さい理由が見えてきます。
連想メモリ(CAM)は全エントリを並列照合するため、容量を増やすほど面積と遅延が増えます。そこで容量と速度を両立させるため、TLB も命令/データキャッシュと同じく階層化されます。
| 階層 | 典型容量 | 特徴 |
|---|---|---|
| L1 ITLB / DTLB | 数十〜百数十エントリ | 命令用とデータ用に分離。1サイクル級で引ける |
| L2 STLB(統合) | 千〜数千エントリ | L1ミスを受ける。命令とデータを統合し容量重視 |
| ページウォークキャッシュ | 数十エントリ | 上位段(PML4/PDPT/PD)の物理ベースを保持 |
L1 TLB を引いてミスすると、まず L2(STLB)を引きます。ここでヒットすればウォークは不要です。L2 でもミスして初めてページウォークが走りますが、その時も次の PWC が効きます。
ページウォークキャッシュ:ウォークの途中結果を残す
ページウォークキャッシュ(PWC。Intel では Paging-Structure Caches)は、TLB とは別物です。TLB が「最終的な変換結果(葉)」をキャッシュするのに対し、PWC はウォーク途中の上位段の物理ベースをキャッシュします。
4レベルウォーク(PWCなし): CR3 → PML4読 → PDPT読 → PD読 → PT読 = 4回の物理参照
PWCヒット(PD段までキャッシュ済): (PML4/PDPT/PDを省略)→ PT読 = 1回
近接したアドレスは上位段(PML4・PDPT・PD のインデックス)を共有することが多く、PWC が効けば最後の数段だけ読めば済みます。同じ 2MB 領域内のページをウォークするなら、上位 3 段は同一エントリを指すため PWC ヒットが続き、実メモリ参照は最下段の 1 回に縮みます。これが「TLB ミス=常に最悪 4 回」とはならない理由です。
TLB エントリは葉の変換、PWC エントリは中間段の物理ベースを保持します。中間段のページテーブルを書き換えたのに PWC が古いベースを返すと、葉だけ無効化しても誤った下位表を読み続けます。x86 の invlpg は指定アドレスに関わる TLB と PWC の両方を無効化し、この不整合を防ぎます。
PCID:文脈切替で全フラッシュを避ける
コンテキストスイッチで CR3 を別プロセスのページテーブルに切り替えると、本来は前プロセスの TLB エントリが次プロセスに誤適用されないよう、TLB 全体をフラッシュする必要があります。x86 の PCID(Process Context Identifier) は、各 TLB エントリに 12bit のアドレス空間 ID を付与し、これを回避します。
CR4 の PCIDE ビットで機能を有効化すると、CR3 の下位 12bit が PCID フィールドとして解釈されます。TLB の照合条件は次のようになります。
ヒット条件: エントリのPCID == 現在のPCID(CR3下位12bit)
かつ エントリの仮想ページ番号 == 参照ページ番号
PCID が一致しないエントリはヒット扱いされないため、複数プロセスの変換が TLB に混在しても誤適用は起きません。さらに CR3 のロード時に最上位ビット(bit 63)を立てると、「この PCID の既存エントリをフラッシュしない」ことを CPU に指示でき、戻ってきたプロセスの残存エントリをそのまま再利用できます。これが切替コストを下げる核心です。
PCID は 4096 個(0〜4095)しかありません。実プロセス数がこれを超えると ID を使い回す必要があり、OS は ID を再割り当てする際に該当 PCID のエントリを明示的に無効化します。Linux は全プロセスに固定 ID を配らず、各 CPU ごとに少数(既定で 6 個)の PCID をプロセスへ動的に貸与する LRU 的な管理で、限られた ID を効率配分しています。
KPTIとPCIDの相互作用
投機実行のOS対策で導入された KPTI(カーネルページテーブル分離)は、ユーザー空間用とカーネル空間用にページテーブルを 2 つ持ち、システムコールや割り込みのたびに CR3 を切り替えます。これは PCID と密接に絡みます。
KPTI 下では同一プロセスでも「ユーザー用」「カーネル用」で別の PCID を割り当てます。もし同じ PCID を使うと、ユーザー/カーネル切替のたびにそのエントリをフラッシュせざるを得ず、PCID の利点が消えるためです。ID を分ければ、両方のエントリが TLB に共存し、頻繁な CR3 切替でもフラッシュが不要になります。PCID を持たない CPU で KPTI を有効にすると、システムコール往復ごとに TLB フラッシュが発生し、オーバーヘッドが顕著に重くなります。
TLBシュートダウン:他コアを揃えるIPIコスト
マルチコアでは各コアが独立した TLB を持ちます。あるコアがページテーブルを書き換えて自分の TLB を無効化しても、他コアの TLB には古い変換が残ります。これを揃える処理が TLB シュートダウンで、munmap や mprotect、ページ移行などマッピングを変える操作で必要になります。
x86 にはハードウェアでの遠隔 TLB 無効化命令が(広くは)ないため、OS がソフトウェアで全該当コアに依頼します。手順はおおむねこうです。
1. 開始コア: 自分のTLBを invlpg で無効化
2. 対象コアの集合を決定(そのアドレス空間を実行中のCPU)
3. 各対象コアへ IPI(プロセッサ間割り込み)を送出
4. 各対象コア: 割り込みハンドラ内で該当エントリを無効化
5. 開始コア: 全対象コアの完了応答を待ってから処理を続行
ここで効くのが 割り込みコントローラ(APIC)です。IPI は APIC を介して送られ、受信側はハンドラ実行のため実行中の処理を中断します。コストの本質は次の 3 点です。
| コスト要因 | 内容 |
|---|---|
| IPI送出と割り込み処理 | 対象コア数に比例。各コアでハンドラ実行と本来処理の中断が発生 |
| 完了待ち(同期点) | 最も遅いコアの応答まで開始コアがブロックする |
| キャッシュ・パイプライン擾乱 | 割り込みで対象コアのキャッシュ局所性が乱れる二次コスト |
対象コア数が増えるほどコストはほぼ線形に伸び、完了待ちは最も遅いコアに律速されます。これが、多コア環境で頻繁な munmap やページ移行が性能の落とし穴になる原理です。Linux はこれを緩和するため、対象を「実際にそのアドレス空間を実行している CPU」だけに絞り、mm_cpumask で無関係なコアへの IPI を省きます。
コストを嫌って無効化を遅らせると、解放済み物理ページを古い権限で参照し続ける窓が開きます。Linux はバッチ化(複数の無効化をまとめて 1 回のシュートダウンにする)で IPI 回数を減らしますが、バッチが確定するまでページを解放しないことで安全性を担保します。性能のための遅延と安全性の境界は、この「解放を待つ」点にあります。
「TLB は L1/L2 の階層を持ち、PWC は上位段の物理ベースを別にキャッシュする」「PCID/ASID は各エントリに空間 ID を付け、切替時の全フラッシュを回避する」「TLB シュートダウンは IPI で他コアに無効化を依頼し、コア数に対してほぼ線形にコストが伸びる」の3点が頻出です。PWC は最終変換ではなく中間段をキャッシュする点、PCID は命令ではなく CR3 と連動するハードウェア機構である点に注意。
まとめ
- TLB は L1(小・高速)と L2(大) の階層を持ち、L1 ミスでも L2 ヒットならウォークを省ける。
- ページウォークキャッシュ(PWC) は上位段の物理ベースを保持し、ミス時の実メモリ参照を削るため「ミス=常に最悪段数」にはならない。
- PCID/ASID は各エントリに空間 ID を付け、CR3 切替でも全フラッシュを回避する。KPTI 併用時はユーザー/カーネルで別 ID が必要。
- TLB シュートダウンは IPI で他コアの TLB を揃える同期点で、対象コア数にほぼ線形でコストが伸びる。
前提となる変換機構はMMUとTLBの内部、表構造の詳細は多段ページテーブルの構造も合わせてどうぞ。
OS Article
アドレス変換キャッシュの階層(TLB・PWC・ASID)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
TLB
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 5
導入後に効く点
x86のPCIDは各TLBエントリにアドレス空間IDを付け、文脈切替でCR3を切り替えても全フラッシュを回避する。ただしKPTI併用時はカーネル/ユーザーで別IDが要る。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 5
判断チェックリスト
- 自社の用途が「TLB / PCID」に近いか確認する。
- 強みである「TLBはL1(小・高速)とL2(大)の階層を持ち、ミス時もページウォークキャッシュ(PWC)が上位段の物理ベースをキャッシュして実メモリ参照回数を削る。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。