指令级并行

指令级并行(ILP)

超标量

一个周期发射多条指令(现代 CPU 4~6 发射)。

乱序执行(Out-of-Order)

指令的实际执行顺序可以不同于程序顺序,只要数据依赖允许。

1. 发射(In-order):   指令进入 reorder buffer
2. 执行(Out-of-order): 操作数就绪就执行
3. 提交(In-order):   按程序顺序写回,保证精确异常

关键:看起来是顺序执行的(对程序员可见的状态),实际是乱序的(内部优化)。

寄存器重命名

消除假依赖(WAR、WAW):

原始:  add r1, r2, r3     写 r1
       sub r1, r4, r5     又写 r1(WAW 依赖,但其实无关)

重命名: add p1, p2, p3
       sub p4, p4, p5     ← 用不同的物理寄存器,可以并行

x86 只有 16 个架构寄存器,但物理寄存器有 100+ 个。

SIMD

一条指令处理多个数据:

指令集宽度
SSE128 bit(4× float)
AVX / AVX2256 bit(8× float)
AVX-512512 bit(16× float)
NEON(ARM)128 bit
// 编译器自动向量化(需要 -O3 且循环结构简单)
for (int i = 0; i < n; i++) c[i] = a[i] + b[i];

// 手动
#include <immintrin.h>
__m256 va = _mm256_load_ps(&a[i]);
__m256 vb = _mm256_load_ps(&b[i]);
__m256 vc = _mm256_add_ps(va, vb);
_mm256_store_ps(&c[i], vc);

自动向量化的障碍:

  • 循环次数未知(可用 #pragma omp simd 或 -fno-tree-vectorize 检查)
  • 数据依赖(如累加 sum += a[i],可用 -ffast-math 允许重排)
  • 非连续访问(gather/scatter)
  • 别名问题(restrict 关键字)