メモリ階層
メモリかいそう
レジスタからストレージまで、速さと容量のトレードオフで階層化された記憶の構造。
概要
メモリ階層は、コンピュータの記憶装置を「速いが小さいもの」から「遅いが大きいもの」へと段階的に積み重ねた構造のことです。CPU内部のレジスタを頂点に、CPUキャッシュ(L1/L2/L3)、主記憶(RAM)、そしてSSDやHDDといったストレージまでが一つのピラミッドを成しており、プログラムが扱うデータは常にこの階層のどこかに置かれています。
なぜわざわざ階層にするのかというと、「速くて大きくて安いメモリ」が物理的に作れないからです。速いメモリは高価で大容量化できず、大容量のメモリは必然的に遅くなります。そこで、よく使うデータだけを速い層に置き、残りは遅くて大きい層に置く — この分業によって、全体としては「ほぼ速いメモリの速度で、大容量メモリの容量を使える」ように見せかけているのがメモリ階層の本質です。
普段プログラムを書くうえでこの階層を直接操作することはほとんどありませんが、性能問題の多くはここに根を持っています。「同じ計算量なのにコードの書き方で数倍速くなる」「メモリに載り切らなくなった途端に激遅になる」といった現象は、どの階層にデータが載っているかの違いで説明できます。
なぜ生まれたか
問題の根源は、CPUとメモリの速度差が開き続けたことにあります。初期のコンピュータではCPUとメモリの速度は釣り合っていましたが、CPUの演算速度が年率で急伸したのに対し、主記憶(DRAM)のアクセス速度の改善は緩やかでした。その結果、現代のCPUにとって主記憶からの読み出しは数百サイクル待ち — つまり「計算そのものより、データが届くのを待つ時間のほうが長い」という逆転が起きました。この現象は「メモリの壁(memory wall)」と呼ばれます。
一方で、SRAM(速いが高くて小さい)とDRAM(遅いが安くて大きい)、さらに磁気ディスクやフラッシュメモリ(もっと遅いがもっと大きい)という、性質の異なる記憶素子はそれぞれ存在していました。どれか一つを選ぶのではなく、全部を組み合わせて「使用頻度の高いデータほど速い層に寄せる」仕掛けを作れば、コストを抑えたまま実効速度を稼げる — これがメモリ階層という設計の答えです。CPUキャッシュのハードウェアによる自動管理も、OSによる仮想メモリ(主記憶が足りない分をストレージへ退避する仕組み)も、この同じ思想の実装です。
詳細
ピラミッドを桁で眺める
メモリ階層を理解する第一歩は、各層の容量とレイテンシ(アクセスにかかる時間)を「桁」で把握することです。正確な数値はハードウェアで変わりますが、桁の感覚はほぼ普遍です。
レジスタは1ナノ秒未満、L1キャッシュは数ナノ秒、主記憶は約100ナノ秒、SSDは約100マイクロ秒、HDDは数ミリ秒 — 隣り合う層の間ですら数倍〜数十倍、レジスタとHDDの間では実に1000万倍もの速度差があります。人間の時間に例えると、L1キャッシュが「机の上のメモを見る(数秒)」なら、主記憶は「隣の部屋に取りに行く(数分)」、SSDは「隣町の倉庫まで往復する(数日)」、HDDは「海外から取り寄せる(数か月)」に相当します。この桁の感覚があると、「ディスクI/Oを1回減らす」ことの重みが直感的に分かるようになります。
階層を機能させる鍵 — 局所性の原理
ただ層を積んだだけでは速くなりません。この構造が機能するのは、プログラムのメモリアクセスに「局所性(locality)」という強い偏りがあるからです。局所性には2種類あります。時間的局所性は「一度使ったデータは近いうちにまた使われる」傾向(ループ内の変数など)、空間的局所性は「使ったデータの近くのデータもすぐ使われる」傾向(配列の連続アクセスなど)です。
この偏りのおかげで、「最近使ったものとその周辺」だけを上の層に置いておけば、アクセスの大半は速い層で完結します。上の層に目当てのデータがあれば「ヒット」、なければ下の層まで取りに行く「ミス」となり、ヒット率が実効速度を支配します。逆に言えば、局所性の乏しいアクセスパターン(巨大なデータをランダムに飛び回るなど)を書くと、階層の恩恵を受けられずミスの嵐になります。データ構造とアクセス順序の設計が性能に直結する理由がここにあります。具体的な仕組みはCPUキャッシュの記事で掘り下げます。
各層の役割分担
頂点のレジスタはCPUが演算に直接使う作業台で、コンパイラがどの変数をレジスタに割り付けるかを決めます。その下のL1〜L3キャッシュはハードウェアが自動管理し、プログラムからは見えません。主記憶はプロセスのコードとデータ — スタックとヒープもここに置かれます — の本籍地です。主記憶とストレージの間はOSの仮想メモリが橋渡しし、使われていないページをストレージへ追い出します(スワップ)。この追い出しが頻発する状態が「スラッシング」で、メモリ不足のマシンが極端に遅くなる正体です。
なおGPUにも同様の階層(レジスタ・共有メモリ・デバイスメモリ)があり、GPUプログラミングの最適化も結局はこの階層とのつき合い方が中心になります。
同じ思想はソフトウェアにも続いている
メモリ階層の考え方は、ハードウェアの中で完結しません。アプリケーションが速いキャッシュ(Redisなど)をRDBMSの手前に置くのも、CDNがユーザーの近くにコンテンツの複製を置くのも、「よく使うものを速い(近い)場所に置き、ヒット率で実効速度を稼ぐ」というまったく同じ思想です。つまりメモリ階層は、レジスタから世界規模の配信網まで一貫して繰り返される、コンピュータシステムの基本パターンの最小単位と言えます。この一段を理解しておくと、上位のキャッシュ設計の判断 — 何を載せるか、いつ捨てるか、一貫性をどう保つか — がすべて同じ問題の変奏として見えてきます。