ARM的NEON是类似于X86的SSE2的一种优化的指令集,主要就是为了实现SIMD全称Single Instruction Multiple Data,单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集。简单来说就是处理一些算法的时候,可以并行处理,大大提高了效率。
在Android手机上大部分都是ARM架构的,我们开启NEON后就可以使用这些指令集了,当然可以使用汇编,但是也为我们提供了封装好的方法,总结一下数据以及函数的解析规律。
NEON 的矢量数据类型
数据类型有下列的模式,首先是不是数组的
<type><size>x<number_of_lanes>_t
例如
int16x4_t 代表4个16bit的数据,也就是4个short
float32x4_t 代表4个32bit的float数据
然后是数组类型
<type><size>x<number_of_lanes>x<length_of_array>_t
类似于这样
struct int16x4x2_t{
int16x4_t val[2];
} <var_name>;
然后我发现还有一个poly的类型,好像多用在有限域的中,密码学等方面,这个暂时不管吧,基本算法也用不到。
函数指令解释
关于函数的指令解释,参考这篇文章:https://blog.youkuaiyun.com/xiongtiancheng/article/details/77103810
再参考这篇文字速查:https://blog.youkuaiyun.com/billbliss/article/details/78924636
方便观看我就直接转载过来了。
arm neon 指令分类:
正常指令(q)
正常指令可对上述任意向量类型执行运算,并生成大小相同且类型通常与操作数向量相同的结果向量。
长指令(l)
长指令对双字向量操作数执行运算,并生成四字向量结果。 所生成的元素通常是操作数元素宽度的两倍,并属于同一类型。
宽指令(w)
宽指令对一个双字向量操作数和一个四字向量操作数执行运算。 此类指令生成四字向量结果。 所生成的元素和第一个操作数的元素是第二个操作数元素宽度的两倍。
窄指令(n)
窄指令对四字向量操作数执行运算,并生成双字向量结果。 所生成的元素通常是操作数元素宽度的一半。
饱和指令(q)
通过在 V 和指令助记符之间使用 Q 前缀可以指定饱和指令。
上述的指令分类区别我感觉就是操作对象与返回对象的区别了。
数据类型 x 的饱和范围 (s 就是signed,有符号的意思,u就是unsigned,无符号的意思)
s8 –2^7 <= x < 2^7
s16 –2^15 <= x < 2^15
s32 –2^31 <= x < 2^31
s64 –2^63 <= x < 2^63
u8 0 <= x < 2^8
u16 0 <= x < 2^16
u32 0 <= x < 2^32
u64 0 <= x < 2^64
neon的寄存器:
有16个128位四字到寄存器Q0-Q15,32个64位双子寄存器D0-D31,两个寄存器是重叠的。
arm_neon.h 中的函数介绍
例如
int8x8_t vadd_s8 (int8x8_t __a, int8x8_t __b);
v是向量操作,可以认为就是neon函数,add 是相加,s8表示结果是s8类型(向量)
int8x16_t vaddq_s8 (int8x16_t __a, int8x16_t __b);
v是向量操作,可以认为就是neon函数,add 是相加,后面的q代表正常指令,s8表示结果是s8类型(向量)
int8x8_t vqadd_s8 (int8x8_t __a, int8x8_t __b);
v是向量操作,可以认为就是neon函数,后面的q代表饱和指令,add 是相加,s8表示结果是s8类型(向量)
int8x8_t vshr_n_s8 (int8x8_t __a, const int __b);
v是向量操作,可以认为就是neon函数,shr是右移位,n表示参数中有个基本数据类型,也就是不是向量或指针,s8表示结果是s8类型(向量)
int8_t vget_lane_s8 (int8x8_t _