对于ARM来说,也提供了类似Intel AVX向量指令集的NEON向量指令集,提供了灵活的向量化编程。对于ARM v7架构以上的产品,NEON提供了丰富的向量化指令,能够带来很好的性能提升。像未来手持设备添加机器学习或者其他耗时严重的代码,利用NEON绝对是很好的选择。而且目前Nvidia的tk1开发板使用的是一颗A15的ARM,充分优化也可以带来很好的性能。

NEON支持64位和128位的向量寄存器,因此其向量宽度有64位和128位可选。使用NEON指令时不需要数据对齐,GCC支持NEON指令集的c语言接口,声明在arm_neon.h文件中。

数据结构

NEON指令集支持的数据结构的命名格式分成3个部分,分割方式是”_“和”x”,形式就是Type_SizexNum_t,例如float32x4_t, uint16x8_t等,其具体的含义如下:

  1. Type 第一部分表示数据类型,目前只支持float,int和uint。
  2. Size 第二部分表示每个元素的数据长度位数,float只支持32位,int和uint支持8位,16位,32位和64位。
  3. Num 第三部分表示元素的数量,即含有多少个Size大小的数量,由于NEON只支持64位和128位的向量寄存器,因此Size乘以Num必须等于64或128

指令结构

NEON向量化指令函数的操作非常灵活,允许参数和返回值的位宽不同的操作,即支持操作几个128位宽的向量返回一个64位宽的向量,或者操作几个64位宽的向量返回一个128位宽的向量,当然参数返回值位宽相同更是不成问题;他还支持标量变量的参与,能够在参数中设定使用标量变量。

由于指令种类较多,我们把指令结构分成两种,一种是含标量操作指令,另一种是纯向量的操作指令(即不含标量操作),下面分别描述其命名方式。

对于 含标量操作 的函数,函数名称分为4个部分,其形式为v op dt_n/lane_type,例如vld1q_lane_u64, vst1q_lane_s16等。对于 纯向量操作 的函数,函数名称分为3个部分,形式为v op dt_type,例如vadd_u64,vmlaq_f32,vmla_u32等,具体含义如下:

  1. op表示使用的是什么操作,如add,mla等等。
  2. dt是返回值和参数长度表示符。
    1). 如果返回值和参数长度都是64位,dt为空;
    2). 如果返回值和参数长度都是128位,dt为q;
    3). 如果返回值为比参数长,且参数长度为64位,返回值为128位,dt为l(英文字母l);
    4). 如果多个参数长度不一致且都不大于返回值长度(一个参数长度为64位,另一个为128位,返回值长度为128位),dt为w;
    5). 如果返回值长度比参数长度小,dt为n
  3. 含标量操作的函数名称中才有这一部分,n/lane表示标量的来源。如果参数中的标量就是一个标量,这个地方使用n; 如果参数中的标量是一个向量中的一个元素,这个地方使用lane。
  4. type表示元数据类型缩写。u8表示uint8; u16表示uint16; u32表示uint32; s8表示int8; s16表示int16; s32表示int32; f32表示float32。

看完这些,对照ARM提供的NEON手册再好好看看,很快就能理解了。这里给个官方手册地址( http://infocenter.arm.com/help/topic/com.arm.doc.dui0491h/Badcdfad.html )。这里也举几个例子来说明一下。

  • int8x8_t vadd_s8(int8x8_t a, int8x8_t b); // VADD.I8 d0,d0,d0

没有n/lane部分,是纯向量操作,做的加法操作,参数和返回值位宽相同,都是int8x8的64位向量,类型是int8。

  • float32x4_t vmulq_f32(float32x4_t a, float32x4_t b); // VMUL.F32 q0,q0,q0

没有n/lane部分,是纯向量操作,做的是乘法操作,参数和返回值位宽相同,都是float32x4的128位向量,类型是float32。

  • uint32x4_t vld1q_lane_u32(__transfersize(1) uint32_t const * ptr, uint32x4_t vec, __constrange(0,3) int lane); // VLD1.32 {d0[0]}, [r0]

带有lane说明含有标量,做的是load操作,返回值和参数位宽相同,都是uint32x4的128位向量,类型是uint32。

  • int16x8_t vmull_s8(int8x8_t a, int8x8_t b); // VMULL.S8 q0,d0,d0

没有n/lane部分,是纯向量操作,做的是乘法操作,返回值是的int16x8的128位向量,而参数是int8x8的64位向量,操作的类型是int8。

这里的东西主要是为了解释NEON指令集函数的命名方式,但他也有一些特殊的函数,例如没有返回值的函数,或者参数数量多于2个的函数等,对于这些特例,上面的规则只是一个参考性方式。详细内容还是要参见NEON的官方手册啊。