0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

NEON編程中的一些常見優(yōu)化技巧

安芯教育科技 ? 來源:安謀科技學(xué)堂 ? 作者:安謀科技學(xué)堂 ? 2022-12-12 09:11 ? 次閱讀

1.簡介

讀過上一篇文章“ARM NEON快速上手指南”之后,相信你已經(jīng)對ARM NEON編程有了基本的認識。但在真正利用ARM NEON優(yōu)化程序性能時,還有很多編程技巧和注意事項。本文將結(jié)合本人的一些開發(fā)經(jīng)歷,介紹NEON編程中的一些常見優(yōu)化技巧,希望能對用戶在NEON實際開發(fā)中有些借鑒意義。

2.NEON優(yōu)化技術(shù)

在利用NEON優(yōu)化程序時,有下述幾項比較通用的優(yōu)化技巧。

2.1 降低數(shù)據(jù)依賴性

在ARM v7-A NEON指令通常需要3~9個指令周期,NEON指令比ARM指令需要更多周期數(shù)。因此,為了減少指令延時,最好避免將當前指令的目的寄存器當作下條指令的源寄存器。如下例所示:

// 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實現(xiàn)一
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實現(xiàn)二
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實現(xiàn)一中,我們把目的寄存器立刻當作源寄存器;在NEON實現(xiàn)二中,我們重新排布了指令,并給予目的寄存器盡量多的延時。經(jīng)過測試實現(xiàn)二比實現(xiàn)一快30%。由此可見,降低數(shù)據(jù)依賴性對于提高程序性能有重要意義。一個好消息是編譯器能自動調(diào)整NEON intrinsics以降低數(shù)據(jù)依賴性。這個利用NEON intrinsics的一個很大優(yōu)勢。

2.2 減少跳轉(zhuǎn)

NEON指令集沒有跳轉(zhuǎn)指令,當需要跳轉(zhuǎn)時,我們需要借助ARM指令。在ARM處理器中,分支預(yù)測技術(shù)被廣泛使用。但是一旦分支預(yù)測失敗,懲罰還是比較高的。因此我們最好盡量減少跳轉(zhuǎn)指令的使用。其實,在有些情況下,我們可以用邏輯運算來代替跳轉(zhuǎn),如下例所示:

// C實現(xiàn)
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實現(xiàn)
//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指令集提供了下列指令來幫助用戶實現(xiàn)上述邏輯實現(xiàn):

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

? VBIT, VBIF, VBSL……

減少跳轉(zhuǎn),不僅僅是在NEON中使用的技巧,是一個比較通用的問題。即使在C程序中,這個問題也是值得注意的。

2.3 其它技巧

在ARM NEON編程時,一種功能有時有多種實現(xiàn)方式,但是更少的指令不總是意味著更好的性能,要依據(jù)測試結(jié)果和profiling數(shù)據(jù),具體問題具體分析。下面列出來我遇到的一些特殊情況。2.3.1 浮點累加指令通常情況下,我們會用VMLA/VMLS來代替VMUL + VADD/ VMUL + VSUB,這樣使用較少的指令,完成更多的功能。但是與浮點VMUL相比,浮點VMLA/VMLS具有更長的指令延時,如果在指令延時中間不能插入其它計算的情況下,使用浮點VMUL + VADD/ VMUL + VSUB反而具有更好的性能。一個真實例子就是Ne10庫函數(shù)的浮點FIR函數(shù)。代碼片段如下所示:

實現(xiàn)1:在兩條VMLA指令之間,僅有VEXT指令。而根據(jù)指令延時表,VMLA需要9個周期。

實現(xiàn)2:對于qAcc0,依然存在指令延時。但是VADD/VMUL只需要5個周期。下列代碼中周期n粗略地表示了指令執(zhí)行需要的周期數(shù)。與實現(xiàn)1相比,實現(xiàn)2節(jié)省了6個周期。性能測試也表明實現(xiàn)2具有更好的性能。

實現(xiàn) 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
得到最終結(jié)果 qAcc0需要36個指令周期。
實現(xiàn) 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
得到最終結(jié)果 qAcc0需要24個指令周期。
與實現(xiàn)1相比,三條VADD指令需要6個發(fā)射指令周期??偣残枰?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:指令發(fā)射時間

? Result:指令執(zhí)行時間

2.4 小結(jié)

總結(jié)起來,NEON的優(yōu)化技巧主要有以下幾點

? 盡量利用指令執(zhí)行延時,合理安排指令順序

? 少用跳轉(zhuǎn)

? 注意cache命中率

審核編輯:郭婷


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • ARM
    ARM
    +關(guān)注

    關(guān)注

    134

    文章

    8967

    瀏覽量

    365028
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5253

    瀏覽量

    119201

原文標題:Arm NEON學(xué)習(xí)(二)優(yōu)化技術(shù)

文章出處:【微信號:Ithingedu,微信公眾號:安芯教育科技】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    關(guān)于一些有助于優(yōu)化電源設(shè)計的新型材料

    眾所周知,人們對更高電源效率的追求正在推動性能的全方位提升。材料科學(xué)的進步對于優(yōu)化電源設(shè)計和開發(fā)更高效、更緊湊和更可靠的解決方案發(fā)揮著關(guān)鍵作用。下文列出了一些有助于優(yōu)化電源設(shè)計的新材料。
    的頭像 發(fā)表于 08-29 15:26 ?203次閱讀

    PCB設(shè)計常見問題有哪些?

    板)設(shè)計是個至關(guān)重要的環(huán)節(jié)。個優(yōu)秀的PCB設(shè)計不僅能夠保證電子產(chǎn)品的穩(wěn)定運行,還能提高產(chǎn)品的外觀和性能。然而,很多設(shè)計師在PCB設(shè)計中會遇到一些常見的問題,這些問題可能會導(dǎo)致設(shè)計延
    的頭像 發(fā)表于 05-23 09:13 ?585次閱讀
    PCB設(shè)計<b class='flag-5'>中</b>的<b class='flag-5'>常見</b>問題有哪些?

    電子束光刻的參數(shù)優(yōu)化常見問題介紹

    本文從光刻圖案設(shè)計、特征尺寸、電鏡參數(shù)優(yōu)化等方面介紹電子束光刻的參數(shù)優(yōu)化,最后介紹了一些常見問題。
    的頭像 發(fā)表于 03-17 14:33 ?686次閱讀
    電子束光刻的參數(shù)<b class='flag-5'>優(yōu)化</b>及<b class='flag-5'>常見</b>問題介紹

    晶振電路電容電阻的一些基本原理和作用解析

    晶振電路的電容和電阻是調(diào)整和維持晶振振蕩穩(wěn)定性的關(guān)鍵元件。KOAN凱擎小妹帶大家了解下晶振電路電容電阻的一些基本原理和作用。
    的頭像 發(fā)表于 02-20 16:22 ?1410次閱讀
    晶振電路<b class='flag-5'>中</b>電容電阻的<b class='flag-5'>一些</b>基本原理和作用解析

    關(guān)于編程模式的總結(jié)與思考

    淘寶創(chuàng)新業(yè)務(wù)的優(yōu)化迭代是非常高頻且迅速的,在這過程要求技術(shù)也必須是快且穩(wěn)的,而為了適應(yīng)這種快速變化的節(jié)奏,我們在項目開發(fā)過程采用了一些面向拓展以及敏捷開發(fā)的設(shè)計,本文旨在總結(jié)并思考
    的頭像 發(fā)表于 01-03 10:14 ?340次閱讀
    關(guān)于<b class='flag-5'>編程</b>模式的總結(jié)與思考

    大功率插件電感損壞的一些常見表現(xiàn)

    大功率插件電感是電子電路中非常重要的種電子元器件,它對于設(shè)備的穩(wěn)定運行有直接的影響的。如果在設(shè)備的日常運行遇到故障,如何辨別是否是大功率插件電感損壞造成的呢?本篇我們就來簡單探討下大功率插件電感損壞的
    發(fā)表于 12-11 16:22 ?3次下載

    提高嵌入式代碼質(zhì)量的一些方法

    的事情搞復(fù)雜,我希望這些文字能給迷惑的人們指出一些正確的方向,讓他們少走一些彎路,基本做到一分耕耘一分收獲。
    的頭像 發(fā)表于 11-30 09:15 ?348次閱讀

    我們?yōu)槭裁葱枰私?b class='flag-5'>一些先進封裝?

    我們?yōu)槭裁葱枰私?b class='flag-5'>一些先進封裝?
    的頭像 發(fā)表于 11-23 16:32 ?465次閱讀
    我們?yōu)槭裁葱枰私?b class='flag-5'>一些</b>先進封裝?

    分享一些SystemVerilog的coding guideline

    本文分享一些SystemVerilog的coding guideline。
    的頭像 發(fā)表于 11-22 09:17 ?579次閱讀
    分享<b class='flag-5'>一些</b>SystemVerilog的coding  guideline

    LED設(shè)計減小輸出紋波的一些常用辦法

    電子發(fā)燒友網(wǎng)站提供《LED設(shè)計減小輸出紋波的一些常用辦法.doc》資料免費下載
    發(fā)表于 11-15 09:09 ?0次下載
    LED設(shè)計<b class='flag-5'>中</b>減小輸出紋波的<b class='flag-5'>一些</b>常用辦法

    在POWERPCB怎樣去隱藏一些PIN腳

     由于一些板,尤其是U盤等面積很小的板,F(xiàn)LASH只使用了為數(shù)不多的幾個PIN,為了可以讓其它PIN下面可以走線,增加GND網(wǎng)絡(luò)的面積,所以實際操作要隱藏一些PIN。這就需要怎么操
    發(fā)表于 11-02 15:19 ?270次閱讀
    在POWERPCB<b class='flag-5'>中</b>怎樣去隱藏<b class='flag-5'>一些</b>PIN腳

    一些封裝沒有串口,或者串口已經(jīng)被用作其他用途,要如何輸出log?

    是非常不便的,但是有一些替代方法可以幫助我們達到類似的效果。在本文中,我們將探討一些常見的方法,以幫助開發(fā)者在沒有串口輸出的情況下調(diào)試和輸出log。 1. 使用LED燈 在一些嵌入式系
    的頭像 發(fā)表于 10-31 14:37 ?408次閱讀

    針對RF PCBA設(shè)計的一些建議

    射頻(RF)PCBA設(shè)計涉及系列復(fù)雜的考慮因素,包括天線設(shè)計、濾波器設(shè)計以及傳輸線(RF Trace)的優(yōu)化。這些因素對于無線通信和射頻應(yīng)用的性能至關(guān)重要。以下是針對RF PCBA設(shè)計的一些建議。
    的頭像 發(fā)表于 10-30 10:19 ?370次閱讀

    編程晶振常見問題以及使用思路

    編程晶振。簡單來說就是種任意編程頻率的晶振,可以通過個發(fā)生器放大或縮小,有選擇地實現(xiàn)各種總線頻率。在實際應(yīng)用或初步了解,會遇到各種各
    的頭像 發(fā)表于 10-27 14:57 ?1056次閱讀
    可<b class='flag-5'>編程</b>晶振<b class='flag-5'>常見</b>問題以及使用思路

    gvim中常用的一些指令介紹

    在 Vim 編輯器,有一些常用的指令可以幫助我們更高效地編輯文本。以下是一些在 gvim 中常用的指令
    的頭像 發(fā)表于 10-10 15:47 ?1657次閱讀