NEON Intrinsics 是一种更简单的编写 NEON 代码的方法,NEON Intrinsics 类似于 C 函数调用,在编译时由编译器替换为相应的汇编指令,使用时需要包含头文件arm_neon.h
。
1、类型格式
(1)非数组向量格式
<基本类型>x<lane个数>_t 如int8x8_t
基本类型int8,int16,int32,int64,uint8,uint16,uint32,uint64,float16,float32
lane个数表示并行处理的基本类型数据的个数(通道数)。
(2)数组向量格式
<基本类型>x<lane个数>x<向量个数>_t 如 uint8x8x3_t
对于多个向量的类型实际上是结构体
typedef struct {
uint8x8_t val[3];
} uint8x8x3_t;
2、函数格式
v<模型><指令名><sharp>[后缀]_<数据基本类型简写>
(1)<模型>
q:表示饱和计算,例如
// a加b的结果做饱和计算
int8x8_t vqadd_s8(int8x8_t a, int8x8_t b);
h:表示折半计算,例如
// a减b的结果右移一位
int8x8_t vhsub_s8(int8x8_t a, int8x8_t b);
d:表示加倍计算,例如
// a乘b的结果扩大一倍, 最后做饱和操作
int32x4_t vqdmull_s16(int16x4_t a, int16x4_t b);
r:表示舍入计算,例如
// 将a与b的和减半,同时做rounding 操作, 每个通道可以表达为: (ai + bi + 1) >> 1
int8x8_t vrhadd_s8(int8x8_t a, int8x8_t b);
p:表示pairwise计算。例如
// 将a, b向量的相邻数据进行两两和操作
int8x8_t vpadd_s8(int8x8_t a, int8x8_t b);
(2)<指令名>表示具体操作,比如 add
,sub
。
(3)<sharp>
l:long,表示长指令,输出数据的基本类型位数是输入的2倍,例如
uint16x8_t vaddl_u8(uint8x8_t a, uint8x8_t b);
n:narrow,表示窄指令,输出数据的基本类型位数是输入的一半,例如
uint32x2_t vmovn_u64(uint64x2_t a);
w:wide,第一个输入向量和输出向量类型一样,且是第二个输入向量元素长度的2倍,例如
uint16x8_t vsubw_u8(uint16x8_t a, uint8x8_t b);
_high:AArch64专用,而且和 l/n 配合使用。当使用 l(Long) 时,表示输入向量只有高 64bit 有效;当使用 n(Narrow) 时,表示输出只有高 64bit 有效。
// a 和 b 只有高 64bit 参与运算
int16x8_t vsubl_high_s8(int8x16_t a, int8x16_t b);
_n:表示有标量参与向量计算,例如
// 向量 a 中的每个元素右移 n 位
int8x8_t vshr_n_s8(int8x8_t a, const int n);
_lane: 指定向量中某个通道参与向量计算,例如
// 取向量 v 中下标为 lane 的元素与向量 a 做乘法计算
int16x4_t vmul_lane_s16(int16x4_t a, int16x4_t v, const int lane);
(4)[后缀]
后缀如果没有,表示64位并行;如果后缀是q,表示128位并行。
(5)<数据基本类型简写>
数据基本类型简写:s8,s16,s32,s64,u8,u16,u32,u64,f16,f32,f64
例如:
vadd_u16:两个uint16x4相加为一个uint16x4
vaddq_u16:两个uint16x8相加为一个uint16x8