2.1 NEON指令集综述

NEON是ARM Cortex A系列处理器支持的数据并行技术,和SSE/AVX类似:一条指令以指令级SIMD的方式同时对多个数据进行操作,同时,操作的数据个数由向量寄存器的长度和数据类型共同决定。NEON SIMD寄存器的长度为128位,如果操作32位浮点数,可同时操作4个;如果操作16位整数(short),可同时操作8个;而如果操作8位整数,则可同时操作16个。可见,选择合适的数据类型也是优化性能的重要方法之一,本章中就有相关的实例。

ARMv7 NEON指令集架构具有16个128位的向量寄存器,命名为q0~q15。这16个寄存器又可以拆分成32个64位寄存器,命名为d0~d31。其中qn和d2n,d2n+1是一样的,故使用汇编编写代码时要注意避免产生寄存器覆盖。

使用NEON指令读写数据时,不需要保证数据对齐到16字节。GCC支持的NEON指令集的C语言接口(内置函数,intrinsic)声明在arm_neon.h头文件中。NEON指令集支持的映射到向量寄存器的向量数据类型命名格式为type size x num。

其中:

①type表示元素的数据类型,目前只支持float、int和uint。

②size表示每个元素的数据长度位数,float只支持32位浮点数,int和unit支持8位、16位、32位和64位整数。

③num表示元素数目,即向量寄存器的位数。由于NEON只支持64位和128位向量寄存器,故size和num的乘积只能是64或128。

如uint16x8_t表示每个元素数据类型为uint,大小为16位,每个向量保存8个数,故使用的向量寄存器长度为128位;如float32x4_t表示每个元素的数据类型为32位浮点,向量寄存器可操作4个数据,故使用128位向量寄存器。

NEON内置函数命名方式有两种,分别对应源操作数是否涉及标量,具体解释如下。

1)源操作数涉及标量时,数据类型表示为v op dt_n/lane_type。

其中:

①n表示源操作数是标量而返回向量,lane表示运算涉及向量的一个元素。

②op表示操作,如dup、add、sub、mla等。

③dt是目标向量和源向量长度表示符。

·如果目标向量和源向量长度都为64位,dt为空。

·如果源向量和目标向量长度一致都为128位,dt为q。

·如果目标向量长度比源数向量长度大,且源向量长度都为64位、目标向量长度为128位,dt为l(英文字母,不是数字1)。

·如果多个源向量长度不一致且都不大于目标向量长度(一个源向量长度为64位,另一个为128位,目标向量长度为128位),dt为w。

·如果目标向量长度比源向量长度小,即目标向量长度dt为n。

④type表示源数据类型缩写,如u8表示uint8;u16表示uint16;u32表示uint32;s8表示int8;s16表示int16;s32表示int32;f32表示float32。

2)源操作数全是向量时,数据类型表示为v op dt_type,其中op、dt和type的含义和源操作数为标量时一致。

下面给出几个实例以增加读者理解。

1)内置函数vmla_f32表示使用64位向量寄存器操作32位浮点数据,即源操作数使用的向量寄存器和目标操作数使用的向量寄存器表示都是float32x2_t。

2)内置函数vmlaq_f32表示使用128位向量寄存器操作32位浮点数据,即源操作数使用的向量寄存器和目标操作数使用的向量表示都是float32x4_t。

3)内置函数vmlal_u32表示使用的目标寄存器是128位向量,源寄存器是64位向量,操作32位无符号整数。

4)内置函数vaddw_s32表示使用的目标寄存器是128位向量,源寄存器一个是64位向量,一个是128位向量。

5)内置函数vmovn_u64表示目标寄存器是64位向量,源寄存器是128位向量,即同时操作两个数。