CPUキャッシュ・NUMA・TLB を“やさしく深く”理解する:メモリアクセスの本質と高速化の仕組みを図解で解説【初心者向け+深掘り】

🧠【初心者向け】CPUキャッシュ・NUMA・TLB をやさしく解説(図解あり)

プログラムの性能は
CPU・メモリ・キャッシュ の関係で大きく変わります。
しかし、この「低レイヤーの仕組み」は普段触れる機会が少なく、
初心者にとってはブラックボックスになりがちです。

  • CPUキャッシュって何?
  • L1 / L2 / L3 の違いって?
  • NUMA構成のサーバーで何が起きる?
  • メモリアクセスが遅いのはなぜ?
  • TLBってなんの略?
  • Javaプログラムにどう影響するの?

この記事では
初心者向けのイメージ → 深掘りの内部構造 の順で
CPUまわりの基礎を“本質から”理解できるようまとめます。


✨ 結論

✔ メモリアクセスの速度は「CPUキャッシュ」が支える

✔ L1 → L2 → L3 → メモリ の順に遅くなる

✔ NUMA構成では“メモリの遠近”が性能を変える

✔ TLBは「アドレス変換キャッシュ」であり、外すと激遅

✔ 高速化の本質は「近い場所のデータをまとめて使う」


CPUキャッシュの本質(まずはここから)

CPU は メモリより圧倒的に速い ため、
メモリを直接読み書きすると遅くなります。

そこで使われる仕組みが CPUキャッシュ


🔹 図解(短線版)

CPU
 ├ L1キャッシュ(超高速・超小容量)
 ├ L2キャッシュ(高速)
 └ L3キャッシュ(大容量・遅め)
↓
メインメモリ(遅い)

アクセス速度の違い(直感的に理解)

速度イメージ(極簡易化)

L1    → 1〜4 サイクル  
L2    → 4〜12 サイクル  
L3    → 30〜40 サイクル  
メモリ → 200〜300 サイクル

メモリは L1 の50〜100倍遅い。


キャッシュヒット/ミスとは?

  • ヒット:必要なデータがキャッシュにある(高速)
  • ミス:キャッシュにない(メモリ参照で激遅)

頻繁に触るデータはキャッシュに載りやすい。


データの「局所性」がなぜ重要か?

CPU の最適化は
データ局所性(Locality) が本質。

✔ 時間的局所性

同じデータを何度も使う

✔ 空間的局所性

近くのデータが連続して使われる


🔹 図解(空間的局所性)

[100][101][102][103]
       ↑   ↑  ↑ 近くを連続でアクセス

NUMA(メモリの遠近による違い)

サーバーでは
CPUが複数のメモリバンクを持つ NUMA構成 が一般的。


🔹 図解(短線版)

Node0: CPU + メモリ
Node1: CPU + メモリ

Node0 のCPUが Node1のメモリ にアクセスすると遅くなる。


TLB(Translation Lookaside Buffer)とは?

TLBは
仮想アドレス → 物理アドレスの変換を高速化するキャッシュ

仮想メモリを使うOSでは
アドレス変換が頻発するため、
TLBがないと毎回ページテーブルをたどって遅くなる。


✔ TLBミスは地味に重い

TLBミス → ページテーブル参照 → メモリアクセス
キャッシュミスよりさらに複雑で遅い


Javaプログラムへの影響(初心者でも理解できる範囲)

✔ 大きな配列をランダムアクセスすると遅い

→ 空間的局所性が失われる

✔ 連続データ構造(配列)は高速

→ キャッシュヒットしやすい

✔ オブジェクトが飛び飛びに配置されると遅い

→ Javaのヒープ構造による“参照追跡コスト”

✔ parallelStream が遅い原因の一部は NUMA

→ コアが違うメモリバンクを参照すると遅い


ここまでのまとめ(初心者向け)

  • CPU → キャッシュ → メモリ の順に遅くなる
  • L1/L2/L3 は“近いほど速い”
  • NUMA では“どのCPUのメモリか”で速度が変わる
  • アドレス変換は TLB が高速化している
  • 高速化の本質は「近くのデータをまとめて使う」

▼▼ 深掘り編ここから ▼▼


キャッシュライン(Cache Line)の本質

CPUは 1バイトずつ ではなく
キャッシュライン(通常64バイト) 単位で読み込む。


🔹 図解

メモリ:
[0][1][2]...[63] → 1キャッシュライン

1個の変数を読む → 近くの63バイトも勝手に載る

✔ ゲームや高速処理では

“構造体をキャッシュライン境界に揃える” のが常識。


False Sharing(偽共有)

複数スレッドが
異なる変数なのに同じキャッシュラインに載っている
と、キャッシュが無駄に同期されて遅くなる。


🔹 図解

ThreadA → varA(ライン1)
ThreadB → varB(同じライン1)
→ 書き込みのたびにライン全体が同期…激遅!

→ 高性能アプリケーションで深刻な問題。


TLBと巨大ページ(Huge Page)

TLBはページ単位(4KB)でキャッシュするため
多くのメモリを扱う場合、TLBミスが増えやすい。

HugePage(2MB / 1GB)を使うと
TLBの負荷が激減する。


NUMA最適化の具体例

  • スレッドとメモリを同じノードに固定する
  • メモリを分散せずローカル化する
  • 並列処理でノードを跨がないように設計する

大規模サービスでは常識のチューニング。


Deep Friendly Tech の一言(後書き)

CPUキャッシュ・NUMA・TLB は
アプリの速度がなぜ変わるか を理解するための
“究極の低レイヤー知識” です。

この領域を知っているだけで

  • パフォーマンス問題の原因が読める
  • 並列処理・高速化の本質が掴める
  • Java の内部構造の理解が一段深くなる

エンジニアとしてのレベルが
確実に一段上がります。

コメント

タイトルとURLをコピーしました