SIMD-高性能计算入门
SIMD
SIMD(Single Instruction Multiple Data)即单指令流多数据流,是一种采用一个控制器来控制多个处理器,同时对一组数据(又称“数据向量”)中的每一个分别执行相同的操作从而实现空间上的并行性的技术。常见的已经实现的指令集如下:
| 指令集 | 寄存器位宽 | 寄存器名 | 一次能处理的 float (32-bit) | 一次能处理的 double (64-bit) |
|---|---|---|---|---|
| SSE | 128 bit | xmm0-15 | 4 × float | 2 × double |
| AVX | 256 bit | ymm0-15 | 8 × float | 4 × double |
| AVX2 | 256 bit | ymm0-15 | 8 × float | 4 × double |
| AVX-512 | 512 bit | zmm0-31 | 16 × float | 8 × double |
Intrinsics 101:头文件 & 命名规则
| ISA | 头文件 | 前缀 | 例 |
|---|---|---|---|
| SSE | <xmmintrin.h> |
_mm_* |
_mm_add_ps |
| AVX | <immintrin.h> |
_mm256_* |
_mm256_add_ps |
| AVX-512 | <immintrin.h> |
_mm512_* |
_mm512_add_ps |
只需
#include <immintrin.h>即可覆盖 SSE → AVX-512。
最常用的 Intrinsics 指令
| 类别 | Scalar | AVX2 (256-bit) | 语义 |
|---|---|---|---|
| 置零 | float z = 0.0f |
_mm256_setzero_ps() |
8×0 |
| 加载 | a[i] |
_mm256_load_ps(ptr) |
从内存 → ymm |
| 存储 | a[i] = v |
_mm256_store_ps(ptr, v) |
ymm → 内存 |
| 加法 | a + b |
_mm256_add_ps(va, vb) |
8 并行加 |
| 乘法 | a * b |
_mm256_mul_ps(va, vb) |
8 并行乘 |
| 水平求和 | 手写 for | _mm256_hadd_ps → _mm_add_ps → _mm_store_ss |
把 8 个数累加成一个 |
实践
这里使用 Intel Intrinsics,可以直接使用 C/C++ 代码调用 SIMD 指令集。下面使用一个向量 L2 距离计算的例子来展示 SIMD 的使用方式、可能遇到的浮点误差以及性能提升。
首先需要引入头文件
1 | |
定义计时函数并且定义生成随机向量函数以便测试:
1 | |
下面分别使用 float 和 double 来进行数值计算对比:
1 | |
1 | |
测试 main 函数
1 | |
编译/运行
1 | |
结果分析
1 | |
可以看到 float 版本的 SIMD 结果误差较大,达到了 0.05% 量级,而 double 版本的误差非常小,为 1e-10 量级。原因在于 float 的有效位数只有 23 位,而在累加过程中会丢失精度。同时可以看出对于标量运行的方式,使用 double 进行计算相比 float 会慢大约 2.4 倍,而使用 SIMD 指令集后,double 版本的性能提升更为显著,达到了 8.8 倍(与 AVX2 float 相近)。
SIMD-高性能计算入门
https://github.com/Cookiecoolkid/Cookiecoolkid.github.io/2025/09/02/高性能计算入门/