计算机 Intel CPU 体系结构分析:指令执行、流水线、乱序执行与缓存

2026年7月21日 · 8089 字 · 17 分钟 · CPU Intel 计算机体系结构 Linux

元信息

  • 类型:技术博客 / 体系结构教程
  • 作者/机构:知乎「玩转Linux内核」
  • 阅读日期:2026-07-21
  • 覆盖范围:原文第 1、2、3、4、5、6 节
  • 关联主题:CPU 指令执行、流水线、指令相关性、Linux 内核前置知识
  • 阅读状态:精读到第 6 节

一句话结论

CPU 执行指令可以先用“取指、译码、执行、访存、写回”五阶段理解;现代 Intel CPU 会在此基础上加入流水线、分支预测、uop 解码、寄存器重命名、乱序执行、ROB/MOB 按序提交和多级缓存。核心原则是:内部可以并行、预测和乱序,但最终对程序可见的结果必须符合原始程序顺序。

阅读目的

前 6 节是理解后文内存屏障、load/store buffer、Meltdown 和 Linux 内核并发原语的前置知识。

如果不先理解“CPU 为什么要流水线执行”“流水线为什么会被依赖关系卡住”“乱序执行如何保证最终正确”,后面看内存屏障时会很容易混在一起。

先建立整体图

最基础的一条指令执行路径是:

取指 IF
  -> 译码 ID
  -> 执行 EX
  -> 访存 MEM
  -> 写回 WB

流水线之后,CPU 不是等第 1 条指令完整结束后才开始第 2 条,而是这样重叠推进:

时钟周期 T:
第 1 条指令:执行 EX
第 2 条指令:译码 ID
第 3 条指令:取指 IF

这里的“同时”指的是同一个 CPU 核心内部的不同功能部件在同一个周期里各自工作,不是一个硬件部件同时干多件事。

核心概念

概念 中文解释 在本文中的作用
PC / RIP 程序计数器 / 指令指针,记录下一条要执行的指令地址 取指阶段靠它知道从哪里取下一条指令
IF Instruction Fetch,取指 把指令从内存或指令缓存中取到 CPU 内部
ID Instruction Decode,译码 识别指令类型、操作数位置和后续微操作
EX Execute,执行 由 ALU/FPU 等执行单元完成计算或控制操作
MEM Memory,访存 对需要内存操作的指令,读取或准备写入数据
WB Writeback,写回 把结果写回寄存器、状态寄存器或内存相关结构
流水线 把指令执行过程拆成多个阶段,让不同指令并行经过这些阶段 提高整体吞吐量
断流 流水线因为依赖、资源争用或跳转预测等原因停下来 解释为什么流水线不总是满速运行
数据相关 后一条指令依赖前一条指令的结果 会导致后一条指令不能提前执行
资源相关 多条指令同时争用同一个硬件资源 会导致某些指令等待资源
控制相关 分支、跳转导致下一条指令地址不确定 会导致取错指令或清空流水线
指令调度 在保证语义正确的前提下调整指令执行顺序 减少流水线停顿
乱序执行 Out-of-Order Execution,允许无依赖的指令提前执行 提高执行单元利用率
分支预测 Branch Prediction,提前猜测条件分支走向 减少等待分支结果造成的空转
uop micro-op,CPU 内部执行的微操作 x86 复杂指令会被拆成更规整的内部操作
RAT Register Alias Table,寄存器别名表 做寄存器重命名,消除一部分名字冲突
RS Reservation Station,保留站 等待操作数 ready,挑选可执行的 uop
ROB Reorder Buffer,重排序缓冲区 保存乱序执行结果,按程序顺序提交
MOB Memory Order Buffer,内存顺序缓冲区 管理 load/store 的乱序执行与按序提交
Load Buffer 载入缓冲 暂存提前执行的 load 结果
Store Buffer 存储缓冲 暂存 store,等可以提交时再真正写入缓存/内存

1. CPU 指令执行过程

1.1 取指 IF

取指就是 CPU 根据 PC 或 x86-64 中的 RIP 找到下一条指令地址,然后把指令读进 CPU。

通俗理解:

PC/RIP 像书签,告诉 CPU 当前读到程序的哪一行。

取完一条普通顺序指令后,PC/RIP 会更新到下一条指令的位置。遇到跳转、函数调用、中断返回这类控制流变化时,它会被改成新的目标地址。

1.2 译码 ID

译码就是 CPU 看懂取来的指令。

它要判断:

  • 这是一条加法、访存、跳转还是其他类型的指令。
  • 操作数在哪里,是寄存器、立即数,还是内存地址。
  • 后面需要哪些内部控制信号或微操作。

对 Intel x86 来说,译码尤其重要,因为 x86 是复杂指令集,指令长度不固定,复杂指令可能被拆成多个内部微操作。

1.3 执行 EX

执行阶段是真正完成指令语义的阶段。

例如:

add eax, ebx

执行阶段要把 eaxebx 的值送到 ALU,完成加法,再产生结果。

不同指令会用不同执行单元:

  • 整数加减:常用 ALU。
  • 浮点计算:常用 FPU。
  • 分支判断:需要比较结果并决定控制流。
  • 地址计算:访存指令需要先算出有效地址。

1.4 访存 MEM

不是每条指令都需要访存。

例如:

add eax, ebx

只操作寄存器,不需要访问内存。

但下面这种指令需要访存:

mov eax, [addr]

它要从内存地址 addr 读取数据,再放入 eax

访存通常会先查缓存:

L1 Cache -> L2 Cache -> L3 Cache -> 主内存

缓存命中很快,访问主内存很慢,所以现代 CPU 会围绕访存做大量优化。

1.5 写回 WB

写回就是让执行结果进入后续指令能看到的位置。

常见写回位置:

  • 通用寄存器,例如 eax
  • 状态寄存器里的标志位,例如 zero flag、carry flag。
  • 内存相关缓冲区,最终再写入缓存或内存。

写回后,程序逻辑上就可以继续使用这个结果。

2. CPU 指令流水线

2.1 为什么需要流水线

如果没有流水线,CPU 可能这样工作:

第 1 条:IF -> ID -> EX -> MEM -> WB
第 2 条:IF -> ID -> EX -> MEM -> WB
第 3 条:IF -> ID -> EX -> MEM -> WB

问题是:当第 1 条指令在执行 EX 时,取指单元和译码单元可能空着;当它在写回 WB 时,前面的单元又空着。

流水线就是让这些部件都尽量忙起来:

周期 1:I1-IF
周期 2:I1-ID,I2-IF
周期 3:I1-EX,I2-ID,I3-IF
周期 4:I1-MEM,I2-EX,I3-ID,I4-IF
周期 5:I1-WB,I2-MEM,I3-EX,I4-ID,I5-IF

这样做的重点是:

单条指令延迟没有明显变短,但单位时间完成的指令数量变多了。

2.2 流水线提升的是吞吐量

流水线常见误解是:有了流水线,一条指令就一个周期执行完。

更准确的理解是:

一条指令仍然要走多个阶段;
但流水线装满后,理想情况下每个周期都能完成一条指令。

所以流水线提升的是 throughput,即吞吐量,不是单条指令的全部执行延迟。

2.3 为什么流水线会断流

流水线想高效,需要持续不断地喂入指令。

但现实中会遇到:

  • 后一条指令需要前一条指令的结果。
  • 多条指令同时争用同一个硬件部件。
  • 分支跳转导致下一条指令地址不确定。

这些情况都会让某些阶段等待,形成流水线停顿或清空。

3. 指令的相关性

指令相关性就是:多条指令之间不是完全独立的,它们可能有数据、资源或控制流上的依赖。

流水线越想并行执行,就越需要处理这些依赖。

3.1 数据相关

数据相关指后一条指令要用前一条指令产生的结果。

例子:

add eax, ebx
mov ecx, eax

第二条 mov ecx, eax 要读 eax,但 eax 是第一条 add 刚刚算出来的。如果第一条还没写回,第二条就不能随便读旧的 eax

文章提到三类典型数据相关:

类型 英文 含义 直觉理解
写后读 RAW, Read After Write 后一条要读前一条写出的结果 真依赖,最常见
读后写 WAR, Write After Read 后一条要写的位置,前一条还没读完 名字冲突导致的顺序约束
写后写 WAW, Write After Write 两条指令写同一个位置,最终顺序不能乱 后写的结果应该覆盖前写

解决方法有两类:

  • 编译器插入其他无关指令或空操作,绕开等待。
  • 硬件用数据旁路,把刚算出的结果直接送给后续指令,不必等完整写回。

数据旁路可以理解成:

结果刚从 ALU 出来,还没写回寄存器;
后面的指令急着用,CPU 直接把这份结果转发过去。

3.2 资源相关

资源相关指多条指令在同一个周期争用同一个硬件资源。

例如:

第 1 条指令在 MEM 阶段要访问内存
第 4 条指令在 IF 阶段也要访问内存取指

如果指令和数据共用同一个单端口存储器,就会冲突。

解决思路是增加资源或拆分资源,例如:

  • 分离指令缓存和数据缓存。
  • 增加缓存端口。
  • 增加执行单元。

现代 CPU 常见的 L1 I-cache 和 L1 D-cache 分离,就是为了减少取指和取数互相抢同一资源。

3.3 控制相关

控制相关来自分支和跳转。

例如:

if (x > 0) {
    a = 1;
} else {
    a = 2;
}

CPU 在条件结果出来前,不知道下一条应该取 if 分支还是 else 分支。

如果等结果出来再取指,流水线前端会空等。

如果先猜一条路径,猜对就赚到时间,猜错就要清空错误路径上的指令,重新取正确路径。

文章提到两类处理方式:

方法 核心思想 代价
延迟转移 编译器把与分支无关的指令挪到分支后面先执行 依赖编译器和指令序列
分支预测 硬件根据历史行为猜下一步走哪条路径 猜错要清空流水线

这也是后文乱序执行和预测执行的入口。

4. 指令的动态执行技术

第 4 节是在回答一个问题:

既然流水线会因为相关性停顿,CPU 能不能在保证结果正确的前提下,让不相关的指令先跑?

答案就是动态执行、乱序执行和分支预测。

4.1 指令调度

指令调度就是重新安排指令执行顺序,目标是减少等待。

它分两类:

类型 谁来做 发生时间 作用
静态指令调度 编译器 编译期 编译器重排指令,尽量把无关指令插到等待空隙里
动态指令调度 CPU 硬件 运行期 CPU 根据真实运行状态,决定哪些指令可以提前执行

直觉理解:

编译器调度:出发前规划路线。
硬件调度:路上遇到堵车时现场改道。

静态调度的优势是硬件简单;缺点是编译器不知道运行时缓存命中、分支走向、真实数据依赖等动态情况。

动态调度的优势是更贴近运行现场;缺点是 CPU 硬件复杂,需要更多缓冲区和依赖检测逻辑。

4.2 乱序执行

乱序执行不是“随便乱跑”,而是:

没有依赖的指令可以提前执行;
有依赖的指令必须等依赖满足;
最终提交结果时仍要按程序顺序。

例子:

load r1, [x]    ; 可能很慢,要等内存
add  r2, r3     ; 不依赖 r1,可以先做
add  r4, r1     ; 依赖 r1,必须等 load 结束

如果 CPU 严格顺序执行,第二条 add r2, r3 会被第一条 load 卡住。

如果 CPU 支持乱序执行,它可以先执行第二条,让 ALU 不闲着。

要点:

  • 乱序执行提升的是指令级并行性,即 ILP。
  • CPU 需要判断哪些指令之间没有真实依赖。
  • 乱序执行产生的结果不能立刻对程序可见,必须先放到 ROB/MOB 这类缓冲区。

4.3 分支预测

分支预测解决的是控制相关。

遇到:

if (x > 0) {
    a = 1;
} else {
    a = 2;
}

CPU 不想等 x > 0 的结果完全出来,因为这会让取指、译码等前端阶段空转。

所以 CPU 会根据历史行为预测:

这次更可能走 if 分支,先取 if 分支的指令。

如果猜对:

流水线继续向前,省掉等待时间。

如果猜错:

清空错误路径上的指令和中间结果,重新取正确路径。

关键理解:

分支预测错误不应该改变程序结果,只会造成性能损失。

5. 实例分析:以 Intel Nehalem 为例

第 5 节把前面的理论落到一个真实 Intel CPU 微架构上。读这一节不要被部件名吓住,抓住一条主线:

x86 指令
  -> 取指前端
  -> 解码成 uop
  -> 寄存器重命名
  -> 进入 RS/ROB/MOB
  -> 执行单元乱序执行
  -> ROB/MOB 按序提交

5.0 x86 和 ARM 架构图

原文先放了 x86 和 ARM 的核心架构图,想说明:现代高性能 CPU 即使指令集不同,内部大方向都类似,都会有取指、解码、调度、执行、缓存和乱序执行相关结构。

x86 核心架构图

ARM 核心架构图

这里要记住:

  • x86 是 CISC,外部指令复杂、不等长。
  • ARM 常被归为 RISC,指令更规整。
  • 现代 x86 内部会把复杂 x86 指令拆成类似 RISC 风格的 uop,再进入后端执行。

5.1 取指阶段:I-cache、分支预测、BTB、RSB、ITLB

Nehalem 取指阶段

取指阶段做的不只是“取下一条指令”,还包含很多预测和缓存优化。

主要部件和图中对应关系:

部件 图中对应 作用
L1 I-cache 最上方粉色框里的 Instruction Cache 32 KByte 一级指令缓存,优先从这里取指令,避免每次都访问更慢的内存
Branch Predictor 右侧白色框 Branch Prediction global/bimodal, loop, indirect 分支预测器,猜条件分支走哪边
BTB 图里没有单独画出;原文在图后文字说明中提到 Branch Target Buffer,记录分支目标地址,让 CPU 快速知道预测路径从哪里取指
RSB 图里没有单独画出;原文在图后文字说明中提到 Return Stack Buffer,保存函数返回地址,优化 call/ret 这类控制流
ITLB 最上方粉色框里的 128-entry TLB-4K, 7 TLB-2/4M per thread Instruction TLB,缓存虚拟地址到物理地址的地址翻译结果

所以这张图不是把所有取指相关部件都拆开画出来,而是把 I-cacheITLB 合在最上方缓存/地址翻译框里,把分支预测器画成右侧白色框;BTBRSB 属于分支预测相关结构,但没有独立标注。

取指阶段可以理解成:

预测下一步走哪
  -> 查指令地址翻译
  -> 从 I-cache 取指令
  -> 放入指令队列

一个细节:原文把 RIP 写成 Relative Instruction Point,这里应按常见术语理解为 instruction pointer,即指令指针。

5.2 译码阶段:x86 指令变成 uop

Nehalem 译码阶段

译码阶段的核心是:

外部 x86 macro-op -> 内部 uop

原文提到 Nehalem 有 4 个解码器:

  • 3 个简单解码器:常见简单 x86 指令通常解成 1 个 uop。
  • 1 个复杂解码器:复杂 x86 指令可能解成 1 到 4 个 uop。
  • 极复杂指令:需要 microcode 解码器,可能展开成更多 uop。

这一点很重要,因为后端执行单元真正调度的不是“原始 x86 指令”,而是更小的 uop。

5.3 循环流检测:LSD Buffer

Nehalem LSD Buffer

LSD 是 Loop Stream Detector / Loop Stream Buffer 一类机制。

它解决的问题是:如果程序在跑一个很小的循环,每次都重新取指、分支预测、译码,会浪费前端资源。

所以 CPU 可以把小循环对应的 uop 缓存起来:

小循环反复执行
  -> 直接从 uop 缓冲里取
  -> 少走取指和译码

原文中 Nehalem 的 LSD 在解码之后,可以保存 28 个 uop。直觉上就是“把热循环翻译后的版本缓存起来”。

5.4 乱序执行阶段:RAT、RS、ROB、RRF

Nehalem 乱序执行与寄存器重命名

这一节是全文核心之一。

进入乱序执行前,CPU 要先解决寄存器名字冲突。

比如多条指令都写 eax

mov eax, 1
add eax, 2
mov eax, 9

从程序语义看它们都叫 eax,但 CPU 内部可以把它们映射到不同物理位置或 ROB entry,避免名字冲突限制并行。

几个关键结构:

结构 中文 作用
RAT Register Alias Table,寄存器别名表 把架构寄存器名映射到当前最新的物理寄存器/ROB 状态
RS Reservation Station,保留站 保存等待执行的 uop;操作数 ready 且执行单元空闲时就发射
ROB Reorder Buffer,重排序缓冲区 保存执行结果,保证最后按程序顺序 retire
RRF Retirement Register File,退役寄存器文件 保存已经正式提交、程序可见的寄存器状态

最容易混淆的是 RSROB

RS:负责“谁可以先执行”
ROB:负责“谁可以最终生效”

所以乱序不是从 ROB 里挑指令执行。更准确是:

uop 顺序进入 ROB
uop 同时进入 RS 等待执行
RS 挑 ready 的 uop 乱序发射
执行结果回填 ROB
ROB 按原始顺序提交

这就是“乱序执行,按序提交”。

5.5 执行单元:超标量和执行端口

Nehalem 执行端口

超标量的意思是:一个核心里有多个执行端口/执行单元,可以在同一周期执行多条互不依赖的 uop。

Nehalem 原文提到 6 个执行端口,但不是每个端口都能做所有事情:

  • 有些端口偏整数 ALU。
  • 有些端口处理浮点或 SSE。
  • 有些端口负责分支。
  • 有些端口负责 load/store。

所以“最多 6 个端口”不等于“任何时候都能完成 6 条任意指令”。

真正性能受多个瓶颈影响:

  • 前端每周期能取多少指令。
  • 解码器每周期能产出多少 uop。
  • RS/ROB/MOB 容量够不够。
  • 执行端口类型是否匹配。
  • load/store 是否被缓存延迟卡住。

5.6 存取单元:Load/Store 为什么最麻烦

Nehalem Load/Store 单元

存取单元处理内存读写。原文提到 Nehalem 有 3 个存取相关单元:

  • Load 地址和数据。
  • Store 地址。
  • Store 数据。

这里的难点是:内存读写之间的依赖比寄存器依赖更难判断。

寄存器依赖容易看:

add eax, ebx
mov ecx, eax

后一条明确读 eax

内存依赖难在地址可能运行时才知道:

store [addr1], eax
load  ebx, [addr2]

如果 addr1 == addr2,load 不能越过 store。

如果 addr1 != addr2,load 提前做通常没问题。

CPU 需要判断或预测这两者是否别名,也就是 Memory Disambiguation。

内存依赖示例

Memory Disambiguation 示意

原文提到 Core 微架构的策略偏激进:Load 往往会提前执行,除非预测器认为它不能越过前面的 Store。

如果猜错了:

提前执行的 load 作废
清空相关流水线
重新按正确顺序 load

性能上损失一些周期,但多数情况下能赚到更多等待时间。

Load/Store 重排示例

5.7 ROB 和 MOB 的关系

ROB 管寄存器结果的按序提交,MOB 管内存访问的按序提交。

可以这样记:

普通计算结果 -> 先放 ROB
内存读写结果 -> 先放 MOB / load buffer / store buffer
最终都要保证按程序语义提交

对 load:

  • 可以提前执行。
  • 结果暂存在 load buffer / MOB。
  • 如果分支预测或内存依赖预测错了,可以丢弃。
  • 但提前 load 可能把数据带入 cache,这也是 Meltdown 类漏洞的背景之一。

对 store:

  • store 会修改数据,副作用更重。
  • 不能随便把猜测路径上的 store 真正写入 cache/内存。
  • 通常需要等确认可以提交后,再从 store buffer 进入缓存层级。

这就是为什么后面谈内存屏障时,总会围绕 load/store buffer 展开。

6. 缓存 Cache

第 6 节很短,主要是在补背景:乱序执行和 load/store 优化离不开缓存层级。

6.1 L1/L2/L3 的作用

CPU 访问数据通常不是直接访问主内存,而是先查缓存:

L1 Cache:最快,容量最小,通常每个核心私有
L2 Cache:比 L1 慢,容量更大,通常每个核心私有或半私有
L3 Cache:更慢,容量更大,常被多个核心共享
主内存:最慢

缓存存在的原因是:CPU 太快,主内存太慢。如果每条 load 都去主内存,流水线和执行单元会大量空等。

6.2 非独占 / 包含式缓存

原文说 Nehalem 的 L3 是非独占设计,也可理解为包含式缓存:L3 包含 L1/L2 中的数据副本。

粗略理解:

包含式:上层小缓存的数据,也能在下层大缓存中找到副本。
独占式:不同层缓存尽量不重复存同一份数据。

包含式 L3 的好处是:多核场景下更容易判断某个缓存行是否可能在其他核心的私有缓存里。

代价是:缓存容量会有重复占用。

6.3 缓存一致性和 NUMA

多核 CPU 中,不同核心可能缓存同一块内存。

问题是:

核心 0 修改了变量 x;
核心 1 的缓存里也有旧的 x;
核心 1 之后读 x 时应该看到什么?

这就需要缓存一致性协议,例如 MESI/MESIF。

原文没有展开 MESI,只是指出 L3 命中或未命中时,CPU 需要结合缓存状态、核心有效位、NUMA 位置来决定后续访问路径。

对 Linux 内核阅读来说,这里先记住:

缓存让单核更快;
多核缓存带来一致性问题;
内存屏障和锁会受到缓存一致性与 store/load buffer 影响。

我的理解

前 6 节真正要建立的是一个心智模型:

CPU 想让内部所有部件尽量忙起来;
流水线让不同指令重叠执行;
相关性会破坏重叠执行;
乱序执行、分支预测、ROB/MOB 都是在解决“如何既并行又正确”;
缓存让访存变快,但也引入多核一致性和内存可见性问题。

所以不要把 CPU 理解成“按代码顺序一条一条机械执行”的机器。更准确的说法是:

CPU 内部会尽量并行、提前、重排;
但最终必须让程序看到符合语义的结果。

常见误解

误解 更准确的理解
一个 CPU 核心只能同一时刻做一件事 一个核心内部有多个功能部件,可以让多条指令处在不同流水阶段
流水线让单条指令变成一个周期完成 单条指令仍需多个阶段,流水线主要提升整体吞吐量
指令顺序永远不能变 只要不破坏依赖关系,现代 CPU 和编译器都可能调整实际执行顺序
分支预测错了会导致程序结果错 预测错会清空错误路径,结果不应对程序可见,代价是性能损失
乱序执行就是结果乱了 乱序的是内部执行顺序,最终提交仍要符合程序语义
ROB 负责挑选哪条指令先执行 RS 更接近“挑选 ready 指令执行”的位置,ROB 主要负责保存结果和按序提交
Load/Store 和普通寄存器运算一样好处理 内存地址可能运行时才知道,load/store 依赖判断比寄存器依赖复杂

和当前主题的关系

这篇文章不是直接讲 Linux 内核代码,但它是理解内核并发、内存屏障和锁机制的重要前置材料。

后续阅读 Linux 内核时会遇到:

  • barrier()smp_mb()smp_rmb()smp_wmb()
  • 原子操作和锁。
  • 中断与并发路径。
  • 多核缓存一致性。

这些内容背后都依赖一个事实:现代 CPU 为了性能会流水线、预测、乱序、缓存;内核必须在必要位置约束这些行为,保证并发语义正确。

疑问

问题 为什么重要 下一步
原文第 2 节中“标量流水计算机满载时每个周期可以执行 2 条以上指令”的表述需要核实 标量流水线通常理解为单发射,一般理想吞吐是每周期完成 1 条;“2 条以上”更像超标量描述 后续查体系结构教材或 Intel 手册确认
x86 中 PCEIPRIP 的区别需要单独整理 后面读系统调用、中断、上下文切换时会频繁遇到指令指针 写入术语表或概念卡
流水线停顿、流水线清空、乱序执行回滚之间的边界需要继续拆 后文 ROB/MOB 和 Meltdown 会用到这些概念 继续精读原文第 7 节总结
Nehalem 相关数字是否适用于现代 Intel CPU 原文用 Nehalem 举例,现代 Intel 微架构已有变化 后续查 Intel Optimization Manual 或现代微架构资料
MESI/MESIF 和 Linux 内存屏障的关系如何串起来 后面理解 smp_mb()、锁和原子操作需要这条链路 单独整理缓存一致性概念卡

可迁移启发

  • 看 CPU 性能问题时,不只看单条指令耗时,还要看流水线是否被依赖、分支、访存拖住。
  • 看并发和内存屏障时,要记住“程序顺序”和“CPU 内部实际执行顺序”不是同一件事。
  • 学 Linux 内核前,需要先建立 CPU 执行模型,否则很难理解锁、屏障、原子操作为什么存在。
  • 分析性能时,不能只看“有几个执行单元”,还要看前端解码、依赖、缓存、ROB/MOB 容量和提交带宽。
  • 分析并发正确性时,要区分“执行了”“提交了”“对其他核心可见了”这三个层次。

来源摘记

  • 原文第 1 节:CPU 指令执行可分为取指、译码、执行、访存、写回。
  • 原文第 2 节:流水线把重复时序过程拆成多个子过程,让不同功能段并行工作。
  • 原文第 3 节:流水线中的指令相关性包括数据相关、资源相关和控制相关。
  • 原文第 4 节:动态调度允许 CPU 在保持依赖正确的前提下乱序执行不相关指令。
  • 原文第 5 节:Nehalem 示例展示了取指、译码、LSD、RAT、RS、ROB、执行端口、MOB 和 load/store buffer 如何串起来。
  • 原文第 6 节:缓存层级和一致性机制是理解内存访问、NUMA 和后续内存屏障问题的背景。