手を上げるチップくんゆるふわ半導体
考え中のチップくん

キャッシュのしくみをPythonでシミュレーション|"速いメモリ"の魔法

2026-09-22実践

#キャッシュ#メモリ#ヒット率#Python#実践

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のしくみへどうぞ。

Footnotes

  1. 参照の局所性(Denning)。時間的局所性=同じ番地の再利用、空間的局所性=近傍番地の連続利用。キャッシュ・仮想記憶の有効性の根拠。

  2. メモリ階層は速度と容量・コストのトレードオフを段階化する。上位ほど速く小さく高価、下位ほど遅く大きく安価。

  3. 連想度・ブロックサイズ・置換ポリシー(LRU/FIFO等)がヒット率を左右する。Hennessy & Patterson の定量的アプローチが設計の教科書的枠組み。

あわせて読みたい