lab2.5 手写 SIMD 向量化¶
思路¶
首先分析需要向量化的部分:
1 2 3 4 | |
可以分为三个部分:
- 需要计算的数据:
a[i], b[i], c[i]。 - 计算操作:
tmp = a[i] * b[i]和c[i] = c[i] + tmp。
于是根据向量化的基本流程,大致需要几个操作:
- Load
a, b, c到__m256类型变量A, B, C。 - 令
C = C + A * B。(此处运算符只代表操作的含义) - 将
C的数据存回float数组c中 。
如何实现?
Load 操作:
__m256 _mm256_loadu_ps (float const * mem_addr)-
从
mem_addr指向的内存地址中 load 256 位(8 个float元素)的数据到向量寄存器。
计算操作:
__m256 _mm256_mul_ps (__m256 a, __m256 b)-
计算
a和b中float元素逐位相乘的结果。 __m256 _mm256_add_ps (__m256 a, __m256 b)-
计算
a和b中float元素逐位相加的结果。
Store 操作:
void _mm256_storeu_ps (float * mem_addr, __m256 a)-
将
a中的 256 位(8 个float元素)的数据 store 到mem_addr指向的内存地址中。
于是就可以写出向量化的代码了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Note
由于 __m256 类型变量只能同时操作 256 位数据,即 8 个 float ,向量化时需对每 8 个数据进行一次向量化
正确性和加速比¶
编译运行 add.cpp ,程序输出:
time=1.916000
Check Passed
向量化后计算结果正确,加速比为 1.916 。
汇编代码分析¶
利用 godbolt 进行汇编代码分析。
向量化前,需要向量化的代码部分的汇编代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
可知,for 循环内部的代码被顺次执行了 MAXN 次,每次只对 1 组数据( a[i], b[i], c[i] ) 进行操作。
向量化后,被向量化的代码部分的汇编代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
可知程序使用了 vmoups 等汇编指令和 256 位寄存器 ymm0, ymm1 ,同时对 8 组 float 类型数据进行操作,循环次数减少到 MAXN / 8 次。
Reference¶
- Intel® Intrinsics Guide:https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html
- n方过百万 暴力碾标算——指令集优化的基础使用:https://www.luogu.com.cn/blog/ouuan/avx-optimize
- 汇编语言笔记(六)——SIMD指令:https://zhuanlan.zhihu.com/p/424475308