SIMD含义
Single Instruction Multi Data,也就是:一条指令,操作多个数据
一个实际问题
a[0] + b[0] = c[0]
a[1] + b[1] = c[1]
a[2] + b[2] = c[2]普通的标量运算,一条指令,一次操作。 如果使用SIMD:
[a0, a1, a2] + [b0, b1, b2] = [c0, c1, c2]原本的三次操作现在只要一次就能完成。
CPU支持SIMD的核心机制 — 宽寄存器
CPU 为 SIMD 专门配备了比普通寄存器宽得多的寄存器:
普通寄存器 (rax): |----64bit----|
SSE 寄存器 (xmm0): |----------128bit----------|
AVX 寄存器 (ymm0): |--------------------256bit--------------------|
AVX-512 (zmm0): |----------------------------------------512bit-|一个256bit的 AVX寄存器,可以装下4个double,8个float32,16个int
具体执行
Step1 Load
把8个float32一次性加载进 ymm0、ymm1
Step2 执行VADDPS
ymm0 + ymm1 → ymm2 (CPU中有8个ALU line同时计算)
Step3 Store 存到内存
ymm2 → 内存
C语言的例子
手写:
#include <immintrin.h> // AVX
void add_arrays(float *a, float *b, float *c, int n) {
for (int i = 0; i < n; i += 8) {
__m256 va = _mm256_load_ps(&a[i]); // 加载 8 个 float
__m256 vb = _mm256_load_ps(&b[i]);
__m256 vc = _mm256_add_ps(va, vb); // 同时加 8 个
_mm256_store_ps(&c[i], vc);
}
}通常是编译器自动优化(向量化):
// 加上 pragma 提示编译器
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2")
void add_arrays(float *a, float *b, float *c, int n) {
for (int i = 0; i < n; i++)
c[i] = a[i] + b[i]; // 编译器自动生成 SIMD 指令
}使用SIMD的前提
- 内存对齐: 跨cache line 性能劣化严重
- 数据连续: 必须是数组这种连续存储,不能是链表
- 无数据依赖: c[1] = c[0] + a[0]这种就不行