← 文章 / 芯片硬件
Lobsters 5小时前 · 2026-08-08 13:42:36 · 3 阅读

汇编耻辱堂:CPU性能下限竞速赛

指令延迟分析通常聚焦于性能优化——让代码跑得越快越好。而《汇编耻辱堂》反其道而行之:寻找单条指令性能的绝对下限。

🏆 当前冠军 🏆

x86: fxrstor64

策略:用 fxrstor64 从 PCIe 架构中高延迟的 MMIO 区域加载 512 字节的 FPU/MMX/XMM 状态,并在加载进行时让结构“饿死”——一群“锤核”以紧凑的 4 字节读取持续冲击另一个高延迟 MMIO 寄存器,用非posted事务淹没 PCIe 根复合体和端点,迫使 CPU 0 的 512 字节 fxrstor64 排队等待所有这些争用流量。

挑战者:AMD Ryzen 7 5800H

; CPU 0 — 计时指令
movl $0xfcc68830, %rsi
fxrstor64 %rsi

; CPU 1..N — 针对另一个高延迟位置的锤击循环
movl 0xfcc68858, %eax

🏆 得分:198,002,498,236 周期

🏆 耗时:62 秒

荣誉提名

一个违反规范的非对齐 ymm0 加载,迫使停滞的 GPU 寄存器产生非posted dword 事务,被用来打破系统管理模式的基本设计,见 smiiiiiiiiiiiiiiii

vmovdqu 0xfcc003b1, %ymm0

规则

  • 指令可以使用任何必要的设置,但只有单条指令有资格参与评分。
  • 陷入/模拟/虚拟化的指令只能计时陷入本身,不计时处理程序。
  • 指令必须不可中断。rep movspause 等均被取消资格。
  • 时间按 CPU 基础时钟频率归一化。
  • 所有平台必须保持出厂默认配置——不允许硬件改装。

x86 排行榜

27. nop

策略nop 什么都不做。它因此理所当然地垫底。

参赛者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

nop

得分:1 周期

耗时:0 纳秒

26. nop16

策略:普通的 nop 太短了,但怎么才能让“什么都不做”花更长时间?试试一个超超超长 nop

参赛者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

data16 data16 data16 data16 data16 data16 data16 nopl 0x00000000(%%eax,%%eax,1)

得分:20 周期

耗时:7 纳秒

25. rdtsc

策略:仅作为参考指令,帮我们找准定位。

参赛者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

rdtsc

得分:49 周期

耗时:18 纳秒

24. idiv

策略:使用 128 位被除数(rdx:rax=2:0)配合小除数,让商超出符号扩展的上限,从而触发除法器微码中的最长路径。

参赛者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

xorq %rax, %rax   ; rax = 0  (被除数低 64 位)
movq $2, %rdx     ; rdx = 2  (高 64 位:完整被除数 = 2^65)
movq $5, %rbx     ; 除数 → 商 = 2^65/5 ≈ 7.4×10^18
idivq %rbx

得分:77 周期

耗时:28 纳秒

23. enter

策略:使用最大嵌套深度(31),强制微码的显示遍历路径执行 30 次显示指针加载和压栈操作。

参赛者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

enter $0, $31       ; 分配 0 字节,嵌套深度 31(最大值)

得分:112 周期

耗时:41 纳秒

22. fldl

策略:尝试一个小的非规格化数,触发 FP 微码辅助。

竞争者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

    movabsq $0x0000000000000001, %rax
    movq    %rax, -8(%rsp)
    fldl    -8(%rsp)

得分:133 周期

耗时:49 纳秒

21. clflush

策略:只需确保缓存行是脏的。

竞争者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

clflush (%rax)          ; rax -> 驻留在 L3 中的脏缓存行

得分:165 周期

耗时:60 纳秒

20. fsin

策略:使用指数 0x7ff 触发微码中的“特殊值”处理;正负、NaN/无穷似乎没有区别,就用 QNaN。

竞争者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

    movabsq $0x7fffffffffffffff, %rax
    movq    %rax, -8(%rsp)
    fldl    -8(%rsp)
    fsin

得分:257 周期

耗时:94 纳秒

19. mfence

策略:用 movnti 存储到不同的缓存行,填满所有写合并行填充缓冲区,迫使 mfence 在退出前将完整的 LFB 写路径排空到 uncore。

竞争者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

movnti %r9,  0*64(%rdi)   ; ×16 个不同的缓存行——填满写合并 LFB
; …
movnti %r9, 15*64(%rdi)
mfence                     ; 退出前必须排空所有待处理的 LFB 写入

得分:326 周期

耗时:120 纳秒

18. mov cr3

策略:暂时没有特别设计,只是看看使 TLB 失效需要多长时间。

竞争者:AMD Ryzen 7 5800H with Radeon Graphics (Trigkey S5)

mov %rax, %cr3

得分:352 周期

时间:110 纳秒

17. fadd

策略:通过使用非规格化源操作数,触发 x87 FP 微码辅助路径。

竞争者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

fldl   subnorm    ; 1e-310:值 < DBL_MIN,偏置指数 = 0
faddl  subnorm    ; 源操作数为非规格化 → FP 微码辅助

得分:677 周期

时间:249 纳秒

16. split lock

策略:将带 lock 前缀的操作数对齐到跨越缓存行边界,迫使 CPU 断言外部总线锁,而非使用快速的 MESI 缓存一致性路径。

竞争者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

; split_ptr % 64 == 63 — dword 跨越字节 63(第 N 行)和 64–66(第 N+1 行)
lock xaddl %r9d, (%rdi)

得分:865 周期

时间:319 纳秒

15. fdiv -

策略:使用非规格化除数,硬件将控制权交给微码辅助,辅助程序规范化操作数,执行除法,然后恢复架构状态。

竞争者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

    movabsq $0x3ff0000000000000, %rax   ; 1.0(规格化被除数)
    movq    %rax, -8(%rsp)
    fldl    -8(%rsp)                     ; ST(0) = 1.0

    movabsq $0x0000002000000000, %rax   ; 6.79e-313(非规格化除数)
    movq    %rax, -8(%rsp)
    fdivl   -8(%rsp)                     ; ST(0) = 1.0 / 非规格化 → FP 辅助

得分:883 周期

时间:325 纳秒

14. cpuid

策略:使用 rakefield 查找延迟最高的 CPUID 叶子节点。

竞争者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

movl $6, %eax
cpuid

得分:1248 周期

时间:460 纳秒

13. rdrand

策略:在紧密循环中执行,使硬件熵池的消耗速度快于其补充速度,迫使后续调用在熵源恢复期间停滞。

参赛者:Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz

rdrand %rax

得分:5,579 个周期

耗时:2.057 微秒

12. wrmsr

策略:利用 project:nightshyft 识别高延迟 MSR。Zen 上的 MCG_CTL 看起来是个不错的选择:它可能涉及微码静默,并在各硬件单元(部分可能位于片外)的 MCA 错误库之间进行同步,需要结构级通信,而非简单的本地寄存器写入。

参赛者:AMD Ryzen 7 5800H with Radeon Graphics (Trigkey S5)

movl $0x17b, %ecx       ; MCG_CTL
wrmsr

得分:34,304 个周期

耗时:10.742 微秒

11. out

策略:瞄准跨越 NIC 设备寄存器边界的 I/O 端口,每次写入时触发设备静默其 TX DMA 引擎。

参赛者:AMD Ryzen 7 5800H with Radeon Graphics (Trigkey S5)

mov $0xf019, %dx
outl %eax, %dx

得分:49,857 个周期

耗时:15.580 微秒

10. rdmsr

策略:利用 project:nightshyft 识别高延迟的模型特定寄存器:VIA 在 0x133 处使用了一个未公开的寄存器,响应时间极高。不清楚其具体功能。

参赛者:VIA Eden Processor 800MHz

movl $0x133, %ecx ; 未公开的 MSR
rdmsr

得分:161,602 个周期

耗时:202.004 微秒

9. wbinvd

策略:用脏行占满 L1/L2/L3 缓存,强制 DRAM 回写整个层级。

参赛者:AMD Ryzen 7 5800H 搭载 Radeon 显卡(Trigkey S5)

wbinvd

得分:1,616,480 周期

耗时:506.165 微秒

8. in

策略:针对映射到 ACPI PM 块的 I/O 端口,非对齐的 4 字节读取会解码为多次非发布式加载,无论该端口通向何处。

参赛者:AMD Ryzen 7 5800H 搭载 Radeon 显卡(Trigkey S5)

mov $0x0413, %dx
inl %dx, %eax

得分:12,524,415 周期

耗时:3.921769 毫秒

7. mov

策略:利用 mmiotic 识别 PCIe 架构中的高延迟死区,命中未知 GPU 寄存器。

参赛者:AMD Ryzen 7 5800H 搭载 Radeon 显卡(Trigkey S5)

movl 0xfcc003b0, %esi

得分:443,937,696 周期

耗时:139.010268 毫秒

6. mov rax -

策略:在 MMIO 空间中搜索 PCIe 架构中最慢的寄存器,命中未知 GPU 寄存器,用 8 字节 MMIO 读取实现两次 dword 寄存器访问——虽然技术上不允许,但实际可行。

参赛者:AMD Ryzen 7 5800H 搭载 Radeon 显卡(Trigkey S5)

movq 0xfcc003b0, %rax

得分:887,716,864 周期

耗时:277.971228 毫秒

5. vmovdqu xmm -

策略:在 MMIO 空间中搜索 PCIe 架构中最慢的寄存器,命中未知 GPU 寄存器,用 16 字节 MMIO 读取实现四次 dword 寄存器访问——虽然技术上不允许,但实际可行。

参赛者:AMD Ryzen 7 5800H 搭载 Radeon 显卡(Trigkey S5)

vmovdqu 0xfcc003b0, %xmm0

得分:1,774,555,776 个周期

耗时:555.664133 毫秒

4. vmovdqu ymm -

策略:在 PCIe 结构中搜索 MMIO 空间里最慢的寄存器,命中未知 GPU 寄存器,用 32 字节 MMIO 读取实现八次 dword 寄存器访问——这在技术上依然不被允许,但照样能跑。

参赛者:AMD Ryzen 7 5800H 配 Radeon 显卡(Trigkey S5)

vmovdqu 0xfcc003b0, %ymm0

得分:3,549,079,296 个周期

耗时:1.111345034 秒

3. vmovdqu ymm(非对齐) -

策略:在 PCIe 结构中搜索 MMIO 空间里最慢的寄存器,命中未知 GPU 寄存器,用 32 字节非对齐 MMIO 读取实现九次 dword 寄存器访问——这比对齐版本更不被允许,但照样能跑。

参赛者:AMD Ryzen 7 5800H 配 Radeon 显卡(Trigkey S5)

vmovdqu 0xfcc003b1, %ymm0

得分:4,453,212,256 个周期

耗时:1.394428818 秒

2. fxrstor64(基线) -

策略:用 mmiotic 找出 PCIe 结构中高延迟的死区,隔离靠近 0 的区域并偏移状态以避免 MXCSR 损坏(可能是 VGA 缓冲区?),再用 fxrstor64 从 MMIO 加载 512 字节的 FPU/MMX/XMM 状态,迫使 CPU 通过最慢的可用内存通道处理 512 字节的 I/O 事务。

参赛者:AMD Ryzen 7 5800H

movl $0xfcc68830, %rsi
fxrstor64 %rsi

得分:74,584,168,512 个周期

耗时:23.354502677 秒

1. 🏆 fxrstor64 🏆

策略:在负载进行期间,通过让一组“锤击”核心以紧密的4字节读取方式反复访问另一个高延迟MMIO寄存器,使互连结构(fabric)陷入饥饿状态,从而饱和PCIe根复合体及端点设备的非发布事务队列,迫使CPU 0的512字节fxrstor64操作排队等待,被这些竞争流量阻塞。

参赛者:AMD Ryzen 7 5800H(集成Radeon显卡,Trigkey S5)

; CPU 0 — 计时指令
movl $0xfcc68830, %rsi
fxrstor64 %rsi

; CPU 1..N — 针对另一高延迟地址的锤击循环
movl 0xfcc68858, %eax

🏆 得分:198,002,498,236 周期

🏆 耗时:62 秒

??. xrstor64(AMX,MMIO)

策略:在Sapphire Rapids上利用扩展AVX状态,结合fxrstor64中的MMIO手法:xsave状态区域为8KB,相比512字节扩大了16倍——预计可达1,000,000,000,000周期

参赛者:待定

; XCR0必须启用AMX组件(位17-18);状态区域约8KB
xrstor64 (%rsi)         ; rsi指向MMIO区域,与fxrstor64采用相同技术

ARM排行榜

  • 待定

RISC-V排行榜

  • 待定

作者

汇编性能耻辱堂是Christopher Domas(@xoreaxeaxeax)的一项研究项目。

原始来源: Lobsters

评论 (0)