现代 CPU 架构
现代 CPU 融合了多核、超线程、乱序执行、分支预测、缓存层次等数十种技术——让单个芯片的算力达到数十亿次运算每秒
从单核到多核
2000 年代初,CPU 频率达到约 4 GHz 后遇到了功耗墙(Power Wall)——频率每提升一点,功耗和发热量大幅增加,散热跟不上了。
芯片厂商的应对方案:不再追求单核频率,而是增加核心数量。
2000 年: 2020 年:
┌──────────────┐ ┌──────┬──────┐
│ Pentium 4 │ │ Core0│ Core1│
│ 单核 │ ├──────┼──────┤
│ 3.8 GHz │ │ Core2│ Core3│
└──────────────┘ ├──────┼──────┤
│ L3 缓存(共享)│
└──────────────┘
多核架构
现代多核 CPU 内部:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Core 0 │ │ Core 1 │ │ Core 2 │ │ Core 3 │
│ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │
│ │L1 I/D│ │ │ │L1 I/D│ │ │ │L1 I/D│ │ │ │L1 I/D│ │
│ ├──────┤ │ │ ├──────┤ │ │ ├──────┤ │ │ ├──────┤ │
│ │L2 │ │ │ │L2 │ │ │ │L2 │ │ │ │L2 │ │
│ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
└──────────────┴──────┬───────┴──────────────┘
│
┌──────────┐
│ L3 缓存 │(所有核心共享)
└──────────┘
│
┌──────────┐
│ 内存控制器│(集成在 CPU 内)
└──────────┘
│
RAM
每个核心:
- 有自己的 L1/L2 缓存
- 可以独立执行程序
- 通过 L3 缓存和内存控制器共享数据
对称多处理(SMP)
现代操作系统中,每个核心被当成独立的 CPU 看待:
OS 视角:
CPU0 ── 进程 A, 进程 D
CPU1 ── 进程 B, 进程 E
CPU2 ── 进程 C
CPU3 ── 空闲
缓存一致性由 MESI 协议 保证——一个核心修改了数据,其他核心的对应缓存行自动失效。
超线程(Hyper-Threading / SMT)
Simultaneous Multithreading(SMT)——让一个物理核心同时运行两个线程。
传统单核:
时间 → ████████████████████████████████████
线程 A 独占总线
SMT 双线程:
时间 → ████▓▓▓▓████▓▓▓▓████▓▓▓▓████▓▓▓▓████
线程 A ██ 线程 B ██ 共享执行单元
工作原理:一个物理核心有两个逻辑处理器,它们共享执行单元(ALU、FPU),但各自有独立的寄存器组和 PC:
物理核心:
┌────────────────────────────┐
│ ┌──────┐ ┌──────┐ │
│ │ PC_A │ │ PC_B │ │ ← 两个逻辑 PC
│ ├──────┤ ├──────┤ │
│ │ Regs_A│ │ Regs_B│ │ ← 两套寄存器
│ ├──────┴──┴──────┤ │
│ │ 共享执行单元 │ │ ← ALU、FPU、缓存共享
│ │ ALU, FPU, L1 │ │
│ └────────────────┘ │
└────────────────────────────┘
收益:让执行单元不再”饿着”——当一个线程在等内存时,另一个线程可以用 ALU。
| 指标 | 物理核数 | 逻辑核数(开 SMT) | 性能提升 |
|---|---|---|---|
| 4 核 | 4 | 8 | ~30%(不是翻倍!) |
| 8 核 | 8 | 16 | ~20-30% |
💡 SMT 的收益取决于工作负载——数值计算提升小(因为 ALU 一直在忙),数据库/Web 服务提升大(因为经常在等内存和 I/O)。
乱序执行(Out-of-Order Execution)
早期 CPU 严格按照程序顺序执行指令。但如果下一条指令需要等上一条的结果呢?——流水线停顿。
乱序执行(OoOE) 让 CPU 在”等待”时执行后面不依赖当前结果的指令:
; 顺序执行:
LOAD R1, [mem] ; 等内存(50 个周期)→ 流水线停顿
ADD R2, R3, R4 ; 不依赖 R1!但只能干等
; 乱序执行:
LOAD R1, [mem] ; 开始加载(等内存)
ADD R2, R3, R4 ; ✅ R2 的计算不依赖 R1,先执行!
SUB R5, R6, R7 ; ✅ 也不依赖,也先执行!
; ...(LOAD 完成了)
ADD R8, R1, R9 ; ✅ 现在 R1 可用了,真正需要 R1 的指令才执行
乱序执行的核心结构
指令流 → ┌──────────────┐ → ┌──────────────┐ → ┌──────────────┐ → 提交结果
│ 取指解码 │ │ 重排序缓冲 │ │ 执行单元 │
│(按程序顺序) │ │ ROB + RS │ │(可乱序执行)│
└──────────────┘ └──────────────┘ └──────────────┘
按序进入 调度器 按序提交
乱序发射 (保证精确异常)
三个关键组件:
① 重排序缓冲(Reorder Buffer, ROB):记录每条指令的状态,确保结果按序提交。
② 保留站(Reservation Station, RS):等待操作数的指令暂存区——操作数就绪后立即发射到执行单元。
③ 公共数据总线(CDB):执行结果广播给所有保留站,唤醒等待该结果的指令。
保留站中:
┌─────────────────────────────────────┐
│ 指令 操作数1 操作数2 │
│─────────────────────────────────────│
│ ADD R2, R3, R4 R3=R3 R4=R4 │← 就绪,可发射
│ SUB R5, R6, R7 R6=R6 R7=R7 │← 就绪,可发射
│ ADD R8, R1, R9 R1=等待中 R9=R9 │← 等 R1,阻塞
│ LOAD R1, [mem] 已发射 - │← 在等内存
└─────────────────────────────────────┘
↑ ↑
当 R1 到达 CDB 唤醒 ADD R8
🔑 乱序执行的本质是:CPU 把指令调度到执行单元,不按程序顺序,而是按操作数就绪顺序。
超标量(Superscalar)
超标量 CPU 有多个执行单元,可以在一个时钟周期内发射多条指令:
非超标量(单发射): 超标量(3 发射):
每个周期最多 1 条指令 每个周期最多 3 条指令
T1: LOAD T1: LOAD ADD SUB ← 3 条并行执行!
T2: ADD T2: STORE MUL AND
T3: STORE T3: ...
现代 CPU 的执行单元:
执行单元池(以 Skylake 为例):
┌────────────────────────────────────────────────┐
│ 整数 ALU │ 整数 ALU │ 整数 ALU │ 整数 ALU │ ← 4 个
├──────────────┼──────────────┼──────────────┼──────────────┤
│ 浮点加法 │ 浮点乘法 │ 浮点除法 │ 向量单元 │
├──────────────┼──────────────┼──────────────┼──────────────┤
│ 内存加载 │ 内存加载 │ 内存存储 │ 分支处理 │
└──────────────┴──────────────┴──────────────┴──────────────┘
💡 现代桌面 CPU 每周期最多可以发射 4-6 条指令(如果指令之间没有依赖关系)。
分支预测器
指令流水线 中提到分支预测。现代分支预测器已经非常复杂:
一级预测(简单历史):
BEQ 上次跳了吗?→ 预测这次也跳/不跳
二级预测(全局历史):
记录最近 N 次分支的结果 → 预测下一跳
三级预测(混合预测器 + AI 风格):
- TAGE 预测器:使用多张历史表,按历史长度匹配
- 循环预测器:专门预测固定次数的循环
- 统计预测器:用神经网络风格的权重
现代分支预测器的准确率:> 97%(服务器负载下)到 > 99.9%(科学计算)。
推测执行(Speculative Execution)
CPU 不仅仅预测分支——它还会推测执行预测方向后的代码:
BEQ R1, R2, else ; 预测不跳转
ADD R3, R4, R5 ; ← CPU 推测执行这条
LOAD R6, [R7] ; ← 甚至继续往下推测执行
; 如果预测正确:这些指令的结果直接可用
; 如果预测错误:丢弃推测结果,回滚
推测执行是乱序执行的延伸——但也是安全漏洞的来源(如 Spectre、Meltdown 漏洞,推测执行访问了不该访问的内存,留下了可测量的痕迹)。
现代 CPU 的完整执行流程
① 取指(Fetch):
从 L1 指令缓存读取 16-32 字节指令
预解码(标记指令边界)
② 解码(Decode):
x86:解码为微操作(μops)
RISC:直接解码
每周期解码 4-6 条指令
③ 重命名(Register Renaming):
把架构寄存器映射到物理寄存器
消除"写后写"(WAW)和"写后读"(WAR)依赖
④ 调度(Schedule):
放到保留站 → 操作数就绪 → 发射到执行单元
⑤ 执行(Execute):
多个执行单元并行执行
结果通过 CDB 广播
⑥ 提交(Commit):
按程序顺序提交结果到 ROB
保证精确异常——异常发生时可以正确回滚
⑦ 写回(WriteBack):
结果写入寄存器文件和缓存
功耗与散热
现代 CPU 面临的核心矛盾:
性能 ∝ 频率² × 核心数
功耗 ∝ 频率³ × 核心数
这意味着频率提升 10%,功耗增加约 33%。这就是为什么 CPU 频率停滞在 4-5 GHz 附近。
省电技术
| 技术 | 做法 | 节电效果 |
|---|---|---|
| 动态频率调整 | 负载低时降频 | 30-50% |
| 休眠状态(C-states) | 空闲核心进入休眠 | 接近零功耗 |
| 时钟门控 | 关闭不用单元的时钟 | 减少动态功耗 |
| 电源门控 | 关闭不用单元的电源 | 减少漏电功耗 |
| DVFS(动态调压调频) | 降低电压配合降频 | 功耗∝V² |
大小核架构(big.LITTLE / Hybrid)
现代 CPU 混合使用高性能大核和省电小核:
Intel Core i7-12700(混合架构):
┌──────────────┐ ┌──────────────┐
│ P-core × 6 │ │ E-core × 4 │
│ 高性能核心 │ │ 能效核心 │
│ 5.0 GHz │ │ 3.6 GHz │
│ 大 L2 缓存 │ │ 小 L2 缓存 │
└──────────────┘ └──────────────┘
OS 调度器把前台任务放 P-core,后台任务放 E-core——既保证了响应速度,又延长了续航。
从数字到感觉
// 现代 CPU 一秒钟可以做的事情:
3 GHz × 4 核 × 1.5 IPC ≈ 180 亿条指令/秒
// 如果你每秒写一条指令:
// ≈ 570 年才能赶上 CPU 一秒的工作量!
小结
现代 CPU 是几十项技术的融合:
| 技术 | 解决的问题 | 效果 |
|---|---|---|
| 多核 | 功耗墙限制了单核频率 | 通过并行提升吞吐量 |
| 超线程(SMT) | 单个核心的执行单元可能空闲 | 利用率提升 20-30% |
| 乱序执行 | 指令等待数据时流水线停顿 | 隐藏内存延迟 |
| 超标量 | 每周期只能执行一条指令 | 每周期执行 4-6 条 |
| 分支预测 | 分支导致流水线冲刷 | 准确率 > 97% |
| 推测执行 | 等待分支结果时浪费周期 | 利用等待时间提前执行 |
| 大小核架构 | 性能和功耗的矛盾 | 兼顾爆发性能和日常省电 |
为什么这很重要? 现代 CPU 不是一个”每周期执行一条指令的简单机器”——它是一个极其复杂的并行引擎,同时处理几十条指令,预测未来、推测执行、动态调度。理解这些技术,你才算真正理解了你每天都在使用的”CPU”到底是什么。
至此,硬件板块全部完成! 你已经走完了从数字电路→逻辑门→CPU 核心→存储系统→现代处理器架构的完整旅程。接下来,你将从硬件视角切换到软件视角——从我们之前完成的汇编语言进入操作系统(02-os) 的世界。