TT Lab
はじめる
学ぶ 学習パス コース

コンピュータ構成

メモリ階層 — 速いものは小さく、大きいものは遅い

TT Labで続きを見る

一言でいうと

速くて大きくて安いメモリは作れないので、小さくて速いものから、大きくて遅いものまでを層状に積み上げ、よく使うデータを上の層へ引き上げます。

なぜ必要なのか

レジスターはCPUと同じ速度で動作しますが、数十個しかありません。DRAMは数十ギガバイトを保持できますが、アクセスに数百サイクルかかります。この隔たりは、縮まるどころか、世代ごとに広がってきました。演算ユニットを増やすのは、トランジスタをもっと入れればよいのですが、データをチップの内外へ運ぶ能力は、ピンの数や配線の物理的な限界に縛られているからです。

この積み重なった隔たりを、メモリウォール(memory wall)といいます。階層構造は、この壁を迂回する唯一の現実的な方法です。

どう動くのか

典型的な階層と、おおよそのレイテンシは次のとおりです。数字は世代ごとに違いますが、桁の違いが要点です。

階層 サイズ アクセスレイテンシ(おおよそ)
レジスター 数百バイト 1サイクル未満
L1キャッシュ 32–64KB 4–5サイクル
L2キャッシュ 0.5–2MB 12–20サイクル
L3キャッシュ 数十MB 40–70サイクル
DRAM 数十GB 200–300サイクル
NVMe SSD 数TB 数万サイクル

この構造が実際に機能するのは、プログラムが局所性(locality)を持つからです。

キャッシュは、この2つの性質をそのまま利用します。データを1バイトずつ取得するのではなく、キャッシュラインの単位、通常は64バイトを丸ごと取得します。4バイトの整数を1つ読んでも、隣り合う15個が一緒に載ってきます。配列を順番に回るコードが速い理由がこれです。

キャッシュミスは、3種類に分けて見ると、対処が分かれます。

現場での姿

行優先で保存された2次元配列を列優先で走査すると、同じ演算なのに数倍から数十倍遅くなります。毎回のアクセスで新しいキャッシュラインを取得し、そのうち4バイトだけを使って捨てるからです。行列の乗算でタイリング(ブロッキング)を使う理由も同じです。ワーキングセットをキャッシュに収まるサイズに切り分けて、再利用を増やすのです。

AIワークロードでは、この問題がさらにあからさまに現れます。LLM推論のデコード段階は、巨大な重みを1回読み込んで、小さな入力と掛け合わせて捨てます。演算強度(取得したバイトあたりに行う演算の数)が極端に低いので、演算器はほとんど遊んでいて、メモリ帯域幅が性能を決めます。GPU使用率が30パーセントを超えられないというよくある嘆きの正体がこれです。このとき、より高価な演算チップを買うのはお金を使うだけの選択で、答えは、読むバイトを減らすこと(量子化)や、再利用を増やすことです。

続くクイズで確認すること

キャッシュラインがなぜ64バイトずつ動くのか、同じアルゴリズムでアクセス順序を変えただけで、なぜ倍数で遅くなるのかを、説明できるかを確認します。