• NEON优化2:ARM优化高频指令总结


    NEON优化2:ARM优化高频指令总结

    在上篇NEON优化1:软件性能优化、降功耗怎么搞?介绍完后,本篇博客主要分享根据优化经验总结的高频NEON指令。

    总体划分:

    • 读写:数据存取及寄存器读写
    • 计算:加减乘和乘加
    • 转换:位宽、类型、重解释
    • 操作:移位、比较、绝对值、最大值

    前言


    了解指令前,你需要知道什么是向量?什么是向量线?

    • 向量,就是指一个寄存器中同时存的不同值,比如float32x4_t类型,就是个4元素的向量。
    • 向量线,就是向量里的具体元素值。

    随后,为便于理解指令意义,你需要知道指令格式规范是什么?

    • 指令样式:int16x4_t vqmovn_s32(int32x4_t a);

      • int16x4,表示返回的类型,int16表示16位整型,x4表示为4个元素的向量
      • vq中的v表示vector向量运算,q表示饱和运算,比如溢出就截断为最值
        • 而如vmulq_n_f32中,_前面的q是指满位宽128位运算
      • movn表示运算类型,是做位宽变换操作
      • s32表示操作对象为int32
    • 饱和运算:

      • 涉及字符位由宽变窄时,超过当前类型最大值时自动截断成该类型最值
    • 并行位数:

      • arm平台的NEON并行计算:最大支持128位
      • 指令中,类型符下划线前,加q为128位运算,不加为64位运算;
        • vld1q_f32,对应f32x4的类型,满128位
        • vld1_f32,对应f32x2的类型,仅64位
    • 涉及到有标量的运算,函数会有_n_作为标识

      • 涉及标量:float32x4_t vmulq_n_f32(float32x4_t a, float32_t b);
      • 不涉及标量:float32x4_t vmulq_f32(float32x4_t a, float32x4_t b);

    读写


    数据存取

    • 读数据指令:
      • 指令1:vld1q_f32(float p), 读满维128位数据,324,4个32位float数据
      • 指令2:vld2q_f32(float p), 读2个满维128位数据,324*2
      • 指令3:vld4q_f32(float p), 读4个满维128位数据,324*4
      • 效果:从内存读取数据到NEON寄存器中
    • 写数据指令:
      • 指令1:void vst1q_f32(__transfersize(4) float32_t * ptr, float32x4_t val); // 拷贝4个32位的浮点,共128位
      • 指令2:void vst1q_s16(__transfersize(8) int16_t * ptr, int16x8_t val); // 拷贝8个16位的整型,共128位
      • 指令3:void vst1_s16(__transfersize(4) int16_t * ptr, int16x4_t val); // 拷贝4个16位的整型,共 64
      • 效果:将NEON寄存器的值存入到内存(正常浮点变量),存储NEON向量到内存 store

    在向量内设置向量线

    • 指令:float32x4_t vdupq_n_f32(float32_t value);
    • 效果:将所有向量线设置为相同的值,将向量初始为特定相同值
    • 注意:进阶指令可调具体位置去设定相应值

    取向量中的向量线

    • 取前两个向量线指令:float32x2_t vget_low_f32(float32x4_t a); // a1, a2, a3, a4 => a1, a2
    • 取后两个向量线指令:float32x2_t vget_high_f32(float32x4_t a); // a1, a2, a3, a4 => a3, a4
    • 效果:取向量中的部分对,从4个值中取前两个与后两个

    计算


    加法

    • 指令:int32x4_t vaddq_s32(int32x4_t a, int32x4_t b);
    • 效果:vr = a + b

    减法

    • 指令:int32x4_t vsubq_s32(int32x4_t a, int32x4_t b);
    • 效果:vr = a - b

    向量乘标量

    • 指令:float32x4_t vmulq_n_f32(float32x4_t a, float32_t b); // a1, a2, a3, a4;
    • 效果:输出为vr = (a1, a2, a3, a4) * b

    向量与标量乘后加

    • 指令:float32x4_t vmlaq_n_f32(float32x4_t a, float32x4_t b, float32_t c);
    • 效果:向量与标量进行的乘加,结果为 vr = a + b * c
    • 特别注意:不是 a * b + c

    转换


    位宽转换

    • 窄到宽指令:int32x4_t vmovl_s16(int16x4_t a);
    • 效果:把4个16位数值扩展为4个32位的数值,相当于:int16_t a = 3; int32_t b = (int32_t)a;
    • 宽到窄指令:int16x4_t vqmovn_s32(int32x4_t a);
    • 效果:由宽字符到窄字符,由于可能溢位,故要做饱和运算

    类型转换

    • 指令:float32x4_t vcvtq_f32_s32(int32x4_t a);
    • 效果:将32位整型转换为32位浮点,cvt是convert的缩写

    类型重解释

    • 指令:int8x16_t vreinterpretq_s8_f32(float32x4_t a); // 将float32x4的a解释成int8x16_t类型
    • 效果:向量重新解释类型转换运算
    • 说明:不更改值本身,将原始二进制值按不同类型进行解码

    操作


    左右移位

    • 左移指令:uint32x4_t vshlq_n_u32(uint32x4_t a, __constrange(0,31) int b); // 左移,b范围:[0, 31]
    • 右移指令:uint32x4_t vshrq_n_u32(uint32x4_t a, __constrange(1,32) int b); // 右移,b范围:[1, 32]
    • 效果:向量按常数标量左右移位

    差值绝对值

    • 指令:float32x4_t vabdq_f32(float32x4_t a, float32x4_t b);
    • 效果:vr = |a - b|
    • 说明:能直接和标量进行运算的,只有乘法;其他如最大值,加减都不行

    最大值

    • 指令:float32x4_t vmaxq_f32(float32x4_t a, float32x4_t b); // a1, a2, a3, a4; b1, b2, b3, b4;
    • 效果:成对取最大值,输出值为:[max(a1, b1), max(a2, b2), ..., max(a4, b4)]

    折叠最大值

    • 指令:float32x2_t vpmax_f32(float32x2_t a, float32x2_t b); // a1, a2; b1, b2;
    • 效果:取相零对的最大值,输出值为:[max(a1, a2), max(b1, b2)]

    比较大小

    • 小于比较:uint32x4_t vcltq_f32(float32x4_t a, float32x4_t b); // 判断a<b

    • <=比较:uint32x4_t vcleq_s32(int32x4_t a, int32x4_t b); // 判断a<=b

    • >=比较:uint32x4_t vcgeq_s32(int32x4_t a, int32x4_t b); // 判断a>=b

    • 缩略助记:clt(compare less than),cgt(compare grete than),ceq(comprae equal), ge(>=), le(<=)

    • 返回类型:无符号数,位宽与入参相同

    按位选择

    • 指令:int32x4_t vbslq_s32(uint32x4_t a, int32x4_t b, int32x4_t c);
    • 函数用法:将a的每一位进行判断,若是1,则输出b中对应位;否则输出c中对应位
    • 注意事项:通常和比较函数的输出a结合使用,a为无符号比较结果,返回值类型与输入值b/c类型相同

    向量内元素反转

    • 指令:uint8x8_t vrev16_u8(uint8x8_t vec);

    • 助记符:vrev(bit)_(type)

    • 效果:向量内按指定的位数bit,成对相互交换

    • 举例:

      uint8x8_t src = {1,2,3,4,5,6,7,8};
      dst = vrev16_u8(src) --> dst = {2,1,4,3,6,5,8,7} // 按16位为1小组,内部以8位为元素逆序
      dst = vrev64_u8(src) --> dst = {8,7,6,5,4,3,2,1} // 按64位为1小组,内部以8位为元素逆序  
      
      • 1
      • 2
      • 3
  • 相关阅读:
    Python 基础之线程池入门
    GDAL获取Hadoop hdfs tif文件信息(java)
    framework.jar如何导入到android studio中进行framework的开发+系统签名
    “AccelerationMotionCount“ app Tech Support(URL)
    腾讯云上创建 对象存储cos
    Nginx 使用自签名证书实现 https 反代 Spring Boot 中碰到的页面跳转问题
    【深度学习】图像修复的一些模型
    Motorola IPMC761 使用边缘TPU加速神经网络
    中集世联达工业级成熟航运港口人工智能AI产品规模化应用,打造新一代高效能智慧港口和创新数字港口,全球港航人工智能能领军者中集飞瞳
    数说睿见连锁药店城市开店空间模型举例,详解渠道经营方法论
  • 原文地址:https://blog.csdn.net/qq_17256689/article/details/125491975