汇编耻辱堂: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 movs、pause等均被取消资格。 - 时间按 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)的一项研究项目。