输入关键词开始搜索

SIMD 入门 — 单指令多数据

核心思想

// 标量: 一次算 1 个
for (int i = 0; i < 1024; ++i)
    c[i] = a[i] + b[i];  // 1024 次加法

// SIMD: 一次算 4 个 (SSE) 或 8 个 (AVX)
// [a0,a1,a2,a3] + [b0,b1,b2,b3] = [c0,c1,c2,c3] ← 一条指令
// → 4 倍吞吐

SSE Intrinsics

#include <xmmintrin.h>  // SSE
#include <emmintrin.h>  // SSE2

// 加载 4 个 float → 128 位寄存器
__m128 va = _mm_loadu_ps(&a[i]);
__m128 vb = _mm_loadu_ps(&b[i]);

// 并行加法
__m128 vc = _mm_add_ps(va, vb);

// 存回
_mm_storeu_ps(&c[i], vc);

AVX2 — 256 位

#include <immintrin.h>

// 一次处理 8 个 float
__m256 va = _mm256_loadu_ps(&a[i]);
__m256 vb = _mm256_loadu_ps(&b[i]);
__m256 vc = _mm256_add_ps(va, vb);
_mm256_storeu_ps(&c[i], vc);

常用 Intrinsics 速查

操作SSE (4×float)AVX (8×float)
_mm_add_ps_mm256_add_ps
_mm_sub_ps_mm256_sub_ps
_mm_mul_ps_mm256_mul_ps
_mm_div_ps_mm256_div_ps
平方根_mm_sqrt_ps_mm256_sqrt_ps
最大_mm_max_ps_mm256_max_ps
比较_mm_cmplt_ps_mm256_cmp_ps

编译器自动向量化

// 编译器在 -O2/-O3 自动尝试向量化
// 以下写法更容易被编译器向量化:

// ✅ 简单的计数循环
for (int i = 0; i < N; ++i)  // 清晰的迭代次数
    c[i] = a[i] + b[i];

// ❌ 复杂的控制流 — 阻止向量化
for (int i = 0; i < N; ++i) {
    if (a[i] > 0) c[i] = a[i] + b[i];
    else c[i] = a[i] - b[i];
}

// 查看编译器是否向量化了
// gcc -fopt-info-vec-optimized
// clang -Rpass=vectorize

何时值得手动 SIMD

自动向量化 80% 场景够用。
手动 SIMD 值得的 3 个条件:
1. 热点循环(profiling 确认)
2. 数据布局对 SIMD 友好(连续、对齐)
3. 编译器没向量化(查看机器码确认)