0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

NEON编程中的一些常见优化技巧

安芯教育科技 ? 来源:安谋科技学堂 ? 作者:安谋科技学堂 ? 2022-12-12 09:11 ? 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

1.简介

读过上一篇文章“ARM NEON快速上手指南”之后,相信你已经对ARM NEON编程有了基本的认识。但在真正利用ARM NEON优化程序性能时,还有很多编程技巧和注意事项。本文将结合本人的一些开发经历,介绍NEON编程中的一些常见优化技巧,希望能对用户在NEON实际开发中有些借鉴意义。

2.NEON优化技术

在利用NEON优化程序时,有下述几项比较通用的优化技巧。

2.1 降低数据依赖性

在ARM v7-A NEON指令通常需要3~9个指令周期,NEON指令比ARM指令需要更多周期数。因此,为了减少指令延时,最好避免将当前指令的目的寄存器当作下条指令的源寄存器。如下例所示:

// C代码
float SumSquareError_C(const float* src_a, const float* src_b, int count) 
{
  float sse = 0u;
  int i;
  for (i = 0; i < count; ++i) {
    float diff = src_a[i] - src_b[i];
    sse += (float)(diff * diff);
  }
  return sse;
}
// NEON实现一
float SumSquareError_NEON1(const float* src_a, const float* src_b, int count)
{
  float sse;
  asm volatile (
    "veor    q8, q8, q8                        
"
    "veor    q9, q9, q9                        
"
    "veor    q10, q10, q10                     
"
    "veor    q11, q11, q11                     
"

  "1:                                          
"
    "vld1.32     {q0, q1}, [%0]!               
"
    "vld1.32     {q2, q3}, [%0]!               
"
    "vld1.32     {q12, q13}, [%1]!             
"
    "vld1.32     {q14, q15}, [%1]!             
"
    "subs       %2, %2, #16                    
"
    // q0, q1, q2, q3 是vsub的目的地寄存器.
    // 也是vmla的源寄存器。
    "vsub.f32   q0, q0, q12                    
"
    "vmla.f32   q8, q0, q0                     
"

    "vsub.f32   q1, q1, q13                    
"
    "vmla.f32   q9, q1, q1                     
"

    "vsub.f32   q2, q2, q14                    
"
    "vmla.f32   q10, q2, q2                    
"

    "vsub.f32   q3, q3, q15                    
"
    "vmla.f32   q11, q3, q3                    
"
    "bgt        1b                             
"

    "vadd.f32   q8, q8, q9                     
"
    "vadd.f32   q10, q10, q11                  
"
    "vadd.f32   q11, q8, q10                   
"
    "vpadd.f32  d2, d22, d23                   
"
    "vpadd.f32  d0, d2, d2                     
"
    "vmov.32    %3, d0[0]                      
"
    : "+r"(src_a),
      "+r"(src_b),
      "+r"(count),
      "=r"(sse)
    :
    : "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11","q12", "q13","q14", "q15");
  return sse;
}
// NEON实现二
float SumSquareError_NEON2(const float* src_a, const float* src_b, int count)
{
  float sse;
  asm volatile (
    "veor    q8, q8, q8                        
"
    "veor    q9, q9, q9                        
"
    "veor    q10, q10, q10                     
"
    "veor    q11, q11, q11                     
"

  "1:                                          
"
    "vld1.32     {q0, q1}, [%0]!               
"
    "vld1.32     {q2, q3}, [%0]!               
"
    "vld1.32     {q12, q13}, [%1]!             
"
    "vld1.32     {q14, q15}, [%1]!             
"
    "subs       %2, %2, #16                    
"
    "vsub.f32   q0, q0, q12                    
"
    "vsub.f32   q1, q1, q13                    
"
    "vsub.f32   q2, q2, q14                    
"
    "vsub.f32   q3, q3, q15                    
"
    
    "vmla.f32   q8, q0, q0                     
"
    "vmla.f32   q9, q1, q1                     
"
    "vmla.f32   q10, q2, q2                    
"
    "vmla.f32   q11, q3, q3                    
"
    "bgt        1b                             
"

    "vadd.f32   q8, q8, q9                     
"
    "vadd.f32   q10, q10, q11                  
"
    "vadd.f32   q11, q8, q10                   
"
    "vpadd.f32  d2, d22, d23                   
"
    "vpadd.f32  d0, d2, d2                     
"
    "vmov.32    %3, d0[0]                      
"
    : "+r"(src_a),
      "+r"(src_b),
      "+r"(count),
      "=r"(sse)
    :
    : "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13","q14", "q15");
  return sse;
}

在NEON实现一中,我们把目的寄存器立刻当作源寄存器;在NEON实现二中,我们重新排布了指令,并给予目的寄存器尽量多的延时。经过测试实现二比实现一快30%。由此可见,降低数据依赖性对于提高程序性能有重要意义。一个好消息是编译器能自动调整NEON intrinsics以降低数据依赖性。这个利用NEON intrinsics的一个很大优势。

2.2 减少跳转

NEON指令集没有跳转指令,当需要跳转时,我们需要借助ARM指令。在ARM处理器中,分支预测技术被广泛使用。但是一旦分支预测失败,惩罚还是比较高的。因此我们最好尽量减少跳转指令的使用。其实,在有些情况下,我们可以用逻辑运算来代替跳转,如下例所示:

// C实现
if( flag )
{
        dst[x * 4]     = a;
        dst[x * 4 + 1] = a;
        dst[x * 4 + 2] = a;
        dst[x * 4 + 3] = a;
}
else
{
        dst[x * 4]     = b;
        dst[x * 4 + 1] = b;
        dst[x * 4 + 2] = b;
        dst[x * 4 + 3] = b;
}
// NEON实现
//dst[x * 4]     = (a&Eflag) | (b&~Eflag);
//dst[x * 4 + 1] = (a&Eflag) | (b&~Eflag);
//dst[x * 4 + 2] = (a&Eflag) | (b&~Eflag);
//dst[x * 4 + 3] = (a&Eflag) | (b&~Eflag);

VBSL qFlag, qA, qB

ARM NEON指令集提供了下列指令来帮助用户实现上述逻辑实现:

? VCEQ, VCGE, VCGT, VCLE, VCLT……

? VBIT, VBIF, VBSL……

减少跳转,不仅仅是在NEON中使用的技巧,是一个比较通用的问题。即使在C程序中,这个问题也是值得注意的。

2.3 其它技巧

在ARM NEON编程时,一种功能有时有多种实现方式,但是更少的指令不总是意味着更好的性能,要依据测试结果和profiling数据,具体问题具体分析。下面列出来我遇到的一些特殊情况。2.3.1 浮点累加指令通常情况下,我们会用VMLA/VMLS来代替VMUL + VADD/ VMUL + VSUB,这样使用较少的指令,完成更多的功能。但是与浮点VMUL相比,浮点VMLA/VMLS具有更长的指令延时,如果在指令延时中间不能插入其它计算的情况下,使用浮点VMUL + VADD/ VMUL + VSUB反而具有更好的性能。一个真实例子就是Ne10库函数的浮点FIR函数。代码片段如下所示:

实现1:在两条VMLA指令之间,仅有VEXT指令。而根据指令延时表,VMLA需要9个周期。

实现2:对于qAcc0,依然存在指令延时。但是VADD/VMUL只需要5个周期。下列代码中周期n粗略地表示了指令执行需要的周期数。与实现1相比,实现2节省了6个周期。性能测试也表明实现2具有更好的性能。

实现 1: VMLA
VEXT qTemp1,qInp,qTemp,#1
VMLA qAcc0,qInp,dCoeff_0[0]-- cycle 0

VEXT qTemp2,qInp,qTemp,#2
VMLA qAcc0,qTemp1,dCoeff_0[1] -- cycle 9

VEXT qTemp3,qInp,qTemp,#3
VMLA qAcc0,qTemp2,dCoeff_1[0] -- cycle 18

VMLA qAcc0,qTemp3,dCoeff_1[1] -- cycle 27
得到最终结果 qAcc0需要36个指令周期。
实现 2:  VMUL+VADD
VEXT qTemp1,qInp,qTemp,#1
VMLA qAcc0,qInp,dCoeff_0[0] ]-- cycle 0
VMUL qAcc1,qTemp1,dCoeff_0[1]

VEXT qTemp2,qInp,qTemp,#2
VMUL qAcc2,qTemp2,dCoeff_1[0]
VADD qAcc0, qAcc0, qAcc1-- cycle 9

VEXT qTemp3,qInp,qTemp,#3
VMUL qAcc3,qTemp3,dCoeff_1[1]
VADD qAcc0, qAcc0, qAcc2-- cycle 14 

VADD qAcc0, qAcc0, qAcc3-- cycle 19
得到最终结果 qAcc0需要24个指令周期。
与实现1相比,三条VADD指令需要6个发射指令周期。总共需要 30个指令周期。

modules/dsp/NE10_fir.neon.s:line 195

指令延时请参考下表:

Name Format Cycles Result
VADD/VSUB/VMUL Qd,Qn,Dm 2 5
VMLA/VMLS Qd,Qn,Dm 2 9

表格来源于Cortex-A9 NEON Media Processing Engine Revision: r4p1 Technical Reference Manual: 3.4.8。

表格中:? Cycles:指令发射时间

? Result:指令执行时间

2.4 小结

总结起来,NEON的优化技巧主要有以下几点

? 尽量利用指令执行延时,合理安排指令顺序

? 少用跳转

? 注意cache命中率

审核编辑:郭婷


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • ARM
    ARM
    +关注

    关注

    134

    文章

    9395

    浏览量

    379848
  • 寄存器
    +关注

    关注

    31

    文章

    5447

    浏览量

    125375

原文标题:Arm NEON学习(二)优化技术

文章出处:【微信号:Ithingedu,微信公众号:安芯教育科技】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    射频工程师需要知道的一些常见转接头

    ,是由于转接头的损坏造成的,而且有些接头的连接固定的方式不对,每次修好的仪器,过去后客户又按照他们原来的方式去拧紧了。特别是在一些生产型的企业,由于操作人员流动性比较
    的头像 发表于 08-06 17:39 ?264次阅读
    射频工程师需要知道的<b class='flag-5'>一些</b><b class='flag-5'>常见</b>转接头

    在低功耗蓝牙产品开发的过程,会涉及到一些参数的选择和设定,这些参数是什么意思,该如何设定呢?(蓝牙广播)

    在低功耗蓝牙产品开发的过程,会涉及到一些参数的选择和设定,这些参数是什么意思,该如何设定呢?在此介绍一些: 蓝牙的广播类型(Advertising Type) 可连接广播(ADV_IND):允许
    发表于 06-25 18:25

    Debian和Ubuntu哪个好一些

    兼容性对比Debian和Ubuntu哪个好一些,并为您揭示如何通过RAKsmart服务器释放Linux系统的最大潜能。
    的头像 发表于 05-07 10:58 ?434次阅读

    树莓派在自动化控制项目中的一些潜在应用

    自动化控制项目中的一些潜在应用。之前,我们已经为Arduino平台探讨了相同的话题。我们确定Arduino是个出色的教育工具,但由于一些限制,它无法在工业环境完全
    的头像 发表于 03-25 09:45 ?280次阅读
    树莓派在自动化控制项目中的<b class='flag-5'>一些</b>潜在应用

    嵌入式系统的代码优化与压缩技术

    不是继续遍历整个数组。 函数调用优化:频繁的函数调用会带来额外的开销,包括参数传递、栈操作等。对于一些短小且频繁调用的函数,可以将其定义为内联函数,这样在编译时,函数代码会直接嵌入到调用处,减少函数
    发表于 02-26 15:00

    Littrow结构光栅系统的配置与优化

    的Littrow配置 我们在这里提供了个根据Littrow配置的光学装置,而且通过一些编程,即使在波长或光栅周期的变化下,也能保持光栅的最佳位置。 高效偏振无关传输光栅的分析与设计 我们演示了如何严格分析二元光栅的偏振相关
    发表于 12-25 15:35

    EEPROM编程常见错误及解决方案

    EEPROM(电可擦可编程只读存储器)在编程过程可能会遇到多种错误。以下是一些常见的EEPROM编程
    的头像 发表于 12-16 17:08 ?5185次阅读

    串联电容的常见应用 如何优化串联电路的效率

    串联电容的常见应用 串联电容是常见的电路元件连接方式,其在家居、工业及电力系统等多个领域有着广泛的应用。以下是串联电容的一些常见应用:
    的头像 发表于 12-02 16:35 ?4382次阅读

    一些常见的动态电路

    无论是模电还是数电,理论知识相对来说还是比较枯燥,各种电路原理理解清楚不算容易,换种生动形象的方式或许会增加一些趣味性,也更容易理解这些知识。下面整理了一些常见的电路,以动态图形的方
    的头像 发表于 11-16 09:26 ?1241次阅读
    <b class='flag-5'>一些</b><b class='flag-5'>常见</b>的动态电路

    编程语言的误区与常见问题

    误区编程语言的选择 常见问题: 初学者在选择编程语言时,往往会被市场上的热门语言所吸引,而忽视了自己的实际需求和兴趣。 一些开发者认为某
    的头像 发表于 11-15 09:35 ?851次阅读

    分享一些常见的电路

    理解模电和数电的电路原理对于初学者来说可能比较困难,但通过一些生动的教学方法和资源,可以有效地提高学习兴趣和理解能力。 下面整理了一些常见的电路,以动态图形的方式展示。 整流电路 单相桥式整流
    的头像 发表于 11-13 09:28 ?898次阅读
    分享<b class='flag-5'>一些</b><b class='flag-5'>常见</b>的电路

    ASCII码在编程的应用实例

    ASCII码(American Standard Code for Information Interchange,美国信息交换标准代码)在编程中有着广泛的应用。以下是一些ASCII码在编程
    的头像 发表于 11-10 09:43 ?1701次阅读

    MySQL性能优化浅析及线上案例

    作者:京东健康 孟飞 1、 数据库性能优化的意义 业务发展初期,数据库中量般都不高,也不太容易出一些性能问题或者出的问题也不大,但是当数据库的量级达到定规模之后,如果缺失有效的预警
    的头像 发表于 10-22 15:17 ?1083次阅读
    MySQL性能<b class='flag-5'>优化</b>浅析及线上案例

    LED驱动器应用的一些指南和技巧

    电子发烧友网站提供《LED驱动器应用的一些指南和技巧.pdf》资料免费下载
    发表于 09-25 11:35 ?0次下载
    LED驱动器应用的<b class='flag-5'>一些</b>指南和技巧

    关于一些有助于优化电源设计的新型材料

    众所周知,人们对更高电源效率的追求正在推动性能的全方位提升。材料科学的进步对于优化电源设计和开发更高效、更紧凑和更可靠的解决方案发挥着关键作用。下文列出了一些有助于优化电源设计的新材料。
    的头像 发表于 08-29 15:26 ?816次阅读