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