SIMD

SIMD(Single Instruction Multiple Data)指令集,指单指令多数据流技术,可用一组指令对多组数据通进行并行操作。SIMD指令可以在一个控制器上控制同时多个平行的处理微元,一次指令运算执行多个数据流,这样在很多时候可以提高程序的运算速度。先行大部分的密码算法库都使用了SIMD加速。

AVX

入门

AVX为在Inter CPU处理器上实现SIMD操作的指令集。主要衍化流程为:

  1. AVX,全称为:Advanced Vector Extensions(又名,Sandy Bridge New Extensions),是Intel和AMD微服务器x86指令集的extension扩展。

  2. AVX2扩充到了支持256bit的整数运算指令,引入了Fused-Multiply-Add(FMA)运算。所谓FMA,即可通过单一指令实现A = A ∗ B + C A=A*B+CA=A∗B+C计算。

  3. AVX-512通过使用新的EVEX prefix编码方式,将AVX扩充到了支持512-bit运算。

在C++中使用AVX(Advanced Vector Extensions)指令集,需要包含相应的头文件以使用AVX指令:

#include <immintrin.h>

immintrin.h 头文件包含了许多 AVX、AVX2 和 AVX-512 指令的声明,以及相应的数据类型和函数。这个头文件提供了对 AVX 指令的访问和使用所需的所有定义和声明。

在包含了 immintrin.h 后,可以在代码中使用 AVX 指令了。请注意,使用 AVX 指令需要确保编译器支持 AVX 指令集,并且需要在编译选项中启用相应的优化标志,如 -mavx

数据类型

数据类型描述元素个数元素大小总大小
__m128包含 4 个单精度浮点数的向量432 位128 位
__m128d包含 2 个双精度浮点数的向量264 位128 位
__m128i包含若干个整型数字的向量-128 位128 位
__m256包含 8 个单精度浮点数的向量832 位256 位
__m256d包含 4 个双精度浮点数的向量464 位256 位
__m256i包含若干个整型数字的向量-256 位256 位

前面带**两个**下划线

名字里带128的 = 一个向量包含128个bit = 32 byte

名字里带256的 = 一个向量包含256个bit = 64 byte

尾巴啥也不带的 = 里面都是float,一个float=32bit

尾巴带d的 = 里面都是double,一个double=64bit

尾巴带i的 = 里面都是整型,根据整型的类型不同,个数也不同

函数名称

_mm<bit_width>_<name>_<data_type>
  • <bit_width> 返回的向量的类型,返回的是256bit大小的就是256,返回128大小的,这里就是空的。还有一些特殊的:store没有返回(void);test系列比较两个输入是否相同,返回0或1。
  • <name> 函数的名字,基本通过名字就可以看出功能啦~
  • <data_type> 表示这个函数在处理数据时,会把输入的数据当作什么类型去处理

关于**<data_type>**,因为m256i / m128i中的整型多种多样,需要告诉AVX里面的整型大小(size)是多少。

  1. ps:里面都是float,把32bits当成一个数看
  2. pd:里面都是double,把64bits当成一个数看
  3. epi8/epi16/epi32/epi64:向量里每个数都是整型,一个整型8bit/16bit/32bit/64bit
  4. epu8/epu16/epu32/epu64:向量里每个数都是无符号整型(unsigned),一个整型8bit/16bit/32bit/64bit
  5. m128/m128i/m128d/m256/m256i/m256d:输入值与返回类型不同时会出现 ,例如__m256i_mm256_setr_m128i(__m128ilo,__m128ihi),输入两个__m128i向量 ,把他们拼在一起,变成一个__m256i返回 。另外这种结尾只见于load
  6. si128/si256:不care向量里到底都是些啥类型,反正128bit/256bit,例如:
__m256i _mm_broadcastsi128_si256 (__m128i a)

此函数接收一个128-bit的__m128i,然后将这__m128i的128位,按位放入返回的__m256i 类型的前128位(127-0)中,再按位放入(复制到)后128位(255-128)中。因为不涉及单个数操作,将128位看成整体操作,所以不用在意其中每个数的数据类型。

si128/si256基本只见于

  • broadcast:复制2遍__m128i,放入 __m256i
  • cast
    • __m128i转型__m256i(upper 128 bit undefined) ,
    • __m256i转型 __m128i

总而言之5 & 6两种并不太常见。

标量值初始化

数据类型描述
_mm256_setzero_ps/pd返回一个全0的 float 类型的向量
_mm256_setzero_si256返回一个全0的整型向量
_mm256_set1_ps/pd用一个 float 类型的数填充向量
_mm256_set1_epi8/epi16/epi32/epi64x用整型数填充向量
_mm256_set_ps/pd用8个 float 或者4个 double 类型数字初始化向量
_mm256_set_epi8/epi16/epi32/epi64x用一个整型数初始化向量
_mm256_set_m128/m128d/m128i用2个128位的向量初始化一个256位向量
_mm256_setr_ps/pd用8个 float 或者4个 double 的转置顺序初始化向量
_mm256_setr_epi8/epi16/epi32/epi64x用若干个整型数的转置顺序初始化向量

从内存中加载数据

数据类型描述
_mm256_load_ps/pd从对齐的内存地址加载浮点向量
_mm256_load_si256从对齐的内存地址加载整型向量
_mm256_loadu_ps/pd从未对齐的内存地址加载浮点向量
_mm256_loadu_si256从未对齐的内存地址加载整型向量
_mm_maskload_ps/pd根据掩码加载128位浮点向量的部分
_mm256_maskload_ps/pd根据掩码加载256位浮点向量的部分
(2)_mm_maskload_epi32/64根据掩码加载128位整型向量的部分
(2)_mm256_maskload_epi32/64根据掩码加载256位整型向量的部分

最后2个函数前面有一个(2),代表这两个函数只在AVX2中支持,下面(2)同理

乘除法

数据类型描述
_mm256_mul_ps/pd对两个 float 类型的向量进行相乘
(2)_mm256_mul_epi32将包含 32 位整数的向量的最低四个元素相乘
(2)_mm256_mul_epu32将无符号 32 位整数的向量的最低四个元素相乘
(2)_mm256_mullo_epi16/32将整数相乘并存储低半部分
(2)_mm256_mulhi_epi16将整数相乘并存储高半部分
(2)_mm256_mulhi_epu16将无符号整数相乘并存储高半部分
(2)_mm256_mulhrs_epi16将16位元素相乘以形成32位元素
_mm256_div_ps/pd对两个 float 类型的向量进行除法

加减法

数据类型描述
_mm256_add_ps/pd对两个浮点向量做加法
_mm256_sub_ps/pd对两个浮点向量做减法
(2)_mm256_add_epi8/16/32/64对两个整型向量做加法
(2)_mm256_sub_epi8/16/32/64对两个整型向量做减法
(2)_mm256_adds_epi8/16两个整数向量相加且考虑内存饱和问题
(2)_mm256_adds_epu8/16两个无符号整数向量相加且考虑内存饱和问题
(2)_mm256_subs_epi8/16两个整数向量相减且考虑内存饱和问题
(2)_mm256_subs_epu8/16两个无符号整数向量相减且考虑内存饱和问题
_mm256_hadd_ps/pd水平方向上对两个浮点向量做加法
_mm256_hsub_ps/pd垂直方向上对两个浮点向量做减法
(2)_mm256_hadd_epi16/32水平方向上对两个整型向量做加法
(2)_mm256_hsub_epi16/32水平方向上对两个整型向量做减法
(2)_mm256_hadds_epi16对两个包含 short 类型的向量做加法且考虑内存饱和问题
(2)_mm256_hsubs_epi16对两个包含 short 类型的向量做减法且考虑内存饱和问题
_mm256_addsub_ps/pd加上和减去两个浮点类型的向量

融合乘法和加法

指令描述
_mm_fmadd_ps/pd将两个向量相乘,再将第三个向量的对应元素加到乘积中。
_mm256_fmadd_ps/pd将两个向量相乘,再将第三个向量的对应元素加到乘积中。
_mm_fmsub_ps/pd将两个向量相乘,然后将第三个向量的对应元素从乘积中减去。
_mm256_fmsub_ps/pd将两个向量相乘,然后将第三个向量的对应元素从乘积中减去。
_mm_fmadd_ss/sd将两个向量的最低元素相乘并相加。
_mm_fmsub_ss/sd将两个向量的最低元素相乘并相减。
_mm_fnmadd_ps/pd将两个向量相乘,并将负乘积加到第三个向量中。
_mm256_fnmadd_ps/pd将两个向量相乘,并将负乘积加到第三个向量中。
_mm_fnmsub_ps/pd将两个向量相乘,并将负乘积减去第三个向量。
_mm256_fnmsub_ps/pd将两个向量相乘,并将负乘积减去第三个向量。
_mm_fmaddsub_ps/pd将两个向量相乘,然后交替地将第三个向量的对应元素加到或减去乘积中。
_mm256_fmaddsub_ps/pd将两个向量相乘,然后交替地将第三个向量的对应元素加到或减去乘积中。
_mm_fmsubadd_ps/pd将两个向量相乘,然后交替地将第三个向量的对应元素减去或加到乘积中,取决于元素的位置(奇数次方或偶数次方)。
_mmf256_fmsubadd_ps/pd将两个向量相乘,然后交替地将第三个向量的对应元素减去或加到乘积中,取决于元素的位置(奇数次方或偶数次方)。

排列

指令描述
_mm_permute_ps/pd根据8位控制值从输入向量中选择元素。
_mm256_permute_ps/pd根据8位控制值从输入向量中选择元素。
_mm256_permute4x64_pd根据8位控制值从输入向量中选择64位元素。
_mm256_permute4x64_epi64根据8位控制值从输入向量中选择64位元素。
_mm256_permute2f128_ps/pd基于8位控制值从两个输入向量中选择128位块。
_mm256_permute2f128_si256基于8位控制值从两个输入向量中选择128位块。
_mm_permutevar_ps/pd根据整数向量中的位从输入向量中选择元素。
_mm256_permutevar_ps/pd根据整数向量中的位从输入向量中选择元素。
_mm256_permutevar8x32_ps使用整数向量中的索引选择32位元素(浮点)。
_mm256_permutevar8x32_epi32使用整数向量中的索引选择32位元素(整数)。

SSE

想要使用SSE指令,则需要包含对应的头文件:

#include <mmintrin.h>   //mmx
#include <xmmintrin.h>  //sse
#include <emmintrin.h>  //sse2
#include <pmmintrin.h>  //sse3