
キャッシュのしくみをPythonでシミュレーション|"速いメモリ"の魔法
2026-09-22 ・ 実践
CPUはメモリよりずっと速いため、そのままでは「データ待ち」で性能が出ません。そこで キャッシュ という小さくて速いメモリが橋渡しをします。この記事ではPythonでキャッシュのヒット率をシミュレーションし、なぜ効くのかを数値で理解します。
なぜキャッシュが効くのか:局所性
プログラムのメモリアクセスには局所性があります。「一度使ったデータは近いうちにまた使う(時間的局所性)」「近くのデータも使う(空間的局所性)」。この性質のおかげで、小さなキャッシュでも大半のアクセスをカバーできます。
準備
Python標準機能だけ。
① ダイレクトマップ・キャッシュを実装
各メモリアドレスが「キャッシュのどこに入るか」が固定の、最もシンプルな方式です。
class DirectMappedCache:
def __init__(self, num_lines=8):
self.num_lines = num_lines
self.tags = [None] * num_lines # 各行が今どのアドレスか
self.hits = 0
self.misses = 0
def access(self, address):
line = address % self.num_lines # どの行に入るか
tag = address // self.num_lines
if self.tags[line] == tag:
self.hits += 1 # キャッシュにあった!
else:
self.misses += 1 # なかった → メモリから読む
self.tags[line] = tag # 入れ替える
def hit_rate(self):
total = self.hits + self.misses
return self.hits / total if total else 0
② 局所性のあるアクセスで試す
配列を順番に何度も舐める(局所性が高い)パターンで測ります。
cache = DirectMappedCache(num_lines=8)
# 0〜7を3周アクセス(局所性が高い)
for loop in range(3):
for addr in range(8):
cache.access(addr)
print(f"ヒット率: {cache.hit_rate():.1%}") # 高くなる
配列を繰り返し使うので、2周目以降はキャッシュに乗っていてヒット率が上がります。
③ 局所性がないと激減する
毎回バラバラの遠いアドレスを触ると、キャッシュは役に立ちません。
import random
cache2 = DirectMappedCache(num_lines=8)
random.seed(0)
for _ in range(1000):
cache2.access(random.randint(0, 100000)) # 広範囲をランダムアクセス
print(f"ランダムアクセスのヒット率: {cache2.hit_rate():.1%}") # 低い
ランダムだとヒット率は数%以下。キャッシュは「局所性がある」前提で効くとわかります。だから局所性を意識したコード(配列を連続アクセスするなど)が高速化に効くのです。
キャッシュミスは高くつく
キャッシュにあれば数サイクル、なければ主メモリまで数百サイクル。ミス1回の代償は大きい。だからCPUは何段ものキャッシュ(L1/L2/L3)を持ち、少しでもヒット率を上げようとします。プログラムの速度は、実は"メモリアクセスの局所性"に強く左右されます。
方式にも工夫がある
今回のダイレクトマップは単純ですが、特定のアドレスが衝突しやすい弱点があります。実際のCPUは「セットアソシアティブ」など、衝突を減らす方式を使います。それでも基本原理は同じ:局所性を活かして速いメモリで橋渡しする、です。
まとめ
- キャッシュはCPUとメモリの速度差を埋める小さく速いメモリ
- プログラムの局所性のおかげで、小容量でも高ヒット率になる
- 局所性が高いと数十%以上、ランダムだと数%とヒット率が激変
- ミスの代償が大きいため多段キャッシュで対処する
もう少し詳しく(背景と理論)
キャッシュが効くのは、プログラムのメモリアクセスに参照の局所性があるからです。直近使ったデータをまた使う時間的局所性と、近くのアドレスを続けて使う空間的局所性の2種があり、小さく速いメモリに「よく使う分」を置くことで平均アクセス時間を短縮します1。CPUは速度と容量が反比例するため、レジスタ→L1→L2→L3→主記憶→ストレージというメモリ階層を段階的に構成します2。設計の要点は、連想度(何ウェイか)・ブロックサイズ・置換方式(LRU 等)で、これらがヒット率とハードコストのトレードオフを決めます3。平均アクセス時間は「ヒット時間+ミス率×ミスペナルティ」で評価する古典的な指標です。
次の一歩 🌸
記憶素子はフリップフロップ、AIチップのメモリはAIチップとHBM、CPUの流れはCPUのしくみへどうぞ。