CPU 特性探测与优化(CPUID & Optimization)

CPUID 指令

cpuid 根据 %eax(有时 %ecx)的功能号返回处理器信息。结果在 %eax %ebx %ecx %edx。常用功能号:

输入 EAX 返回内容
0 厂商 ID(ebx:edx:ecx = "GenuineIntel" 等)及最大功能号
1 特性位(EDX/ECX 中的 SSE、AVX、FPU 等标志)
7 扩展特性(EBX 中的 AVX2、BMI、AVX-512 等,需 ECX=0)
0x80000001 扩展特性(64 位、ABM 等)

检测 SSE/AVX 是否可用

# 检测 AVX(CPUID leaf 1,ECX bit 28)
movl $1, %eax
cpuid
btl  $28, %ecx          # AVX 支持?
jc   has_avx

# 检测 AVX2(leaf 7, ebx bit 5)
movl $7, %eax
xorl %ecx, %ecx
cpuid
btl  $5, %ebx
jc   has_avx2

注意:使用 AVX 前还需检查 OS 是否启用 YMM 状态保存(XCR0 中 SSE/AVX 位),否则执行 AVX 指令会 #UD

# 检查 XCR0(用 xgetbv 读扩展控制寄存器)
xorl %ecx, %ecx
xgetbv                  # edx:eax = XCR0
andl $6, %eax           # bit1 = SSE 状态, bit2 = AVX 状态
cmpl $6, %eax
jne  no_avx             # OS 未启用

实际库函数(如 glibc 的 CPU 特性检测)会完整检测,一般不需要手写。

关键特性位(leaf 1)

标志 含义
EDX 23 MMX MMX 指令集
EDX 25 SSE SSE
EDX 26 SSE2 SSE2
ECX 0 SSE3 SSE3
ECX 9 SSSE3 补充 SSE3
ECX 19 SSE4.1 SSE4.1
ECX 20 SSE4.2 SSE4.2
ECX 28 AVX AVX
ECX 12 FMA fused multiply-add

性能优化要点

  1. 减少内存访问:把热数据放寄存器;利用 %r8%r15 等额外寄存器避免频繁 load/store。
  2. 指令级并行:现代 CPU 乱序执行,独立指令链越多吞吐越高;避免长依赖链(如循环累加可展开成 4 路)。
  3. 避免流水线停顿
    • div/idiv 延迟高(~20-100 周期),用乘法+shift 替代或预计算倒数。
    • 分支预测失败代价高,用条件传送 cmov 或无分支技巧。
  4. 对齐热循环与数据:代码 16/32 字节对齐(-falign-loops),SIMD 数据 16/32 字节对齐。
  5. SIMD 化:能用 addps/vaddps 一次处理 4/8 个元素就别用标量循环。
  6. 栈对齐:保持 16 字节对齐,避免 movaps 在栈上触发异常。
  7. rep movs/rep stos:大块拷贝/清零交给 CPU 微码最优化。
  8. 宏融合(macro-fusion):某些 cmp/test + 相邻条件跳转可被融为单微操作,注意把比较紧挨跳转。
  9. 避免部分寄存器停顿:写 8/16 位子寄存器后立即读整个 64 位寄存器会产生停顿,用 movzbl/movzwl 零扩展替代直接写 %al/%ax
  10. 零扩展优先于 32 位拷贝:需要清零整个寄存器时,xorl %eax, %eaxmovl $0, %eax 短,两者都比 movq $0, %rax 好。

循环展开示例

# 普通累加 4 个元素(未展开)
    xorq %rax, %rax
    movq $0, %rcx
1:  addq (%rdi,%rcx,8), %rax
    incq %rcx
    cmpq %rdx, %rcx
    jl   1b

# 4 路展开(减少分支与循环开销;假定 n 是 4 的倍数,余数需单独循环处理)
    xorq %rax, %rax
    xorq %rcx, %rcx
    shrq $2, %rdx              # 次数/4
1:  addq (%rdi,%rcx,8),   %rax
    addq 8(%rdi,%rcx,8),  %rax
    addq 16(%rdi,%rcx,8), %rax
    addq 24(%rdi,%rcx,8), %rax
    addq $4, %rcx
    cmpq %rdx, %rcx
    jl   1b

无分支绝对值(avoid branch)

# rax = |rdi|
    movq  %rdi, %rax
    movq  %rdi, %rcx
    sarq  $63, %rcx       # rcx = 符号位掩码(全0或全1)
    xorq  %rcx, %rax
    subq  %rcx, %rax      # rax = (rdi ^ mask) - mask

读取时间戳(性能计时)

rdtsc                    # EDX:EAX = 64 位 TSC 计数(需序列化避免乱序)
lfence                  # 常配合 rdtsc/rdtscp 隔离
rdtscp                  # 含处理器 ID,且序列化