コンテンツにスキップ

第 9 章 コンピュータアーキテクチャ

まえがき — 機械の中身を覗く

print("Hello") と書いて Enter を押すと、なぜ画面に文字が表示されるの? CPU って具体的に何をしているの? なぜ「キャッシュに乗るデータ」は速いの? なぜ「分岐予測ミス」がパフォーマンスを左右するの?

これらの疑問に答えるのが コンピュータアーキテクチャ です。

ハードウェアを知らないプログラマは、性能チューニングや低レイヤのバグで詰まります。逆に「メモリは平面ではない」「キャッシュミスは命取り」「分岐予測ミスは数十サイクルかかる」を体感していると、書くコードが変わります。

🎯 章の目標

  • 数の表現(2 進・2 の補数・浮動小数点)と論理回路の組み立てを理解する
  • CPU の構成(ALU・レジスタ・データパス)を辿り、命令がどう実行されるかを説明できる
  • パイプライン・キャッシュ・仮想記憶など現代 CPU の高速化機構を体系的に把握する
  • 並列性(命令・データ・スレッド)の階層を理解する

9.1 なぜハードウェアを知るべきか

9.1.1 性能の違いは桁違い

同じ「配列の合計を求める」でも、メモリアクセスの順序を変えるだけで 10 倍 性能が変わることがあります。

// 早い: 連続アクセス(キャッシュ効率良い)
for (int i = 0; i < N; i++) sum += a[i];

// 遅い: 飛び飛びアクセス
for (int i = 0; i < N; i += 64) sum += a[i];

ハードウェアの「キャッシュライン」を知っていれば、これが当然と分かります。

9.1.2 OS・コンパイラ・ネットワークの基盤

第 10 章 (OS)、第 11 章 (ネットワーク)、第 13 章 (コンパイラ) はすべて本章の上に立ちます。


9.2 数の表現

9.2.1 2 進数

人間は 10 進、コンピュータは 2 進。

10 進 2 進
0 0000
1 0001
5 0101
10 1010
255 11111111

ビット・バイト・ワード

  • 1 ビット: 0 または 1
  • 1 バイト = 8 ビット (256 通り)
  • 1 ワード = CPU の自然な単位 (32 ビット or 64 ビット)

9.2.2 2 の補数表現

負の数の表現方法。

例 (8 ビット): - \(5\)00000101 - \(-5\)11111011 (5 のビット反転 + 1)

メリット: - 加算回路が同じで済む - 0 が 1 つだけ - 範囲: \(-2^{n-1}\) から \(2^{n-1} - 1\)

オーバーフロー

8 ビットで \(127 + 1 = 128\) → ビット列としては 10000000 = \(-128\)。「整数が突然負になる」のはこれ。安全な言語 (Rust, Swift) は実行時にチェックします。

9.2.3 浮動小数点 (IEEE 754)

\(\pm 仮数 \times 2^{指数}\) の形。32 ビット (float) と 64 ビット (double) が一般。

構造(32 ビットの場合)

┌─┬────────┬───────────────────────┐
│S│ Exponent│       Mantissa        │
│1│  8 bit  │         23 bit        │
└─┴────────┴───────────────────────┘
 符号    指数(バイアス付き)  仮数

よくある罠

print(0.1 + 0.2)   # 0.30000000000000004
print(0.1 + 0.2 == 0.3)  # False!

10 進の 0.1 は 2 進では循環小数。だから誤差が出る。

金額計算では絶対に float を使わない。Decimal 型や整数(円→銭)を使う。

特殊値

  • +0, -0
  • +Infinity, -Infinity
  • NaN (Not a Number) — 0/0, sqrt(-1) など

9.3 ブール代数と論理回路

9.3.1 ゲート

第 5 章で扱った AND・OR・NOT を、半導体で実装したのが 論理ゲート

NAND ゲート (AND の否定)
A ─┤  )○─ ¬(A∧B)
B ─┘

NAND だけ で、すべての論理回路が作れます。これが現代 CPU の物理基盤。

9.3.2 組合せ回路

入力だけで出力が決まる回路。

半加算器 (HA)

1 ビットの加算:

A ──┐
    XOR ── Sum (和)
B ──┤
    AND ── Carry (桁上げ)

全加算器 (FA)

桁上げも含む加算。これを並べると 32 ビット加算器ができます。

ALU (Arithmetic Logic Unit)

加算、減算、論理演算をまとめた回路。CPU の核。

9.3.3 順序回路

状態を持つ回路。フリップフロップ (D-FF, JK-FF, T-FF) が基本素子。

レジスタ = D-FF を 32 個並べたもの

クロックが来るたびに「現在の入力」を「次の状態」に保存

CPU、メモリ、I/O コントローラーは順序回路の集まりです。


9.4 命令セットアーキテクチャ (ISA)

ハードウェアとソフトウェアの境界。

ISA 特徴 用途
x86 / x86-64 CISC、複雑、可変長 PC, サーバ
ARM RISC、固定長、省電力 スマホ、Apple Silicon
RISC-V オープン、教育・研究で人気 組込み、新興チップ

9.4.1 命令の種類

  • データ転送: load, store, mov
  • 算術論理: add, sub, and, or, shift
  • 制御: branch, jump, call, return
  • システム: syscall, int

9.4.2 アセンブリ例 (RISC-V)

addi x10, x0, 5    # x10 = 0 + 5
addi x11, x0, 3    # x11 = 0 + 3
add  x12, x10, x11 # x12 = x10 + x11 = 8

x10 = 5; x11 = 3; x12 = 8」と読み替えられます。

9.4.3 アセンブリを学ぶ意味

  • C のポインタやインライン関数の最適化を理解できる
  • セキュリティ脆弱性 (バッファオーバーフロー) の仕組みが分かる
  • 性能チューニング時に「コンパイラがこう吐いている」を読める

9.5 CPU の動作

9.5.1 5 ステージのデータパス

教育用の RISC-V を例に:

graph LR
    IF[IF<br/>Fetch<br/>命令取得] --> ID[ID<br/>Decode<br/>命令解読]
    ID --> EX[EX<br/>Execute<br/>ALU 計算]
    EX --> MEM[MEM<br/>Memory<br/>load/store]
    MEM --> WB[WB<br/>Write Back<br/>結果書戻]

    style IF fill:#bbdefb
    style ID fill:#c5e1a5
    style EX fill:#ffe0b2
    style MEM fill:#f8bbd0
    style WB fill:#d1c4e9
  1. IF (Instruction Fetch): PC から命令を取得
  2. ID (Instruction Decode): 命令を解読、レジスタ読み出し
  3. EX (Execute): ALU で計算
  4. MEM (Memory Access): load/store ならメモリアクセス
  5. WB (Write Back): 結果をレジスタに書き戻す

毎サイクル、命令が 1 つ進みます。

9.5.2 制御ユニット

各ステージに「この命令で何をするか」を指示する司令塔。


9.6 パイプライン — 同時並行実行

9.6.1 アイデア

5 ステージを 重ねて 実行。1 サイクルに 1 命令完成 (理想時 IPC = 1)。

時刻 →
命令1:  IF  ID  EX  MEM WB
命令2:      IF  ID  EX  MEM WB
命令3:          IF  ID  EX  MEM WB
命令4:              IF  ID  EX  MEM WB
命令5:                  IF  ID  EX  MEM WB
        └───────── 1 サイクルに 1 命令完成 (理想時 CPI = 1) ─────────┘

工場の流れ作業」と同じ発想。組立ラインで各工程が並行して動く。

工場の流れ作業と同じ発想。

9.6.2 ハザード — パイプラインの敵

構造ハザード

同じハードウェアの取り合い。命令メモリとデータメモリを分離して解消。

データハザード

「前の命令の結果を、まだ書き戻し前に次が使う」。

add x1, x2, x3   # x1 を計算中
sub x4, x1, x5   # x1 を読みたい (まだ書かれてない!)

対策: - フォワーディング: ALU 出力を直接次の命令へ - パイプラインストール: 1 サイクル待つ

制御ハザード

分岐命令で「どこへ飛ぶか分からない」とフェッチが無駄に。

対策: - 分岐予測: 「過去の傾向」から予測 - 投機実行: 予測通りに進め、間違っていたら戻る

現代 CPU の分岐予測精度は 95% 以上

9.6.3 スーパースカラと OoO 実行

複数の実行ユニットを並列に持ち、依存のない命令を アウトオブオーダ で実行。

命令ウィンドウ:
add x1, x2, x3 (即実行)
sub x4, x1, x5 (x1 待ち)
mul x6, x7, x8 (即実行 ← x4 より先に動く)

「Tomasulo アルゴリズム」「レジスタリネーミング」で並列性を最大化。Intel Core i7、Apple M1 もこの仕組み。

9.6.4 Spectre / Meltdown — 投機実行が脆弱性に

投機実行を悪用した サイドチャネル攻撃 が 2018 年に発覚。CPU 全体の設計見直しを迫る大事件。

「最適化と安全性のトレードオフ」の典型例です。


9.7 メモリ階層 — 速度とサイズのトレードオフ

9.7.1 階層構造

容量 遅延
レジスタ 数 KB 1 サイクル CPU 内
L1 キャッシュ 数十 KB 4 サイクル CPU コア内
L2 キャッシュ 数百 KB 12 サイクル コア内
L3 キャッシュ 数十 MB 40 サイクル チップ内共有
主記憶 (DRAM) GB 100+ サイクル DIMM
SSD TB 数万サイクル NVMe
HDD TB 数百万サイクル 磁気ディスク

比喩

L1 = 自分の机の上、L2 = 引き出し、L3 = 本棚、DRAM = 隣の部屋、SSD = 倉庫、HDD = 別の建物。距離 = 遅延 と思えば直感的。

9.7.2 局所性 — キャッシュが効く理由

  • 時間局所性: 一度使ったデータは再利用される
  • 空間局所性: 近くのデータも使われる
// 良い: 連続アクセス、空間局所性
for (i = 0; i < N; i++) sum += a[i];

// 悪い: 飛び飛び
for (i = 0; i < N; i++) sum += a[i * 1000];

9.7.3 キャッシュ構造

  • ライン: 64 B が一般。ライン単位で読み込み
  • 直接マップ: 1 つのアドレスは 1 つの場所だけ
  • セットアソシアティブ: \(n\)-way、\(n\) 個の候補
  • フルアソシアティブ: どこにでも入れる
  • 置換: LRU, FIFO, ランダム

9.7.4 行優先 vs 列優先 — 重要な実例

double a[1024][1024];

// 行優先 (C のメモリレイアウト)
for (i = 0; i < 1024; i++)
    for (j = 0; j < 1024; j++) sum += a[i][j];   // 速い

// 列優先
for (j = 0; j < 1024; j++)
    for (i = 0; i < 1024; i++) sum += a[i][j];   // 遅い (10 倍以上)

C/C++ のメモリレイアウトは 行優先。Fortran は列優先。配列のアクセス順を間違えると桁違いに遅くなる

9.7.5 キャッシュコヒーレンス

マルチコアでは、各コアの L1 が矛盾しないようにする必要があります。MESI プロトコル (Modified, Exclusive, Shared, Invalid) で管理。

False sharing」(同じキャッシュラインに別スレッドのデータ) は性能悪化の有名な落とし穴。


9.8 仮想記憶

9.8.1 概念

各プロセスに「自分専用の連続したメモリ空間」を提供。

プロセス A の仮想空間: 0x0 ─ 0xFFFF
プロセス B の仮想空間: 0x0 ─ 0xFFFF   (別物!)
        ↓ 物理メモリへの「翻訳」
物理メモリ:    [プロセス A のページ] [プロセス B のページ] ...

9.8.2 ページング

仮想空間を 4 KB のページ に分割し、物理フレームに対応付け。

ページテーブル

仮想ページ → 物理フレーム + 属性 (R/W/X、有効ビット、ダーティ)

x86-64 は 4 段の多段ページテーブル (4 KB × 512 × 512 × 512 × 512 = 256 TB の仮想空間)。

9.8.3 TLB (Translation Lookaside Buffer)

ページテーブルのキャッシュ。これがないと毎メモリアクセスで 4 段のページテーブルを引く必要があり、致命的に遅くなります。

9.8.4 ページフォルト

物理メモリにないページにアクセス → OS が処理。

  • マイナーフォルト: 物理にあるが対応がついてない (mmap など)
  • メジャーフォルト: ディスクから読み込む必要あり (遅い)

9.8.5 スワップ

物理メモリ不足時、ページを ディスクに退避。スワップが多発すると激遅 (スラッシング)。

メモリ不足に気付いたら、設計を見直すかメモリを増やす」のが鉄則。


9.9 I/O とバス

9.9.1 通信方式

  • メモリマップド I/O: デバイスをメモリアドレスとして扱う
  • ポート I/O: 専用命令 (x86 の in/out)
  • 割り込み: デバイスから CPU への通知
  • DMA: CPU を介さずデバイスとメモリで転送

9.9.2 バス

  • PCIe (内部接続、グラボ・SSD)
  • USB (外部接続)
  • SATA (旧式 SSD/HDD)
  • Thunderbolt (高速外部)

9.10 並列性の階層

9.10.1 4 つのレベル

  1. 命令レベル並列 (ILP): パイプライン、スーパースカラ
  2. データ並列: SIMD (SSE, AVX, NEON)、GPU
  3. スレッドレベル並列: マルチコア、SMT (Hyper-Threading)
  4. 要求レベル並列: クラスタ、データセンタ

9.10.2 Amdahl の法則

直列部分がボトルネックになる

\(N\) プロセッサでの最大スピードアップ: $\(S = \frac{1}{s + (1-s)/N}\)$

\(s\) が直列部分の割合。

例: \(s = 10\%\) なら、コアを無限に増やしても 最大 10 倍 しか速くならない。

9.10.3 Gustafson の法則

問題サイズも増やせば天井はない

リアルワールドでは問題サイズも増えるので、Amdahl ほど悲観的ではない。


9.11 GPU と特殊アクセラレータ

種類 用途
CPU 汎用処理
GPU 大量並列のデータ処理
TPU 行列積 (Google)
FPGA 再構成可能ロジック
ASIC 特定用途専用

機械学習の発展は GPU の進化と表裏一体。NVIDIA H100 で ChatGPT が動いています。


9.12 性能の測り方

9.12.1 指標

  • スループット: 単位時間あたり処理量 (IPS, FLOPS)
  • 遅延 (latency): 1 件あたりの時間
  • IPC: 1 サイクルあたり命令数

9.12.2 ベンチマーク

  • SPEC (CPU)
  • MLPerf (機械学習)
  • TPC (データベース)

9.12.3 プロファイリング

実機での性能測定: - perf (Linux) - VTune (Intel) - gprof (GNU) - Apple Instruments

推測するな、計測せよ」が性能チューニングの鉄則。

9.12.4 ルーフラインモデル

演算強度 (Flops/Byte)」でボトルネックを判定。CPU 律速 vs メモリ律速を切り分けられます。


9.13 演習問題

  1. \(-37\) を 8 ビット 2 の補数で表せ。
  2. 0.1 + 0.2 != 0.3 を IEEE 754 表現で説明せよ。
  3. NAND ゲート 4 個で XOR を構成せよ。
  4. RISC-V で for (i = 0; i < 10; i++) sum += a[i]; をアセンブリで書け。
  5. データハザードのフォワーディングが効かないケース (load-use) を例示せよ。
  6. キャッシュサイズ 32 KB、ライン 64 B、4-way の場合のセット数を計算せよ。
  7. for (i; i<N; i++) for (j; j<N; j++) A[j][i]A[i][j] のキャッシュ効率の違いを説明せよ。
  8. Amdahl の法則で、直列 10% の作業を 100 コアで実行したときのスピードアップを求めよ。
  9. ページサイズ 4 KB、仮想アドレス 32 ビット、物理メモリ 1 GB のとき、単一段ページテーブルのサイズを計算せよ。
  10. perf stat を使って自作プログラムの IPC、キャッシュミス率を測れ。

9.14 この章のまとめ

階層 内容
トランジスタ → 論理回路 NAND, FF
論理回路 → ALU・CPU データパス、制御
パイプライン 並列実行、ハザード
キャッシュ 局所性、ライン
仮想記憶 ページテーブル、TLB
並列性 ILP, SIMD, SMT
Amdahl 直列部分が天井

ハードウェアの感覚を持ったエンジニアは、コードを「実機の挙動」で見抜けます。「キャッシュに乗るか」「分岐予測が当たるか」を考える だけで、性能の桁が変わります。

9.15 次に読むもの

  • Patterson & Hennessy, Computer Organization and Design (RISC-V 版)
  • Hennessy & Patterson, Computer Architecture: A Quantitative Approach
  • Bryant & O'Hallaron, Computer Systems: A Programmer's Perspective (CSAPP) — 名著
  • nand2tetris プロジェクト — NAND からコンピュータを作る体験
  • Onur Mutlu の YouTube 講義 (CMU/ETH)

🌟 メッセージCPU は魔法ではなく、論理回路の集まり」と腹落ちすると、デバッグも最適化も自信を持って進められます。1 度 nand2tetris を完走すると、世界が変わります。