目标读者:想在 x86-64 平台(Intel/AMD)手写或微调汇编,以获得极致性能的开发者。
核心收获:掌握一套“写-测-调”闭环流程 + 10 条现代 CPU 优化心法 + 可直接复用的代码片段。
1 准备用具(Win & Linux 通用)
任务 | Windows | Linux |
汇编器 | MASM64 或 NASM | NASM / GAS |
链接器 | link.exe / lld | ld / lld |
调试 | WinDbg、VS 调试器 | gdb / lldb |
性能计数 | Intel VTune、Windows Profiler | perf, pmu-tools |
反汇编 | IDA Free / Ghidra / objdump | objdump / Ghidra |
安装 NASM(示例)
# Debian / Ubuntu
sudo apt update && sudo apt install nasm lld -y
winget install -e --id NASM.NASM
2 写代码:10 条实用心法
# | 关键点 | 说明 / 示例 |
1 | 优先用寄存器 | RAM≈100ns,L1≈1ns。同一函数尽量把热数据留在寄存器/栈。 |
2 | 配合 SIMD | AVX2 已是标配,AVX-512 在新 CPU 激活即可用。vaddps ymm0, ymm1, ymm2 |
3 | 指令对齐 | 热循环首地址对齐 16 B,可减少取指分支。align 16 |
4 | 避免分支错预测 | 用 cmov / 位运算替换低概率分支。 |
5 | 软展开循环 | 使用 unroll 4 可与流水线深度匹配(过度会导致 I-cache miss)。 |
6 | 预取读取 | prefetcht0 [rsi + rdx];对顺序遍历收益明显。 |
7 | 调用约定一致 | Win64:rcx/rdx/r8/r9;SysV:rdi/rsi/rdx/rcx/r8/r9。跨平台接口用宏封装。 |
8 | 减少 store-load 依赖 | 写后尽量避免立即读同地址;可插入独立指令消除气泡。 |
9 | 批量处理分支 | 对随机布尔数组,用 bt + setc + SIMD mask 批量统计。 |
10 | 用汇编内联 | 在 C/C++/Rust 内联小段关键路径,保持可维护性。 |
3 示范:双精度向量相乘并累加
目标:for(i) sum += a[i]*b[i];
工具链:NASM + GCC 链接(Linux)
; file: dot_sse.asm
global dot_sse
section .text
; rdi=a*, rsi=b*, rdx=len
dot_sse:
xor rax, rax ; i = 0
vxorpd ymm0, ymm0, ymm0 ; sum = 0
.loop:
cmp rax, rdx
jge .done
vmovupd ymm1, [rdi+rax*8]
vmovupd ymm2, [rsi+rax*8]
vfmadd231pd ymm0, ymm1, ymm2 ; sum += a*b
add rax, 4
jmp .loop
.done:
vhaddpd ymm0, ymm0, ymm0
vextractf128 xmm1, ymm0, 1
addsd xmm0, xmm1
ret
编译 & 测试:
nasm -f elf64 dot_sse.asm
gcc -c main.c
gcc dot_sse.o main.o -o dot -mavx2
perf stat ./dot
4 测性能:三步闭环
- 硬件计数
perf stat -e cycles,instructions,branch-misses ./dot
- 火焰图找热点
perf record -g ./dot && perf script > out.perf
flamegraph.pl out.perf > flame.svg
- 反汇编核对
objdump -d -Mintel dot | less
5 常见坑
症状 | 排查技巧 |
频繁 #UD | 检查 CPU 是否支持对应指令集(`lscpu |
性能下降 | 可能是 AVX-512=>频率降低,关闭 AVX512 或用混编策略 |
结果错误 | 确认调用约定,浮点保存规则(SSE vs x87) |
结语
手写汇编的黄金法则:写少量、测大量、改关键。
掌握这套「写-测-调」方法,你就能在现代 CPU 上挖掘出极致性能,而不必陷入不可维护的汇编泥潭。