1.簡(jiǎn)介
讀過(guò)上一篇文章“ARM NEON快速上手指南”之后,相信你已經(jīng)對(duì)ARM NEON編程有了基本的認(rèn)識(shí)。但在真正利用ARM NEON優(yōu)化程序性能時(shí),還有很多編程技巧和注意事項(xiàng)。本文將結(jié)合本人的一些開(kāi)發(fā)經(jīng)歷,介紹NEON編程中的一些常見(jiàn)優(yōu)化技巧,希望能對(duì)用戶在NEON實(shí)際開(kāi)發(fā)中有些借鑒意義。
2.NEON優(yōu)化技術(shù)
在利用NEON優(yōu)化程序時(shí),有下述幾項(xiàng)比較通用的優(yōu)化技巧。
2.1 降低數(shù)據(jù)依賴性
在ARM v7-A NEON指令通常需要3~9個(gè)指令周期,NEON指令比ARM指令需要更多周期數(shù)。因此,為了減少指令延時(shí),最好避免將當(dāng)前指令的目的寄存器當(dāng)作下條指令的源寄存器。如下例所示:
// 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實(shí)現(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實(shí)現(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實(shí)現(xiàn)一中,我們把目的寄存器立刻當(dāng)作源寄存器;在NEON實(shí)現(xiàn)二中,我們重新排布了指令,并給予目的寄存器盡量多的延時(shí)。經(jīng)過(guò)測(cè)試實(shí)現(xiàn)二比實(shí)現(xiàn)一快30%。由此可見(jiàn),降低數(shù)據(jù)依賴性對(duì)于提高程序性能有重要意義。一個(gè)好消息是編譯器能自動(dòng)調(diào)整NEON intrinsics以降低數(shù)據(jù)依賴性。這個(gè)利用NEON intrinsics的一個(gè)很大優(yōu)勢(shì)。
2.2 減少跳轉(zhuǎn)
NEON指令集沒(méi)有跳轉(zhuǎn)指令,當(dāng)需要跳轉(zhuǎn)時(shí),我們需要借助ARM指令。在ARM處理器中,分支預(yù)測(cè)技術(shù)被廣泛使用。但是一旦分支預(yù)測(cè)失敗,懲罰還是比較高的。因此我們最好盡量減少跳轉(zhuǎn)指令的使用。其實(shí),在有些情況下,我們可以用邏輯運(yùn)算來(lái)代替跳轉(zhuǎn),如下例所示:
// C實(shí)現(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實(shí)現(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指令集提供了下列指令來(lái)幫助用戶實(shí)現(xiàn)上述邏輯實(shí)現(xiàn):
? VCEQ, VCGE, VCGT, VCLE, VCLT……
? VBIT, VBIF, VBSL……
減少跳轉(zhuǎn),不僅僅是在NEON中使用的技巧,是一個(gè)比較通用的問(wèn)題。即使在C程序中,這個(gè)問(wèn)題也是值得注意的。
2.3 其它技巧
在ARM NEON編程時(shí),一種功能有時(shí)有多種實(shí)現(xiàn)方式,但是更少的指令不總是意味著更好的性能,要依據(jù)測(cè)試結(jié)果和profiling數(shù)據(jù),具體問(wèn)題具體分析。下面列出來(lái)我遇到的一些特殊情況。2.3.1 浮點(diǎn)累加指令通常情況下,我們會(huì)用VMLA/VMLS來(lái)代替VMUL + VADD/ VMUL + VSUB,這樣使用較少的指令,完成更多的功能。但是與浮點(diǎn)VMUL相比,浮點(diǎn)VMLA/VMLS具有更長(zhǎng)的指令延時(shí),如果在指令延時(shí)中間不能插入其它計(jì)算的情況下,使用浮點(diǎn)VMUL + VADD/ VMUL + VSUB反而具有更好的性能。一個(gè)真實(shí)例子就是Ne10庫(kù)函數(shù)的浮點(diǎn)FIR函數(shù)。代碼片段如下所示:
實(shí)現(xiàn)1:在兩條VMLA指令之間,僅有VEXT指令。而根據(jù)指令延時(shí)表,VMLA需要9個(gè)周期。
實(shí)現(xiàn)2:對(duì)于qAcc0,依然存在指令延時(shí)。但是VADD/VMUL只需要5個(gè)周期。下列代碼中周期n粗略地表示了指令執(zhí)行需要的周期數(shù)。與實(shí)現(xiàn)1相比,實(shí)現(xiàn)2節(jié)省了6個(gè)周期。性能測(cè)試也表明實(shí)現(xiàn)2具有更好的性能。
實(shí)現(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個(gè)指令周期。
實(shí)現(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個(gè)指令周期。
與實(shí)現(xiàn)1相比,三條VADD指令需要6個(gè)發(fā)射指令周期??偣残枰?30個(gè)指令周期。
modules/dsp/NE10_fir.neon.s:line 195
指令延時(shí)請(qǐng)參考下表:
Name | Format | Cycles | Result |
---|---|---|---|
VADD/VSUB/VMUL | Qd,Qn,Dm | 2 | 5 |
VMLA/VMLS | Qd,Qn,Dm | 2 | 9 |
表格來(lái)源于Cortex-A9 NEON Media Processing Engine Revision: r4p1 Technical Reference Manual: 3.4.8。
表格中:? Cycles:指令發(fā)射時(shí)間
? Result:指令執(zhí)行時(shí)間
2.4 小結(jié)
總結(jié)起來(lái),NEON的優(yōu)化技巧主要有以下幾點(diǎn)
? 盡量利用指令執(zhí)行延時(shí),合理安排指令順序
? 少用跳轉(zhuǎn)
? 注意cache命中率
審核編輯:郭婷
-
ARM
+關(guān)注
關(guān)注
134文章
9273瀏覽量
373747 -
寄存器
+關(guān)注
關(guān)注
31文章
5402瀏覽量
122862
原文標(biāo)題:Arm NEON學(xué)習(xí)(二)優(yōu)化技術(shù)
文章出處:【微信號(hào):Ithingedu,微信公眾號(hào):安芯教育科技】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Debian和Ubuntu哪個(gè)好一些?
樹(shù)莓派在自動(dòng)化控制項(xiàng)目中的一些潛在應(yīng)用

嵌入式系統(tǒng)中的代碼優(yōu)化與壓縮技術(shù)
Littrow結(jié)構(gòu)中光柵系統(tǒng)的配置與優(yōu)化
EEPROM編程常見(jiàn)錯(cuò)誤及解決方案
串聯(lián)電容的常見(jiàn)應(yīng)用 如何優(yōu)化串聯(lián)電路的效率
一些常見(jiàn)的動(dòng)態(tài)電路

編程語(yǔ)言的誤區(qū)與常見(jiàn)問(wèn)題
分享一些常見(jiàn)的電路

ASCII碼在編程中的應(yīng)用實(shí)例
LED驅(qū)動(dòng)器應(yīng)用的一些指南和技巧

關(guān)于一些有助于優(yōu)化電源設(shè)計(jì)的新型材料
二進(jìn)制處理中的一些技巧
聚徽觸控 - 教學(xué)一體機(jī)常見(jiàn)故障及解決辦法
PCB設(shè)計(jì)中的常見(jiàn)問(wèn)題有哪些?

評(píng)論