硬件地图

点一个框看它是什么、在哪篇讲过;选一条路径,一步一步看指令和数据怎么走。蓝字可以直接点开对应的文章。

硬件地图:一次访存会经过哪些层 蓝字 = 在哪里讲过(#N = 交易系统笔记 #N,MESI = 专题,速查 N.N = 速查页的小节)。数字都是量级。 实线箭头 = 指令和数据怎么走;红色虚线 = 分支猜错后冲刷、重新取指;灰色虚线 = 核与核之间的一致性消息。 CPU 插槽 0(socket)= 一个 NUMA 节点 #1 绑核 / 隔离 · NUMA 核心 0(core) 2 个硬件线程共用这个核(SMT,超线程,#1) 前端(front end):取指令 后端(back end):乱序执行 访存(memory):load / store 分支预测器(BPU) BTB · RAS · 方向预测器 猜下一条指令在哪 #7 分支预测 取指(fetch) 按预测的地址从 L1I 取指令 猜错的路径上取的全部作废 #7 推测执行 译码(decode) 指令 → 微操作(µop) L1I 32 KiB(指令缓存) 存指令,不存数据 #6 函数分组 重命名 + ROB ROB(reorder buffer,重排序缓冲) 记着还没退休的指令 速查 2.3 ROB 与 MLP · #16 PAUSE 调度 → 执行单元 ALU 算数 · AGU 算地址 分支在这里算出真假 #2 依赖链 · 多累加器 · #12 除法器 退休(retire) 按程序顺序提交结果 寄存器(register file) #2 寄存器溢出 #6 存储类别 dTLB(数据 TLB,地址翻译) 没命中:从 CR3 指的根往下走 4 级 #6 2 MiB 大页 · #3 TLB shootdown 速查 2.1 TLB 覆盖范围 Store buffer 存储缓冲 写先在这排队, 按顺序写进 L1D #2 store buffer #2 store-to-load 转发 Invalidate queue 失效队列 别的核发来的 invalidate 请求先排队 #2 invalidate queue #2 acquire L1D 32 KiB · 8 路(数据缓存) LFB(line fill buffer,行填充缓冲) #6 对齐 / 跨行 · 组相联 速查 2.3 LFB L2 512 KiB · 8 路(本核私有,指令和数据共用) #6 关键步长 64 KiB · #2 L1/L2 私有 缓存和内存之间按 64 B 一整条“缓存行”搬运 #2 · #6 一次访问碰几条行 µop 提交 store 核心 1 … 核心 5(结构和核心 0 相同) 核心 1 前端 后端 L1I · L1D L2 核心 2 前端 后端 L1I · L1D L2 核心 3 前端 后端 L1I · L1D L2 核心 4 前端 后端 L1I · L1D L2 核心 5 前端 后端 L1I · L1D L2 MESI:单写者 / 多读者 同一条缓存行可以在多个核的 L1/L2 里各有一份副本。 谁要写,就得先让别人的副本作废(发 invalidate 请求)。 MESI 专题 #2 伪共享(两个核抢同一行) #4 缓存游标 · #5 读者越多写越贵 #6 alignas(64) 让对象独占一行 #16 自旋锁:TTAS、排队锁、umwait 每个核各有自己的 L1、L2 (TLB、store buffer 也各有一份) 别的核要写某一行:发 invalidate 请求,排进核心 0 的 invalidate queue 一致性 L3 16 MiB · 16 路(全部核共享) #2 L3 共享 · #6 高位哈希选组 内存控制器:把连续地址轮流分到各个通道(交织) #6 多通道均衡(行数和通道数互质) 内存条(DRAM) #6 通道 0 DIMM → rank → 多个 bank 一个 bank = 行 × 列的表 行缓冲:当前打开的那一行 命中已打开的行 ~15 ns 换一行(行冲突)~40 ns 刷新:每 ~7.8 µs 一次, 卡 300–500 ns → P99 毛刺 通道 1 结构同通道 0 各通道能同时工作, 带宽 ≈ 通道数 × 单通道 通道 2 … 结构同通道 0 页表(每个进程一份) 4 级的树,CR3 指着根那一页 和普通数据一样散在各通道里 页表项会被缓存进 L1/L2/L3, TLB 没命中多半不用真来这里 进程线程 4.2 · #3 TLB shootdown CPU 插槽 1 自己的核、L3、内存控制器和内存 访问另一个插槽的内存要过互连,更慢 #1 NUMA:线程和内存放同一节点 速查 2.1 first-touch 放置 插槽间互连 网卡 → 中断 → 某个核 #1 中断绑到非热路径的核 (操作系统层,不是缓存层) 一个地址的各段(4 KiB 页,L1D 32 KiB 8 路,64 B 行) 虚拟页号(bit 12 以上)→ TLB 翻译 大页:页号变少,TLB 装得下 组号 bit 6–11 #6 相差 4 KiB 倍数 → 同一组 行内偏移 bit 0–5 #6 对齐 = 这几位为 0 页内偏移 bit 0–11:翻译前后不变,所以 L1 能一边翻译一边选组(VIPT) → L1 的“组数 × 行大小”被卡在 4 KiB,做大只能加路数 访问延迟量级 周期按约 4 GHz 折算。每往下一层,大约慢 3–10 倍。 访问时间 容量 谁来管理 ~1 周期(< 1 ns) 约 1 KB 编译器 寄存器 4–5 周期(~1 ns) 32–48 KiB / 核 硬件 L1D 缓存 ~12–20 周期(~3–5 ns) 0.5–2 MiB / 核 硬件 L2 缓存 ~40–120 周期(~10–30 ns) 约 10–500 MiB,全部核共享 硬件 L3 缓存 ~300–500 周期(~70–120 ns) 约 16 GB 到 4 TB 操作系统(页表) 本地内存 ~500–800 周期(~120–200 ns) 另一个插槽的内存 操作系统 + 你绑的位置 远端 NUMA 内存 10–100 µs TB 级 操作系统(文件系统) NVMe SSD 核内 芯片内 芯片外 外部设备 数字都是帮助理解的量级,不对应哪台机器。 图随文章更新:后续文章讲到新的硬件层时再往上补标注。