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

    文章

    8736

    浏览量

    363307
  • 寄存器
    +关注

    关注

    30

    文章

    5181

    浏览量

    118339

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

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

收藏 人收藏

    评论

    相关推荐

    电子束光刻的参数优化常见问题介绍

    本文从光刻图案设计、特征尺寸、电镜参数优化等方面介绍电子束光刻的参数优化,最后介绍了一些常见问题。
    的头像 发表于 03-17 14:33 456次阅读
    电子束光刻的参数<b class='flag-5'>优化</b>及<b class='flag-5'>常见</b>问题介绍

    一些有关通信电路的资料?

    有关嵌入式之间DSP、ARM、FPGA三者之间和这三款芯片和外部电路之间通信的一些资料,比如说芯片之间的并行通信和芯片和外部电路之间的串行通信,MODBUS、DP、CAN等,一些一些常用的通信协议的
    发表于 03-03 18:53

    PSOC Creator 4.4是否有一些设置可以阻止strtok操作?

    ,“;”); 我无法让指针指向每个代币的第个字符。 我想知道 PSOC Creator 4.4 是否有一些设置可以阻止 strtok 操作。
    发表于 01-24 08:31

    有关通信协议时一些问题?

    工业上应用到许多种协议,按通信同时刻发送的数据位数分为串行通信和并行通信,首先我见并行通信多在芯片之间进行通信,而串行通信则在外部设备和芯片之间进行的一些通信,对于常见的串行通信,比如
    发表于 01-14 00:58

    MATLAB编程优化的应用介绍

    用途的优化软件包可以放松编程知识。为了有效地使用它们,仍有两个方面需要解决。 有几本关于优化主题的优秀书籍,其中有几本是最近发行的。它们的大多数都深入地涵盖了这
    发表于 09-21 07:07

    《 AI加速器架构设计与实现》+学习和一些思考

    AI加速器设计的学习和一些思考 致谢 首先感谢电子发烧友论坛提供的书籍 然后为该书打个广告吧,32K的幅面,非常小巧方便,全彩印刷,质量精良,很有质感。 前言 设计神经网络首先要考虑的几个问题
    发表于 09-16 11:11

    RealView编译工具NEON矢量化编译器指南

    向量指令。 编译器可以从dspfns.h头文件向量化常规的C和C++操作,如+和一些ITU内部函数。 作为编译器矢量化的替代方案,RVCT还支持将霓虹灯内部函数作为矢量化编译器和编写汇编器代码之间的SIMD代码生成的中间步骤。 有关详细信息,请参阅《RVCT编译器参考指南》
    发表于 08-12 06:22

    想往ROM预存一些数据,需要怎么操作?

    请问想往ROM预存一些数据,需要怎么操作?没有找到教程
    发表于 08-11 10:36

    如何将Arm Neon C#内部函数与Unity Burst编译器起使用

    如何通过并行操作数据来帮助提高性能。 •编写编译器可以根据Neon指令自动优化的代码的最佳实践。 •当编译器错过Neon优化机会时,如何使用Arm
    发表于 08-10 07:11

    Arm Neon技术指南

    本指南介绍了Arm Neon技术,即用于执行Armv8-A或Armv8-R结构剖面的高级 SIMD(单指示多数据)架构扩展,Neon技术为指令设置架构提供了专门的扩展,提供了可同时在多个
    发表于 08-08 06:13

    如何使用Arm Compiler 6自动矢量化功能为Neon编译

    编程序可以是种替代方法有经验的程序员。 本指南展示了如何使用Arm Compiler 6的自动向量化功能来自动地生成包含Armv8高级SIMD指令的代码。它包含了一些例子探索
    发表于 08-02 19:31

    使用GNU和ARM RealView®编译工具生成NEON代码的不同方法

    NEON技术在实现高级SIMD架构扩展的ARM处理器中提供单指令多数据(SIMD)操作。这些操作可以显著加快对大型数据集的重复操作。这在诸如媒体编解码器之类的应用是有用的。 许多使用该技术
    发表于 08-02 16:00

    学习架构-用Neon优化C代码intrinsic

    本指南向您展示了如何在C或C++代码中使用Neon内部函数来利用Armv8体系结构的高级SIMD技术。简单的例子展示了如何使用这些本质并提供了解释其目的的机会。 想要使用高级SIMD的低级软件工程
    发表于 08-02 10:32

    如何使用Neon intrinsic的矢量处理代码迁移到氦intrinsic

    。 •浮点单元(FPU)的寄存器被重新用作矢量寄存器。 •一些矢量指令在氦和氖之间很常见。 然而,氖和氦之间也存在差异,包括以下方面: •矢量寄存器的数量。氦支持8个矢量寄存器,而霓虹灯支持16或32个
    发表于 08-02 07:39

    介绍得物App在资源优化上做的一些实践

    包体积优化中,资源优化一般都是首要且容易有成效的优化方向。资源优化是通过优化APK中的资源项来优化
    的头像 发表于 07-24 09:00 538次阅读
    介绍得物App在资源<b class='flag-5'>优化</b>上做的<b class='flag-5'>一些</b>实践