一条 x86 指令跑 62 秒:Assembly Hall of Shame 如何找到 CPU 最慢的指令

x86 指令延迟排行榜(对数刻度)

2026 年 8 月 7 日,安全研究员 Christopher Domas(@xoreaxeaxeax)发布了新项目 Assembly Hall of Shame(汇编耻辱堂)。项目目标只有一个:让 CPU 慢到极限。

大多数性能研究都在想办法让代码跑得更快。Domas 反过来,系统性地寻找 x86 指令集中单条指令能慢到什么程度。结果是:最快的 nop 指令 1 个周期(0 纳秒),最慢的 fxrstor64 跑了 1980 亿个周期,耗时 62 秒。两者相差超过 11 个数量级。

排行榜:从 0 纳秒到 62 秒

项目维护了一份 x86 指令延迟排行榜,按周期数从快到慢排列。以下是关键节点:

排名指令策略周期数耗时硬件
27nop什么都不做10 nsIntel i7-8559U
24idiv128 位除法溢出触发最长微码路径7728 nsIntel i7-8559U
22fsin特殊值触发微码浮点处理25794 nsIntel i7-8559U
16split lock跨缓存行边界原子锁,强制外部总线锁865319 nsIntel i7-8559U
13rdrand循环耗尽硬件熵池5,5792.0 μsIntel i7-8559U
12wrmsr写 MCG_CTL,触发 Zen 微码全局同步34,30410.7 μsAMD Ryzen 7 5800H
9wbinvd填满脏缓存行后全量回写1,616,480506 μsAMD Ryzen 7 5800H
7mov(MMIO)命中 PCIe 结构中高延迟 GPU 寄存器443,937,696139 msAMD Ryzen 7 5800H
4vmovdqu ymm32 字节 MMIO 读取 = 8 次 dword 事务3,549,079,2961.11 sAMD Ryzen 7 5800H
2fxrstor64(基线)从 MMIO 加载 512 字节 FPU 状态74,584,168,51223.3 sAMD Ryzen 7 5800H
1fxrstor64(冠军)基线 + 锤核饱和 PCIe 争用198,002,498,23662 sAMD Ryzen 7 5800H

图表的 Y 轴是对数刻度。线性刻度下,nop 的 1 个周期和冠军的 1980 亿个周期画在同一张图上根本看不出来。

冠军指令:fxrstor64 怎么跑出 62 秒的

fxrstor64 本身是一个普通的 x86 指令,功能是从内存恢复 512 字节的 FPU/MMX/XMM 处理器状态。在正常内存上执行,几十个周期就完事。

Domas 的做法是:把数据源换成 PCIe 总线上高延迟的 MMIO 寄存器

具体步骤:

  1. 用 mmiotic 工具扫描 PCIe 地址空间,找到响应最慢的 GPU 寄存器区域
  2. 将 fxrstor64 的目标地址指向这片 MMIO 空间
  3. CPU 必须 512 字节全部从 PCIe 总线读取,每次读取都是一次 non-posted 事务(CPU 发出请求后必须等设备应答)

冠军版本更进一步:在 fxrstor64 执行的同时,用其他核心("锤核")疯狂读取另一片高延迟 MMIO 寄存器,把 PCIe root complex 和端点设备的处理能力全部占满。结果 CPU 0 的 512 字节 fxrstor64 被堵在队列后面排队,62 秒才执行完。

这揭示了 x86 架构的一个根本特性:指令的执行时间没有上限。当一条指令的内存操作数指向 MMIO 空间时,它的延迟取决于 PCIe 总线另一端设备的响应速度。GPU 的某些寄存器响应极慢,而 x86 指令集没有机制让指令在等待 IO 时提前放弃。

从微码到 PCIe:慢指令的三种来源

排行榜上的指令按变慢的机制可以分为三类:

微码 assist 路径(排名 22-17):CPU 执行浮点指令时遇到非规格化数(denormal),硬件无法直接处理,交由微码处理。微码需要归一化操作数、执行运算、再恢复处理器状态,周期数从正常几个周期暴涨到数百。fsin(257 周期)、fadd(677 周期)、fdiv(883 周期)都属于这类。

系统总线争用(排名 16, 13, 12):split lock 通过跨缓存行边界的原子操作强制 CPU 锁住外部总线,放弃 MESI 缓存一致性快路径。rdrand 的策略更巧妙——在紧密循环中不断调用,耗尽硬件熵池后后续调用被迫等待熵源恢复。wrmsr 写 MCG_CTL 寄存器时,AMD Zen 架构需要同步所有硬件单元的错误状态,涉及跨芯片通信。

PCIe MMIO 事务(排名 7-1):这是最暴力也最有效的方法。所有排名前七的指令都通过指向 PCIe 结构中的 GPU 寄存器来获取极高延迟。指令本身(movvmovdqufxrstor64)完全合法,但操作数地址指向了响应极慢的设备寄存器,CPU 每次访问都必须等待 PCIe 总线往返。

从 8 字节的 mov rax(278 ms)到 32 字节的 vmovdqu ymm(1.1 秒),延迟随数据宽度线性增长,因为更大的读取会被拆分成更多 dword 事务,每个事务都要单独排队。

还没到极限

排行榜底部有一条标着 :finnadie: 的条目:xrstor64(AMX)。策略是利用 Sapphire Rapids 处理器的扩展 AVX 状态区域——xsave 状态区 8KB,是 fxrstor64 的 512 字节的 16 倍。按比例推算,理论周期数会达到 1 万亿级别。条目标注 Contender: TODO,还没找到愿意跑的硬件。

项目同时开放了 ARM 和 RISC-V 排行榜,目前为空。

为什么有人关心最慢的指令

Domas 此前的工作包括 sandsifter(x86 处理器模糊测试工具)和 rosenbridge(发现某些 x86 CPU 中的硬件后门核心)。他的研究始终围绕一个方向:x86 架构中那些被忽略、未文档化、或者从未被认真测试的角落。

Assembly Hall of Shame 的排名第七 vmovdqu ymm (unaligned) 被标注为 "honorable mention"——这条非对齐的 256 位向量加载指令,通过迫使 GPU 寄存器发起 non-posted dword 事务,此前已被用于攻破系统管理模式(SMM)。一条普通的数据搬运指令,因为命中了 PCIe 结构中不该命中的地方,变成了绕过 CPU 特权级的攻击路径。

这说明 CPU 指令的"延迟"不只是一个性能数字。当一条指令的执行时间取决于外部设备响应时,它同时也取决于谁控制了那个外部设备。


来源:Assembly Hall of Shame (GitHub) · 作者 Christopher Domas (@xoreaxeaxeax)