arm NEON学习

参考:arm NEON简介
快速上手指南
清晰的简介
参考手册
SIMD:一条指令处理多个数据。在32bit内核处理器上,如cortexA系列,如果不采用SIMD将会把大量时间花费在处理8-bit或者16-bit的数据上,但处理器本身的ALU,寄存拿起、数据深度是为了32bit的运算而设计的,因此设计NEON。
NEON:是一种基于SIMD思想的ARM,结合了64bit和128bit的SIMD指令集,提供128bit宽的向量运算,该技术从ARM7开始被采用,目前可在ARM Cortex-A和Cortex-A系列处理器使用。
NEON包含16个128位寄存器,拥有100多条完整指令,并且拥有独立的寄存器系统和独立的硬件执行单元,支持8位、16位、32位、64位等数据类型的向量运算,最多可同时对16路8位数据进行并行计算,可用于2D/3D图形图像加速、音视频编解码、数字信号处理等应用。
参考:
ARM NEON技术在车位识别算法中的应用
这里介绍了NEON的基本使用。
汇编指令:汇编指令集合
这个很有用,方便查阅
这里也有指令的详细解释
在这里插入图片描述
在这里插入图片描述

OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint8x16, uint8x8,  u8)
OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int8x16,  int8x8,   s8)
OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint16x8, uint16x4, u16)
OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int16x8,  int16x4,  s16)
OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(uint32x4, uint32x2, u32)
OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(int32x4,  int32x2,  s32)
OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX(float32x4, float32x2, f32)
OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_I64(uint64x2, uint64x1, u64)
OPENCV_HAL_IMPL_NEON_UTILS_SUFFIX_I64(int64x2,  int64x1,  s64)
//add for int array. assumed that count is multiple of 4
 
#include<arm_neon.h>
 
// C version
void add_int_c(int* dst, int* src1, int* src2, int count)
{
  int i;
  for (i = 0; i < count; i++)
    dst[i] = src1[i] + src2[i];
  }
}
 
// NEON version
void add_float_neon1(int* dst, int* src1, int* src2, int count)
{
  int i;
  for (i = 0; i < count; i += 4)
  {
    int32x4_t in1, in2, out;
    in1 = vld1q_s32(src1);
    src1 += 4;
    in2 = vld1q_s32(src2);
    src2 += 4;
    out = vaddq_s32(in1, in2);
    vst1q_s32(dst, out);
    dst += 4;
  }
}

代码中的vld1q_s32会被编译器转换成vld1.32 {d0, d1}, [r0]指令,同理vaddq_s32和vst1q_s32被转换成vadd.i32 q0, q0, q0,vst1.32 {d0, d1}, [r0]。

指令格式
在这里插入图片描述
NEON用法包括四种:

  • NEON优化库
  • 向量化编译器
  • NEON内联函数
  • NEON汇编
    我主要用到第三种:
    在这里插入图片描述
    A:使用Neon intrinsics函数,可以在直接接触ASM的情况下,使用Neon。这些函数被定义在:

arm_neon.h 中。类似于:
vadd_s8 (int8x8_t __a, int8x8_t __b)
此方法需要注意2点:
1.必须: #include
2.编译时必须加入; -mfloat-abi=softfp -mfpu=neon

B:使用汇编指令:
效率最高. 使用intrinsics没法控制寄存器分配和内存对齐等。

0. rgb2gray函数:

在这里插入图片描述
在这里插入图片描述

  • uint8×8×3:第一个8是位数,第二个是一次处理8个数据,3是表示三维向量
  • vld表示从内存加载到寄存器,vst表示从寄存器传给内存
  • vdup_n_u8表示把uint8复制n个;
  • mla:先乘后加,l表示变长,位宽变为原来的两倍,temp是16bit的
  • int8x8_t vshrn_n_s16 (int16x8_t, const int) n表示变窄,q表示输出128bit寄存器,右移n位并变窄
  • NEON 的指令都是以v 字母开头的,例如:vadd.i16q0,q1,q2,这就是一个NEON 的指令了,很明显的特点就是v 开头,i 主要用来表明是一个整型(int),16 表示一个16 位的型,q0,q1,q2 都是128 位的寄存器(q 打头的寄存器都是128 位的)。这个指令就是让q1,q2 中装载8 个16位的数据,然后执行加法操作,最后放到q0中去。这么一个指令就完成了8次加法运算,这也就是性能的提升,对于其他运算也是如此。
  • 在这里插入图片描述
    看一些其他的例子:

1. 累加和

在这里插入图片描述

  • vget_low_u32表示获取128位的低64位
  • vadd_u32因为处理的不是128位寄存器,所以不加q
  • vget_lane就是按顺序读取
  • vld1表示一维向量

2. 两个数组相关映射元素的乘积和

在这里插入图片描述
在这里插入图片描述
部分指令的中文解释

3. 矩阵乘法

neon矩阵乘法博客
通用矩阵乘法优化
NEON有32个64位寄存器,因而加载所有的输入矩阵元素到16个64-bit寄存器,我们仍然有16个64位寄存器做后续的处理。
D和Q寄存器:多数的NEON指令有两种方法来访问寄存器组:

作为32个双字寄存器,64-bit位宽,命名为d0-d31
作为16个四字寄存器,128-bit位宽,命名为q0-q15 

这些寄存器中一个Q寄存器是一对D寄存器的别名,如Q0是d0和d1寄存器对的别名,寄存器中的值可以用两种方式访问。这种实现方式很类似C语言里的union联合的数据结构。对于浮点的矩阵乘法,我们会经常使用Q寄存器的表达方式,因为经常会处理4个32-bit的单精度浮点,这对应于128-bit的Q寄存器。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值