H10 —— CPU 里的内存控制器:IMC 的架构和它与核心的交互

金句:最好的内存控制,是让 CPU 感受不到延迟的存在。


内存控制器(IMC)的位置

内存控制器(Memory Controller)是 CPU 和 DRAM 之间的桥梁,现代 CPU 把 IMC 集成在 CPU 芯片上。

Bash
CPU 芯片架构(以 Intel Skylake 为例):

  ┌─────────────────────────────────────────────────┐
  │                  CPU Package                     │
  │  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
  │  │  Core 0  │  │  Core 1  │  │  Core 2   │  ... │
  │  │ L1/L2    │  │ L1/L2    │  │ L1/L2     │      │
  │  └────┬─────┘  └────┬─────┘  └────┬─────┘      │
  │       │              │              │           │
  │  ┌────┴─────────────┴──────────────┴────┐      │
  │  │     Ring Bus / Mesh Interconnect      │      │
  │  └────┬─────────────┬──────────────┬────┘      │
  │       │             │              │             │
  │  ┌────┴────┐   ┌────┴────┐  ┌─────┴─────┐      │
  │  │   IMC    │   │  System  │  │   PCIe    │      │
  │  │ Channel0 │   │  Agent   │  │ Controller│      │
  │  │ Channel1 │   │          │  │           │      │
  │  └─────────┘   └──────────┘  └───────────┘      │
  └─────────────────────────────────────────────────┘

IMC(Integrated Memory Controller)的位置:
  - IMC 在 CPU 芯片上(Integrated)
  - 通过 Ring Bus(核心数少)或 Mesh(核心数多)和核心连接
  - 直接连接到 DDR PHY(物理层),再到 DIMM 插槽

vs 早期架构(North Bridge):
  - 早期 CPU 是"CPU + North Bridge + South Bridge"分离
  - 内存控制器在 North Bridge 里
  - CPU 和内存之间通过 FSB(Front Side Bus)通信
  - 延迟高(~80ns),带宽低

内存控制器的架构

Bash
IMC 的内部结构(DDR4 双通道):

  ┌──────────────────────────────────────────────────┐
  │                 Memory Controller                  │
  │                                                    │
  │  ┌────────────┐    ┌────────────┐                │
  │  │  Channel 0  │    │  Channel 1  │                │
  │  │ DDR4-3200   │    │ DDR4-3200   │                │
  │  │ 64-bit      │    │ 64-bit      │                │
  │  └──────┬──────┘    └──────┬──────┘                │
  │         │                  │                        │
  │    ┌────┴──────────────────┴────┐                │
  │    │     Address Decode / Routing │                │
  │    └─────────────────────────────┘                │
  │                                                    │
  │  ┌────────────────────────────────────────┐       │
  │  │   Read/Write Queue (Scheduler)         │       │
  │  │   - 命令重排(Command Reordering)       │       │
  │  │   - 行冲突优化(Bank Conflict)          │       │
  │  │   - 读优先(Read Prioritization)        │       │
  │  └────────────────────────────────────────┘       │
  │                                                    │
  │  ┌────────────────────────────────────────┐       │
  │  │   ECC Engine(纠错)                      │       │
  │  │   - SECDED 计算                          │       │
  │  │   - 地址保护                             │       │
  │  └────────────────────────────────────────┘       │
  └──────────────────────────────────────────────────┘

IMC 的核心功能

  1. 地址解码:把物理地址翻译成 Channel/Rank/Bank/Row/Column
  2. 命令调度:重排内存访问顺序,最小化行冲突
  3. ECC 纠错:计算和检查 ECC(RDIMM 或 ECC UDIMM)
  4. 时序控制:发送 ACT/READ/WRITE/PRE/REF 命令

内存控制器的调度器(Scheduler)

内存访问的调度,是性能的关键。

Bash
调度器的优化策略:

1. 命令重排(Command Reordering):
   - 尽量把同一行的访问放在一起(行命中)
   - 避免行冲突(行切换)
   - 把 READ 优先于 WRITE(CPU 需要数据才能继续)

2. 读优先(Read Prioritization):
   - 读操作通常影响指令执行(stall)
   - 写操作可以延迟(Write Buffer)
   - 调度器通常优先处理 READ

3. 行冲突优化(Bank Conflict Avoidance):
   - 如果两个请求访问不同行,先访问已打开的行
   - 把 PRE 和 ACT 延迟到真正需要时(Lazy Precharge)

4. 刷新协调(Refresh Coordination):
   - 刷新期间,IMC 会延迟非紧急请求
   - 如果刷新和紧急请求冲突,IMC 可能延迟刷新(tREFI 内完成即可)

实际调度效果:

  请求序列(无调度):
    Request A: Row=1 → ACT → READ → PRE
    Request B: Row=2 → PRE → ACT → READ → PRE
    Request C: Row=1 → ACT → READ
    → 总时间:3×ACT + 3×PRE + 2×READ = ~100ns

  请求序列(有调度):
    Reorder: C 先(A 已经有 Row 1 打开)
    Request C: Row=1(打开)→ READ(直接读)
    Request A: Row=1(打开)→ READ(直接读)
    Request B: Row=2 → ACT → READ → PRE
    → 总时间:2×READ + ACT + READ + PRE = ~60ns

  优化效果:减少 40% 延迟

IMC 和 CPU 核心的连接:Ring Bus vs Mesh

Bash
多核 CPU 的内存控制器连接方式:

Ring Bus(Intel Haswell ~ Coffee Lake,4~8 核):

  ┌─────────────────────────────┐
  │    Core0 ─── Ring ─── Core1 │
  │       │                │    │
  │    Core2 ─── Ring ─── Core3 │
  │       │                │    │
  │    IMC ── Ring ── Cache Agent │
  └─────────────────────────────┘

  - 所有核心和 IMC 都在同一个环上
  - 访问延迟 = 跳数 × 每跳延迟
  - 最坏情况:4 核系统,Core 0 到 IMC 要跳 2 站
  - 典型延迟:50~60ns(L3 miss + IMC)

Mesh(Intel Skylake,10+ 核):

  ┌───────┬───────┬───────┬───────┐
  │ Core0 │ Core1 │ Core2 │ Core3 │
  ├───┬───┼───┬───┼───┬───┼───┬───┤
  │IMC│   │   │   │   │   │   │ IMC│
  ├───┴───┼───┴───┼───┴───┼───┴───┤
  │ Core4 │ Core5 │ Core6 │ Core7 │
  └───────┴───────┴───────┴───────┘

  - 每行/列交叉点是路由器
  - 路径可以绕过拥塞点
  - 延迟增长比 Ring Bus 慢(O(sqrt(n)) vs O(n))
  - 典型延迟:45~55ns(取决于位置)

AMD Zen 架构的 Infinity Fabric:
  - 每 4 核(CCD)一个 Fabric 节点
  - 节点之间用 Infinity Fabric 互联
  - 延迟取决于跨节点访问的跳数

内存控制器和 NUMA

在多路服务器(2P/4P)上,每个 CPU 都有自己的 IMC:

Bash
NUMA(Non-Uniform Memory Access):

2 路服务器(2 CPUs):

  CPU 0:
    - 核心 0~7
    - IMC → Channel 0~3(本地内存)
    - 连接到 CPU 1 的 UPI 链路

  CPU 1:
    - 核心 8~15
    - IMC → Channel 0~3(本地内存)
    - 连接到 CPU 0 的 UPI 链路

访问延迟:
  - CPU 0 访问本地内存:~60ns
  - CPU 0 访问 CPU 1 的内存:~100ns(跨 UPI)

NUMA 感知操作系统:
  - Linux 可以用 numactl 绑核和内存在同一节点
  - 进程尽量访问本地内存,避免跨节点访问

  # 将进程绑定到 Node 0
  numactl --membind=0 --cpunodebind=0 ./my_program

这就是为什么高性能数据库需要 NUMA 优化——
把数据放在访问它的 CPU 对应的内存节点上。

总结

  • IMC 位置:集成在 CPU 芯片上(Intel/AMD 现代 CPU),通过 Ring Bus 或 Mesh 和核心连接
  • IMC 功能:地址解码、命令调度(重排优化)、ECC 纠错、时序控制
  • 调度器优化:命令重排(行命中优先)、读优先、Lazy Precharge、刷新协调
  • Ring Bus:适合 4~8 核,延迟 ~50~60ns,最坏跳数 2~3
  • Mesh:适合 10+ 核,延迟 ~45~55ns,路径可绕过拥塞
  • NUMA:多路 CPU 每路有自己的 IMC,本地访问快,跨节点访问慢(需要 numactl 优化)

H 层总结:Hardware 层从 DIMM 结构(H01)到双通道(H02)到时序(H03)到 XMP/EXPO(H04)到 SPD/Training(H05)到 PCB 走线(H06)到功耗(H07)到 ECC(H08)到制造测试(H09)到 IMC 架构(H10)——全面覆盖了内存硬件从物理连接到 CPU 控制器交互的完整链路。

下篇预告(B01):计算机是怎么知道内存有多大、内存在哪里的?e820 内存映射表——BIOS 在开机时给操作系统的第一份”地图”。


关注公众号「AI不着急」,回复”资料”获取内存学习路线图。

最后修改: 2024年4月22日

作者

评论

发表评论

您的邮箱地址不会被公开。