向量intrinsic编程
本文最后更新于:1 年前
SIMD指令集的发展
什么是SIMD
我们接触的传统的程序,一般是SISD,即Single Instruction Single Data,单指令单数据流,它是传统的串行计算机架构,即相当于在该计算机上,任何时刻都是从指令流里取一条指令来处理数据流中的一个数据,交由一个核心来处理。
SIMD则是利用到了计算机的多个核心的较新的并行架构,采用控制器来控制多个处理器,对多个核心的不同数据执行同一条指令,从而实现并行。
SIMD有对应SIMD指令集,而支持这些SIMD指令集的CPU在设计的时候增加了专用的向量寄存器,
就好像之前一条指令只能处理一个单精度浮点数,而现在采用一条SIMD指令能处理如4个,8个甚至更多个的单精度浮点数,这些数据就存储于向量寄存器中,显然,向量寄存器较通用寄存器其能容纳更多的位数。当然,放入向量寄存器的多个数据的数据类型是一致的。
SIMD指令集的历史进程
下图是SIMD指令集随CPU的发展:
- MMX(Multi Media eXtension),伴生64位的MM寄存器
- SSE(Streaming Simd Extension),伴生128位的XMM寄存器,XMM0-XMM15,一共十六个寄存器
- AVX/AVX2(Advanced Vector eXtension),伴生256位的YMM寄存器,YMM0-YMM15,一共十六个寄存器。AVX2增加了乘加融合指令(FMA,这同时也是一个硬件)。低128位对应XMM
- AVX512,伴生512位的ZMM寄存器,ZMM0-ZMM31,一共32个寄存器,还有8个操作掩码寄存器,K0-K7,低128位和低256位对应XMM和YMM
SIMD指令集的数据类型
虽说有好像很多种指令集,但毕竟都是一家公司发行的,有一定规律可遵循:
__m+位宽+数据类型
其中位宽有128位(SSE),256位(AVX/AVX2),512位(AVX512)
数据类型有三种:
- 什么都不写,就代表单精度浮点数
- d,代表double,双精度浮点数
- i,整型,对应char,short,int,long这一堆定点数
因此数据类型可以如下表示:
__m256
: 向量长度为256位的单精度浮点数,有8个32位的float
__m128i
: 向量长度为128位的整型,有多个整型数值
__m5121d
: 向量长度为512位的双精度浮点数,有8个64位的double
SIMD指令集的方法
在说到SIMD指令集,如AVX指令集时,我们知道,可以采用指令集中的汇编指令去操纵YMM寄存器,但是,汇编难写难读难管理,因此有了Intrinsic Function。
Intrinsic Function就跟C一样,是较于汇编的一种高级语言(但没到C那种高级程度,只是用类C的方式编写汇编),它又与C不同(毕竟面对的寄存器不是通用寄存器了,而是向量寄存器),但可以和C/C++无缝融合
需要注意:Intrinsic Function并非完全与指令集一一对应
SSE/AVX的intrinsic function命名习惯如下:
__<return_type> _<vector_size>_<intrin_op>_<suffix>
- 返回类型return_type就是上面对应的SIMD数据类型,如
__m256d
这些 - 向量长度vector_size就是代表函数操作的数据向量的位长度,如
mm
表示128位的数据向量(SSE),mm256
表示256位的数据向量(AVX/AVX2),mm512
表示512位的数据向量(AVX512) - 代表函数具体功能的
intrin_op
- suffix是后缀,表示函数参数的数据类型,p=packed,s=single(fp32),d=double(fp64),ep=整型(具体英文指代词没找到)
- ps
- pd
- epi8/epi16/epi32/epi64
- epu8/epu16/epu32/epu64
- si128/si256: 未指定的128位/256位向量,si=unSpecIfied
常规方法类型
SET系列,LOAD系列,STORE系列,MATH系列,COMPARE系列,CONVERT系列,SHUFFLES系列,详情参见[1]
注:load是往向量寄存器YMM里灌数据,而store给从向量寄存器写到内存去
其中shuffles中有个_mm256_blendv_pd(__m256d a,__m256d b,__m256d mask)
中的v表示的是variable,否则是constant,且它可以这么理解:mask中的false由a填充,true的部分则由b填充
第一个intrinsic的demo
1 |
|
immintrin.h
是AVX的头文件,然后在编译的时候要加上-mavx
以启用AVX指令集
AVX2算子编写
这部分的内容大量的参考了学习资料[1],其中内容编写部分是我看了之后自己重新编写的
GELU
参照学习资料[1]自行观看,过于直白,其中tanh是一种近似实现,当然也可以用别的轻量数学库来替换,好像是Intel C++ Compiler没有这个的实现(但是好像MKL是有的),用别的编译器可能存在不支持AVX2指令的情况.查了下百度,原因如下(要做优化,就别用cmath这些,当然icc针对这些数学公式也有优化的库:mkl):
softmax
矩阵加
矩阵转置
参考文章
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!