vfmaq_laneq_f32是乘法运算相关指令,vextq_f32是取数据相关指令。具体功能用文字描述比较麻烦,直接看个列子一下就懂了,所以在这里记录下来。

    float32x4_t sum = vdupq_n_f32(0); //sum={0,0,0,0}float _a[] = {1,2,3,4}, _b[] = {5,6,7,8} ;float32x4_t a = vld1q_f32(_a), b = vld1q_f32(_b);//sum1={5,10,15,20}float32x4_t sum1 = vfmaq_laneq_f32(sum, a, b, 0); //sum2={5,10,15,20}+{6,12,18,24} = {11,22,33,44}float32x4_t sum2 = vfmaq_laneq_f32(sum1, a, b, 1); //sum3={11,22,33,44}+{7,14,21,28} = {18,36,54,72}float32x4_t sum3 = vfmaq_laneq_f32(sum2, a, b, 2);float32x4_t r1 = vextq_f32(a,b,1); //r1={2,3,4,5}float32x4_t r2 = vextq_f32(a,b,2); //r2={3,4,5,6}float32x4_t r3 = vextq_f32(a,b,3); //r3={4,5,6,7}

OpenCV Carotene 源码阅读(持续更新)

乘normalize系数:这里Carotene有两种实现方式,一种是转到fp32乘系数,另一种是用量化的方式计算。其中量化的方法比较tricky:

const int16x8_t vScale = vmovq_n_s16(3640);   // (1.0f / 9) * 65536 / 2 = 3640...int16x8_t tt0 = vqrdmulhq_s16(vreinterpretq_s16_u16(t0), vScale);
// vqrdmulhq_s16是对应元素相乘后,先左移一位,再取高16位(相当于右移16位)作为输出
uint8x8_t it0 = vmovn_u16(vreinterpretq_u16_s16(tt0));
vst1_u8(drow + x - 8, it0);

这里Carotene用了vqrdmulhq_s16一口气替代了“乘系数、数据类型转换、右移”的操作,不然得由vget_low/high、vmull、vshrn等多条指令完成,且要分[0-3]和[4-7]两组进行。

嵌入式程序优化(3)——neon内建函数讲解

neon内部函数

初始化寄存器
vcreate_type: 将一个64bit的数据装入vector中,并返回元素类型为type的vector。r=a
vdup_n_type/vmov_n_type: 用类型为type的数值,初始化一个元素类型为type的新vector的所有元素。ri=a
vdupq_n_type/vmovq_n_type:
vdup_lane_type: 用元素类型为type的vector的某个元素,初始化一个元素类型为type的新vector的所有元素。ri=a[b]
vdupq_lane_type:
vmovl_type: 将vector的元素bit位扩大到原来的两倍,元素值不变。
vmovn_type: 用旧vector创建一个新vector,新vector的元素bit位是旧vector的一半。新vector元素只保留旧vector元素的低半部分。
vqmovn_type: 用旧vector创建一个新vector,新vector的元素bit位是旧vector的一半。如果旧vector元素的值超过新vector元素的最大值,则新vector元素就取最大值。否则新vector元素就等于旧vector元素的值。
vqmovun_type: 作用与vqmovn_type类似,但它输入的是有符号vector,输出的是无符号vector。
从内存加载数据进neon寄存器
vld1_type: 按顺序将内存的数据装入neon寄存器,并返回元素类型为type格式的vector
vld1q_type:
vld1_lane_type:用旧vector创建一个同类型的新vector,同时将新vector中指定元素的值改为内存中的值。
vld1q_lane_type:
vld1_dup_type:用type类型的内存中第一个值,初始化一个元素类型为type的新vector的所有元素。
vld1q_dup_type:
vld2_type: 按交叉顺序将内存的数据装入2个neon寄存器(内存第1个数据放入第1个neon寄存器的第1个通道,内存第2个数据放入第2个neon寄存器的第1个通道,内存第3个数据放入第1个neon寄存器的第2个通道,内存第4个数据放入第2个neon寄存器的第2个通道。。。)。并返回有两个vector的结构体
vld2q_type:
vld2_lane_type:
vld2q_lane_type:
vld2_dup_type: 用type类型的内存中第一个值,初始化第一个新vector的所有元素,用内存中第二个值,初始化第二个新vector的所有元素。
vld3_type: 交叉存放,本质上与vld2_type类似,只是这里装载3个neon寄存器
vld3q_type:
vld3_lane_type:
vld3q_lane_type:
vld3_dup_type: 本质上与vld2_dup_type类似
vld4_type: 交叉存放,本质上与vld2_type类似,只是这里装载4个neon寄存器
vld4q_type:
vld4_lane_type:
vld4q_lane_type:
vld4q_dup_type: 本质上与vld2_dup_type类似从neon寄存器加载数据进内存
vst1_type: 将元素类型为type格式的vector的所有元素装入内存
vst1q_type:
vst1_lane_type: 将元素类型为type格式的vector中指定的某个元素装入内存
vst1q_lane_type:
vst2_type: 交叉存放,vld2_type的逆过程
vst2q_type:
vst2_lane_type:
vst2q_lane_type:
vst3_type: 交叉存放,vld3_type的逆过程
vst3q_type:
vst3_lane_type:
vst3q_lane_type:
vst4_type: 交叉存放,vld4_type的逆过程
vst4q_type:
vst4_lane_type:
vst4q_lane_type:
直接获取neon寄存器某个通道的值
vget_low_type: 获取128bit vector的低半部分元素,输出的是元素类型相同的64bit vector。
vget_high_type: 获取128bit vector的高半部分元素,输出的是元素类型相同的64bit vector。
vget_lane_type: 获取元素类型为type的vector中指定的某个元素值。
vgetq_lane_type:
直接设置neon寄存器某个通道的值
vset_lane_type: 设置元素类型为type的vector中指定的某个元素的值,并返回新vector。
vsetq_lane_type:
寄存器数据重排
vext_type: 取第2个输入vector的低n个元素放入新vector的高位,新vector剩下的元素取自第1个输入vector最高的几个元素(可实现vector内元素位置的移动)
vextq_type:
如:src1 = {1,2,3,4,5,6,7,8}src2 = {9,10,11,12,13,14,15,16}dst = vext_type(src1,src2,3)时,则dst = {4,5,6,7,8, 9,10,11}vtbl1_type: 第二个vector是索引,根据索引去第一个vector(相当于数组)中搜索相应的元素,并输出新的vector,超过范围的索引返回的是0.
如:src1 = {1,2,3,4,5,6,7,8}src2 = {0,0,1,1,2,2,7,8}dst = vtbl1_u8(src1,src2)时,则dst = {1,1,2,2,3,3,8,0}vtbl2_type: 数组长度扩大到2个vector
如:src.val[0] = {1,2,3,4,5,6,7,8}src.val[1] = {9,10,11,12,13,14,15,16}src2 = {0,0,1,1,2,2,8,10}dst = vtbl2_u8(src,src2)时,则dst = {1,1,2,2,3,3,9,11}vtbl3_type:
vtbl4_type:
vtbx1_type: 根vtbl1_type功能一样,不过搜索到的元素是用来替换第一个vector中的元素,并输出替换后的新vector,当索引超出范围时,则不替换第一个vector中相应的元素。
vtbx2_type:
vtbx3_type:
vtbx4_type:
vrev16_type: 将vector中的元素位置反转vrev16q_type:
如:src1 = {1,2,3,4,5,6,7,8}dst = vrev16_u8(src1)时,则dst = {2,1,4,3,6,5,8,7}vrev32_type:vrev32q_type:
如:src1 = {1,2,3,4,5,6,7,8}dst = vrev32_u8(src1)时,则dst = {4,3,2,1,8,7,6,5}
vrev64_type:vrev64q_type:
如:src1 = {1,2,3,4,5,6,7,8}dst = vrev32_u8(src1)时,则dst = {8,7,6,5,4,3,2,1}vtrn_type: 将两个输入vector的元素通过转置生成一个有两个vector的矩阵
vtrnq_type:
如:src.val[0] = {1,2,3,4,5,6,7,8}src.val[1] = {9,10,11,12,13,14,15,16}dst = vtrn_u8(src.val[0], src.val[1])时,则 dst.val[0] = {1,9, 3,11,5,13,7,15}dst.val[1] = {2,10,4,12,6,14,8,16}vzip_type: 将两个输入vector的元素通过交叉生成一个有两个vector的矩阵
vzipq_type:
如:src.val[0] = {1,2,3,4,5,6,7,8}src.val[1] = {9,10,11,12,13,14,15,16}dst = vzip_u8(src.val[0], src.val[1])时,则dst.val[0] = {1,9, 2,10,3,11,4,12}dst.val[1] = {5,13,6,14,7,15,8,16}vuzp_type: 将两个输入vector的元素通过反交叉生成一个有两个vector的矩阵(通过这个可实现n-way 交织)
vuzpq_type:
如:src.val[0] = {1,2,3,4,5,6,7,8}src.val[1] = {9,10,11,12,13,14,15,16}dst = vuzp_u8(src.val[0], src.val[1])时,则dst.val[0] = {1,3,5,7,9, 11,13,15}dst.val[1] = {2,4,6,8,10,12,14,16}vcombine_type: 将两个元素类型相同的输入vector拼接成一个同类型但大小是输入vector两倍的新vector。新vector中低部分元素存放的是第一个输入vector元素。
vbsl_type:按位选择,参数为(mask, src1, src2)。mask的某个bit为1,则选择src1中对应的bit,为0,则选择src2中对应的bit。
vbslq_type:加法
vadd_type: ri = ai + bi
vaddq_type:
vaddl_type: 变长加法运算,为了防止溢出
vaddw_type: 第一个vector元素宽度大于第二个vector元素
vaddhn_type: 结果vector元素的类型大小是输入vector元素的一半
vqadd_type: ri = sat(ai + bi) 饱和指令,相加结果超出元素的最大值时,元素就取最大值。
vqaddq_type:
vhadd_type: 相加结果再除2。ri = (ai + bi) >> 1;
vhaddq_type:
vrhadd_type: 相加结果再除2(四舍五入)。ri = (ai + bi + 1) >> 1
vrhaddq_type:
vpadd_type: r0 = a0 + a1, ..., r3 = a6 + a7, r4 = b0 + b1, ..., r7 = b6 + b7
vpaddl_type: r0 = a0 + a1, ..., r3 = a6 + a7;
vpaddlq_type:
vpadal_type: r0 = a0 + (b0 + b1), ..., r3 = a3 + (b6 + b7);
减法
vsub_type: ri = ai - bi
vsubq_type:
vsubl_type:
vsubw_type:
vsubhn_type:
vqsub_type: 饱和指令 ri = sat(ai - bi)
vqsubq_type:
vhsub_type: 相减结果再除2。ri = (ai - bi) >> 1
vhsubq_type:
vrsubhn_type: 相减结果再除2(四舍五入)。ri = (ai - bi + 1) >> 1
乘法
vmul_type: ri = ai * bi
vmulq_type:
vmul_n_type: ri = ai * b
vmulq_n_type:
vmul_lane_type: ri = ai * b[c]
vmulq_lane_type:
vmull_type: 变长乘法运算,为了防止溢出
vmull_n_type:
vmull_lane_type:
vqdmull_type: 变长乘法运算,参与运算的值是有符号数(所以可能溢出),当结果溢出时,取饱和值
vqdmull_n_type:
vqdmull_lane_type:
vqdmulh_type:
vqdmulhq_type:
vqdmulh_n_type:
vqdmulhq_n_type:
vqdmulh_lane_type:
vqdmulhq_lane_type:
vqrdmulh_type:
vqrdmulhq_type:
vqrdmulh_n_type:
vqrdmulhq_n_type:
vqrdmulh_lane_type:
vqrdmulhq_lane_type:
乘加组合运算
vmla_type: ri = ai + bi * ci
vmlaq_type:
vmla_n_type: ri = ai + bi * c
vmlaq_n_type:
vmla_lane_type: ri = ai + bi * c[d]
vmlaq_lane_type:
vmlal_type: 长指令 ri = ai + bi * ci
vmlal_n_type:
vmlal_lane_type:
vfma_f32:ri = ai + bi * ci 在加法之前,bi、ci相乘的结果不会被四舍五入
vqdmlal_type: ri = sat(ai + bi * ci)  bi/ci的元素大小是ai的一半
vqdmlal_n_type: ri = sat(ai + bi * c)
vqdmlal_lane_type: ri = sat(ai + bi * c[d])
乘减组合运算
vmls_type: ri = ai - bi * ci
vmlsq_type:
vmls_n_type: ri = ai - bi * c
vmlsq_n_type:
vmls_lane_type: ri = ai - bi * c[d]
vmlsq_lane_type:
vmlsl_type: 长指令 ri = ai - bi * ci
vmlsl_n_type:
vmlsl_lane_type:
vfms_f32:ri = ai - bi * ci 在减法之前,bi、ci相乘的结果不会被四舍五入
vqdmlsl_type: ri = sat(ai - bi * ci) bi/ci的元素大小是ai的一半
vqdmlsl_n_type: ri = sat(ai - bi * c)
vqdmlsl_lane_type: ri = sat(ai - bi * c[d])
取整
vrndn_f32: to nearest, ties to even
vrndqn_f32:
vrnda_f32: to nearest, ties away from zero
vrndqa_f32:
vrndp_f32: towards +Inf
vrndqp_f32:
vrndm_f32: towards -Inf
vrndqm_f32:
vrnd_f32: towards 0
vrnqd_f32:
比较运算
(结果为true,则所有的bit位被设置为1)vceq_type: ri = ai == bi ? 1...1 : 0...0
vceqq_type:
vcge_type: ri = ai >= bi ? 1...1:0...0
vcgeq_type:
vcle_type: ri = ai <= bi ? 1...1:0...0
vcleq_type:
vcgt_type: ri = ai > bi ? 1...1:0...0
vcgtq_type:
vclt_type: ri = ai < bi ? 1...1:0...0
vcltq_type:
vcage_f32: ri = |ai| >= |bi| ? 1...1:0...0
vcageq_f32:
vcale_f32: ri = |ai| <= |bi| ? 1...1:0...0
vcaleq_f32:
vcagt_f32: ri = |ai| > |bi| ? 1...1:0...0
vcagtq_f32:
vcalt_f32: ri = |ai| < |bi| ? 1...1:0...0
vcaltq_f32:
vtst_type: ri = (ai & bi != 0) ? 1...1:0...0
vtstq_type:
绝对值
vabs_type: ri = |ai|
vabsq_type:
vqabs_type: ri = sat(|ai|)
vqabsq_type:
vabd_type: ri = |ai - bi|
vabdq_type:
vabdl_type: 长指令
vaba_type: ri = ai + |bi - ci|
vabaq_type:
vabal_type: 长指令
取最大最小值
vmax_type: ri = ai >= bi ? ai : bi
vmaxq_type:
vpmax_type: r0 = a0 >= a1 ? a0 : a1, ..., r4 = b0 >= b1 ? b0 : b1, ...
vmin_type: ri = ai <= bi ? ai : bi
vminq_type:
vpmin_type: r0 = a0 <= a1 ? a0 : a1, ..., r4 = b0 <= b1 ? b0 : b1, ...
倒数
vrecpe_type: 求近似倒数,type是f32或者u32
vrecpeq_type:
vrecps_f32:(牛顿 - 拉夫逊迭代)
vrecpsq_f32
注:vrecpe_type计算倒数能保证千分之一左右的精度,如1.0的倒数为0.998047。执行完如下语句后能提高百万分之一精度
float32x4_t recip = vrecpeq_f32(src);此时能达到千分之一左右的精度,如1.0的倒数为0.998047
recip = vmulq_f32 (vrecpsq_f32 (src, rec), rec);执行后能达到百万分之一左右的精度,如1.0的倒数为0.999996
recip = vmulq_f32 (vrecpsq_f32 (src, rec), rec);再次执行后能基本能达到完全精度,如1.0的倒数为1.000000
平方根倒数
vrsqrte_type: 计算输入值的平方根的倒数,type是f32或者u32。输入值不能是负数,否则计算出来的值是nan。
vrsqrteq_type:
vrsqrts_f32
vrsqrtsq_f32
移位运算
vshl_type: ri = ai << bi 如果bi是负数,则变成右移
vshlq_type:
vshl_n_type: ri = ai << b 这里b是常数,如果传入的不是常数(即在编译的时候就要知道b的值),编译时会报错
vshlq_n_type:
vqshl_type: ri = sat(ai << bi)
vqshlq_type:
vrshl_type: ri = round(ai << bi)
vrshlq_type:
vqrshl_type: ri = sat&round(ai << bi)
vqrshlq_type:
vqshl_n_type: ri = sat(ai << b)
vqshlq_n_type:
vqshlu_n_type: ri = ai << b 输入vector是有符号,输出vector是无符号
vqshluq_n_type:
vshll_n_type:vshr_n_type: ri = ai >> b
vshrq_n_type:
vrshr_n_type: ri = round(ai >> b)
vrshrq_n_type:
vsra_n_type: ri = (ai >> c) + (bi >> c)
vsraq_n_type:
vrsra_n_type: ri = round((ai >> c) + (bi >> c))
vrsraq_n_type:
vshrn_n_type:  窄指令ri = ai >> b
vqshrun_n_type:
vqrshrun_n_type:
vqshrn_n_type:
vrshrn_n_type:
vqrshrn_n_type:vsri_n_type:
vsriq_n_type:
vsli_n_type:
vsliq_n_type:
取负
vneg_type: ri = -ai
vnegq_type:
vqneg_type: ri = sat(-ai)
vqnegq_type:
按位运算
vmvn_type: ri = ~ai
vmvnq_type:
vand_type: ri = ai & bi
vandq_type:
vorr_type: ri = ai | bi
vorrq_type:
veor_type: ri = ai ^ bi
veorq_type:
vbic_type: ri = ~ai & bi
vbicq_type:
vorn_type: ri = ai | (~bi)
vornq_type:
统计
vcls_type:
vclz_type:
vcnt_type: 统计向量每个元素有多少bit位是1
vcntq_type:
数据类型转换
vcvt_type1_type2: f32、u32、s32之间的转换。在f32转到u32时,是向下取整,且如果是负数,则转换后为0
vcvtq_type1_type2:
vcvt_n_type1_type2:
vcvtq_n_type1_type2:
vreinterpret_type1_type2: 将元素类型为type2的vector转换为元素类型为type1的vector。数据重新解析
vreinterpretq_type1_type2:

Aarch64 NEON指令笔记一相关推荐

  1. 大前端CPU优化技术--NEON指令介绍

    前言 ARM NEON 可以提升音视频,图像,计算机视觉等计算密集型程序的性能,在上一篇大前端CPU优化技术--NEON技术的介绍中,我们知道一些编译器可以将 C/C++ 代码自动转换为 NEON 指 ...

  2. math-neon基于NEON指令的数学库

    这是一个开源的库,地址为https://code.google.com/p/math-neon/,根据项目介绍应该是利用neon指令实现的数学库:包括三角.对数.指数等基于浮点的运算实现,以及矩阵运算 ...

  3. ARM和NEON指令 very gooooooood.............

    http://blog.csdn.net/chshplp_liaoping/article/details/12752749 在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速.目前在移 ...

  4. ARM和NEON指令 very nice

    在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速.目前在移动上使用最多的是ARM芯片. ARM是微处理器行业的一家知名企业,其芯片结构有:armv5.armv6.armv7和armv8 ...

  5. Android NDK开发之 ARM与NEON指令说明

    原文引自: http://blog.csdn.net/chshplp_liaoping/article/details/12752749 在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加 ...

  6. ARM Neon 编程笔记一(ARM NEON Intrinsics, SIMD运算, 优化心得)

    1. ARM Neon Intrinsics 编程 1.入门:基本能上手写Intrinsics 1.1 Neon介绍.简明案例与编程惯例 1.2 如何检索Intrinsics 1.3 优化效果案例 1 ...

  7. ARM和NEON指令

    转载自:http://blog.csdn.net/chshplp_liaoping/article/details/12752749 在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速. ...

  8. NEON指令优化指南学习之一

    RM平台NEON指令的编译和优化 本文介绍了ARM平台基于ARM v7-A架构的ARM Cortex-A系列处理器(Cortex-A5, Cortex-A7,Cortex-A8, Cortex-A9, ...

  9. Record: Linux指令笔记

    Record: Linux指令笔记 简介 vi 操作终端指令 进程相关 感受 简介 这只是一个小黑使用Linux的一个记录,就是record一下最近使用过的一些指令,很多都是网上搜直接用,并没有仔细看 ...

最新文章

  1. 动态环境下的SLAM:DynaSLAM 论文学习笔记
  2. scrum敏捷项目管理工具leangoo卡片关联上线(可关联卡片,看板,项目)
  3. JS的parseFloat
  4. 内省java_Java内省 – 奇怪的行为
  5. 大型网站后台架构的演变
  6. 手把手一起 图形化安装 k8s 集群
  7. 开发 Windows 8 Bing地图应用(2)
  8. RS错误RSV-VAL-0032之项目未在布局中引用的3种解决办法
  9. 2.1 ListView 源码
  10. pip安装软件报错:Cannot uninstall 'requests'. It is a distutils installed.........
  11. python 字符串 换行_Python基础教程——字符串
  12. Linux -- 查找文件内容或文件位置
  13. AWS EMR 上 Spark 任务 Container killed Exit code 137 错误
  14. PS通道高反差保留计算人物磨皮技巧
  15. 计算机程序ui设计员工资,ui设计师工资一般多少,发展前景怎么样
  16. 更改计算机的主题和桌面背景,禁止更改电脑主题背景的技巧
  17. 模态对话框和非模态对话框的区别1
  18. 使用anaconda配置python3.6环境安装pyltp超详细教程,使用pycharm调用
  19. java 日期格式常量_Java 日期格式类
  20. go-micro使用Consul做服务发现的方法和原理

热门文章

  1. 运放输出接电容会带来的问题
  2. 【多模态】9、GLIP | 首次将 object detection 重建为 phrase grounding 任务
  3. 洛奇英雄传老福单机版服务器不显示,洛奇英雄传官方网站
  4. CANoe-诊断控制台实现同一个目标ECU的物理寻址和功能寻址
  5. 『对话』埃森哲互动Lamy:数字营销的变与不变
  6. Git 远程仓库/ssh
  7. opc协议 服务器,使用OPC UA协议在python中进行服务器端客户端编程
  8. Aseprite安装教程
  9. STM8硬件线路设计基础
  10. 递推和递归的方法解决猴子吃桃问题(10天延伸到N天)——Java