本文選自極術專欄《Infrastructure開源軟件 on Arm》的Arm NEON學習系列。前面我們學習了如何快速上手開始NEON編程以及ArmNEON優(yōu)化技術,本篇我們將對比NEON匯編與NEON Intrinsics編程的優(yōu)缺點。
1.簡介
ARMNEON編程主要有兩種最常用的方式手寫匯編和Intrinsics。本文將對比NEON匯編與NEON Intrinsics編程的優(yōu)缺點。
2.NEON匯編與Intrinsics
NEON匯編與Intrinsics各有優(yōu)缺點:
NEON匯編與Intrinsics各有優(yōu)缺點:但實際情況遠遠比這些復雜很多,特別是涉及到ARM v7-A/v8-A跨平臺的時候。下面我們結(jié)合實例做一些更深入的分析。
2.1 編程
對于初學者來說,Intrinsics比較易學易用。但是對于有匯編經(jīng)驗的開發(fā)者來說,可能更熟悉NEON匯編編程,切換到Intrinsics反倒需要有個適應過程。下文列出了實際開發(fā)中的一些問題。
2.1.1 指令靈活性
從指令使用角度來說,匯編指令比Intrinsics指令更靈活,主要體現(xiàn)在數(shù)據(jù)加載/存儲上,比如下例:
-
Intrinsics指令
-
加載數(shù)據(jù)到一個64位寄存器 vld1_s8/u8/s16/u16/s32…etc
-
加載數(shù)據(jù)到一個128位寄存器vld1q_s8/u8/s16/u16/s32…etc
-
-
ARM v7-A匯編
VLD1 { Dd}, [Rn]
VLD1 { Dd, D(d+1) }, [Rn]
VLD1 { Dd, D(d+1), D(d+2)}, [Rn]
VLD1 { Dd, D(d+1), D(d+2), D(d+3) }, [Rn]
-
ARM v8-A匯編
LD1 { .]
LD1 { ., .}, []
LD1 { ., ., . }, []
LD1 { ., ., ., . }, []
這個問題主要針對現(xiàn)在,相信隨著編譯器的升級這些問題會逐漸解決的。
在一些情況下,有的編譯器已經(jīng)能把兩條指令解析成一條匯編指令,比如:
因此,我們有理由由相信,隨著ARM v8-A編譯器的不斷升級,intrinsics指令會完善到跟匯編指令一樣靈活的。
2.1.2 寄存器分配
NEON匯編編程時,需要自己分配寄存器,用戶必須清楚寄存器的使用情況。而Intrinsics編程的一個好處就是,用戶只需要定義變量即可,編譯器會自動分配寄存器。這是優(yōu)點,但有時也會變成弱點。實踐證明,因為ARMv7-A只有16個128位NEON寄存器,在Intrinsics編程時,如果用戶同時使用過多的NEON寄存器,會導致gcc編譯器的寄存器分配問題。主要表現(xiàn)是編譯器會把很多數(shù)據(jù)存儲到堆棧中,這樣會極大的影響程序性能。因此用戶在使用Intrinsics編程時要注意這個問題。在性能出現(xiàn)異常時(比如C程序的性能比NEON程序的性能要好),檢查反匯編,看是否有寄存器分配的問題出現(xiàn)。在ARM64中,有32個128位NEON寄存器,這個問題的影響大大減弱。
2.2 性能與編譯器
在同一平臺下,NEON匯編的性能與編譯器無關,只由NEON的實現(xiàn)方式?jīng)Q定。好處是用戶在調(diào)整代碼時,用戶可以預測、控制自己程序的性能,但沒有驚喜。
NEON Intrinsics 的性能則極大的依賴于編譯器,不同的編譯器,性能可能有極大的差別。一般來說,越老版本的編譯器,性能越差。如果用戶需要保留對老版本編譯器兼容性時,需要慎重考慮使用Intrinsics。此外,當用戶優(yōu)化代碼細節(jié)的時候,編譯器的介入,使用戶很難預測程序性能的變化,但有時候會有驚喜,有時Intrinsics的性能會比匯編的性能要好。盡管很少見,但確實存在。
編譯器主要對優(yōu)化NEON程序造成影響。下圖是NEON實現(xiàn)及優(yōu)化的一般流程:
對于NEON匯編或是Intrinsics來講,實現(xiàn)流程是一樣的,編程——調(diào)試——測試。但是調(diào)優(yōu)的步驟是不一樣的。
NEON匯編的調(diào)優(yōu)方式主要有:
? 改變實現(xiàn)方式,比如改變所用指令,調(diào)整并行方式。
? 調(diào)整指令順序,以降低數(shù)據(jù)依賴性
? 上文第二章所介紹的方式都可以嘗試
在匯編調(diào)優(yōu)時,最精細方式是:
? 確定匯編指令數(shù)目和指令的時序
? 使用PMU (Performance Monitoring Unit)測量程序執(zhí)行的周期數(shù)
? 根據(jù)使用指令的時序,調(diào)整程序,盡量減少指令延時
這種方式的缺點是,針對指定微架構(gòu)的調(diào)整,換到另外的平臺性能不一定會好。經(jīng)?;ㄙM很大的工作量而只能取得很小的性能提升。
NEON intrinsics的調(diào)優(yōu)則比較困難,
? 嘗試NEON匯編所用的調(diào)優(yōu)方式,然后
? 觀察反匯編,看看數(shù)據(jù)依賴性、寄存器使用等情況
? 判斷優(yōu)化效果是否達到預期, 如果符合預期則工作結(jié)束。此時,需要測試多種編譯器,檢查性能的異同。
在使用intrinsics轉(zhuǎn)換ARMv7-A的匯編時,優(yōu)化效果判斷比較簡單,只要Intrinsics性能接近匯編性能即可。但是,在使用Intrinsics優(yōu)化ARM v8-A的代碼時,我們沒有性能參考的對象,較難判斷代碼是否調(diào)整到最優(yōu)狀態(tài)了??赡軙幸蓡枺瑫粫R編實現(xiàn)的性能會更好?但隨著整個ARM v8-A環(huán)境的成熟,這個問題帶來的影響會越來越小。另外,如果更看重Intrinsics的其它優(yōu)點,對性能也不是錙銖必較的話,這個問題的影響也不大。
2.3 跨平臺與可移植性
現(xiàn)在,現(xiàn)有的大部分NEON匯編代碼只能運行在ARM v7-A或是ARM v8-A AArch32模式的平臺上。想要運行在ARM v8-A AArch64模式的平臺,我們必須重寫代碼,這帶來了很大的工作量。這時,NEON Intrinsics代碼的好處就體現(xiàn)出來了,在ARM v8-A AArch64模式下,我們可以直接運行這些代碼,減少了重寫代碼的工作量。同時,我們可以只維護一套代碼,這樣也減少了維護的工作量。
然而,由于ARMv7-A/ARMv8-A的硬件資源不同,即使用Intrinsics,有時我們也需要兩套代碼。Ne10中FFT實現(xiàn)就是一個例子:
// radix 4 butterfly with twiddles
scratch[0].r = scratch_in[0].r;
scratch[0].i = scratch_in[0].i;
scratch[1].r = scratch_in[1].r * scratch_tw[0].r - scratch_in[1].i * scratch_tw[0].i;
scratch[1].i = scratch_in[1].i * scratch_tw[0].r + scratch_in[1].r * scratch_tw[0].i;
scratch[2].r = scratch_in[2].r * scratch_tw[1].r - scratch_in[2].i * scratch_tw[1].i;
scratch[2].i = scratch_in[2].i * scratch_tw[1].r + scratch_in[2].r * scratch_tw[1].i;
scratch[3].r = scratch_in[3].r * scratch_tw[2].r - scratch_in[3].i * scratch_tw[2].i;
scratch[3].i = scratch_in[3].i * scratch_tw[2].r + scratch_in[3].r * scratch_tw[2].i;
上述代碼描述了32位浮點復數(shù)FFT算法的基本元——基4蝶形運算。從代碼中我們可以看出:
? 如果在一次循環(huán)中,兩個基4蝶形運算并行,需要20個 64位寄存器。
? 如果在一次循環(huán)中,四個基4蝶形運算并行,需要20個 128位寄存器。
由于ARM v7-A只有16個128位寄存器,因此,該平臺的FFT實現(xiàn)僅能一次循環(huán)兩個基4蝶形運算并行。而ARM v8-A有32個128位寄存器,該平臺的FFT實現(xiàn)能一次循環(huán)四個基4蝶形運算并行。因此,即使用Intrinsics,我們也需要兩套代碼。
上例可以說明,在實現(xiàn)一套代碼跨ARM v7-A/v8-A平臺時,我們需要注意一些類似的特例。
2.4 將來
上面已經(jīng)分析了NEON匯編與Intrinsics的很多問題,但是這些問題都是暫時的。長遠來看,使用intrinsics還是更好。Intrinsics能帶來硬件以及編譯器發(fā)展的好處。經(jīng)典算法只要實現(xiàn)一次即可,不用隨著硬件的升級而重新編程,大大減少了工作量。
2.5 總結(jié)
結(jié)合實例,上文對NEON匯編和Intrinsics做了一些分析??傮w來說,使用intrinsics利大于弊。特別是與匯編相比,Intrinsics更容易編程,且能夠更好地兼容ARMv7-A/ARMv8-A。
下面再總結(jié)一下NEON Intrinsics使用時的一些注意事項:
? 使用的寄存器數(shù)量
? 編譯器選擇
? 查看反匯編
3.總結(jié)
本文通過實際程序分析了NEON匯編與Intrinsics的優(yōu)缺點。希望能對用戶在NEON實際開發(fā)中有些借鑒意義。
審核編輯 :李倩
-
寄存器
+關注
關注
31文章
5433瀏覽量
124405 -
編程
+關注
關注
88文章
3689瀏覽量
95222
原文標題:Arm NEON學習(三)NEON 匯編與Intrinsics編程
文章出處:【微信號:Ithingedu,微信公眾號:安芯教育科技】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
超級電容對比鋰電池的優(yōu)缺點

在IAR Embedded Workbench for Arm中使用Arm Cortex-R52 NEON

評論