Mímisbrunnr知恵の泉

← コンピュータ基礎 一覧

🎓 レベル:標準 | 重要度:A(必須)

📎 前提:仮想記憶とページング | 関連:メモリ階層とキャッシュディスクとストレージ階層

要点(BLUF)

概念 ── 追い出す相手を選ぶ

仮想記憶とページング のデマンドページングでは、必要なページをディスクから持ってきます。でも物理フレームは有限。満杯のときに新しいページが要れば、誰かを追い出して空きを作る必要があります。

ページフォルトはディスクアクセス(ディスクとストレージ階層)を伴い、メモリの数万倍遅い。だからフォルト数をいかに減らすかが死活問題で、追い出す相手の選び方が効いてきます。

仕組み ── 3つの方式

OPT(最適置換)

この先、最も長く使われないページ」を追い出す。フォルト数を理論上最小にできることが証明されています。が、未来の参照列を知る必要があり実装不可能。現実の方式が「どれだけOPTに近いか」を測る物差しとして使います。

LRU(Least Recently Used)

最も長く使われていないページ」を追い出す。「最近使ったものはまた使う」という時間的局所性(メモリ階層とキャッシュ)への賭け。OPTの良い近似だが、厳密な実装は全アクセスに時刻記録が要り高コスト。実OSはクロックアルゴリズム等で近似します。

FIFO(First-In First-Out)

最も古く入れたページ」を追い出す。実装は最も簡単(キュー1本)。だが「古い=もう使わない」とは限らず、よく使うページも順番が来れば追い出してしまう。

具体例 ── フォルト数を計算で比べる

同じ参照列・フレーム3で3方式を比較します([[#対応ラボ]] の 03-03_page_replacement.py)。

refs = [7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1]
# fifo / lru / opt を実装して faults を数える

実行結果(実機):

== フレーム数3 のページフォルト数 ==
FIFO: 15  /  LRU: 12  /  OPT(最適): 9

OPT(9) < LRU(12) < FIFO(15)。LRUはOPTにかなり近く、FIFOは見劣りします。局所性を考慮するかどうかの差が、フォルト数=実速度に効いています。

落とし穴 ── Beladyの異常

直感では「フレーム(物理メモリ)を増やせばフォルトは減る」はず。LRUとOPTでは確かに成り立ちます。ところがFIFOでは、フレームを増やすとフォルトが増えることがある。これがBeladyの異常です。

seq = [1,2,3,4,1,2,5,1,2,3,4,5]
for f in (3, 4):
    print(f"FIFO フレーム{f}: フォルト {fifo(seq,f)}")

実行結果(実機):

FIFO フレーム3: フォルト 9
FIFO フレーム4: フォルト 10

フレームを3→4に増やしたのにフォルトが9→10に増えた。FIFOは「使用頻度」を見ず「入れた順」だけで追い出すため、メモリを増やしても良くなる保証がないのです。LRU/OPTのようなスタックアルゴリズム(フレーム数を増やすと中身が必ず包含関係になる方式)ではこの異常は起きません。これがFIFOが敬遠される決定的な理由です。

関連現象 ── スラッシング

物理メモリに対し、走らせるプロセスのワーキングセット(当面必要なページ群)が大きすぎると、ページを入れては追い出し、追い出してはまた入れる、を繰り返します。CPUは計算でなくページの出し入れ(ディスクI/O)に追われ、スループットが急落する。これがスラッシングです。

flowchart LR
    load["多重度を上げる(プロセスを増やす)"] --> up["スループット上昇"]
    up --> peak["最適点"]
    peak --> thrash["これ以上増やすと…"]
    thrash --> down["スラッシング(フォルト多発でI/O律速・性能急落)"]

対策は、各プロセスのワーキングセットを見積もり、収まらないなら多重度を下げる(一部プロセスをスワップアウトして待たせる)こと。「詰め込みすぎると全員遅くなる」典型です。

仕組みの直観 ── なぜこの設計か

⚠️ よくある誤解・落とし穴

対応ラボ

cs-foundations-study/labs/03-03_page_replacement.py(実行して上記フォルト数とBeladyの異常を確認済み)。

関連

第3章 メモリ管理 目次