【genius_platform软件平台开发】第二十八讲:NEON指令集优化(附实例)
- 當在ARM芯片上進行一些例如圖像處理等計算的時候,常常會因為計算量太大造成計算幀率較低的情況。因而,需要選擇一種更加簡單快捷的計算方式以獲得處理速度上的提升。ARM NEON就是一個不錯的選擇.
※ Neon指令優(yōu)化
-
NEON是一種SIMD(Single Instruction Multiple Data)*指令,也就是說,NEON可以把若干源(source)操作數(shù)(operand)打包放到一個源寄存器中,對他們執(zhí)行相同的操作,產(chǎn)生若干目的(dest)操作數(shù),這種方式也叫向量化(vectorization)。
-
可能你對這個描述還不夠清晰,簡單來說,就是:NEON指令優(yōu)化的精髓就在于同時在不同通道內(nèi)進行并行運算。通常可用于圖像等矩陣數(shù)據(jù)的循環(huán)優(yōu)化。
-
更簡單的說,就是,將Neon寄存器分為多個通道,每個通道存儲一個數(shù)據(jù)。一條對Neon寄存器的計算指令,實際上,是對各通道的數(shù)據(jù)分別的計算指令。即寄存器位寬,直接影響到數(shù)據(jù)的通道數(shù)。
-
例如:在ARMv7的NEON unit中,register file總大小是1024-bit,可以劃分為16個128-bit的Q-register(Quadword register)或者32個64-bit的D-register(Dualword register),也就是說,最長的寄存器位寬是128-bit。那么,假設我們采用32-bit單精度浮點數(shù)float來做浮點運算,那么可以把最多128/32=4個浮點數(shù)打包放到Q-register中做運算,即4個4個參與計算,從而提高吞吐量,減少loop次數(shù)。
-
Neon指令的使用
主流支持目標平臺為ARM CPU的編譯器基本都支持NEON指令。可以通過在代碼中嵌入NEON匯編來使用NEON,但是更加常見的方式是通過類似C函數(shù)的NEON Instrinsic來編寫NEON代碼。本文統(tǒng)一采用后者。
※ 硬件平臺
- 本文的例子都是基于ARMV7架構平臺。ARMV7架構包含:
16個通用寄存器(32bit),R0-R15
16個NEON寄存器(128bit),Q0-Q15(同時也可以被視為32個64bit的寄存器,D0-D31)
16個VFP寄存器(32bit),S0-S15
其中:NEON和VFP的區(qū)別在于VFP是加速浮點計算的硬件不具備數(shù)據(jù)并行能力,同時VFP更盡興雙精度浮點數(shù)(double)的計算,NEON只有單精度浮點計算能力。
● 頭文件和編譯選項
- 在使用NEON Instrinsic來進行編寫NEON代碼前,需要引入頭文件:
- 同時,在編譯的時候,需要指定編譯參數(shù)。如果使用CMakeLists.txt,可以指定:
- 關于編譯選項,可以參考:ARM平臺NEON指令的編譯和優(yōu)化
※ NEON Instrinsic詳細解釋
● 數(shù)據(jù)類型
-
對于數(shù)據(jù)類型的命名,一般遵循這樣的規(guī)則:
<基本類型>x<lane個數(shù)>x<向量個數(shù)>_t
其中,向量個數(shù)如果省略表示只有一個。
基本類型:int8,int16,int32,int64,uint8,uint16,uint32,uint64,float16,float32 -
lane個數(shù)表示并行處理的基本類型數(shù)據(jù)的個數(shù)。
按照上述的規(guī)則,比如:
float32x4_t
● 指令函數(shù)
-
對于指令函數(shù)的命名,一般遵循這樣的規(guī)則:
v<指令名>[后綴]_<數(shù)據(jù)基本類型簡寫>
-
其中,后綴如果沒有,表示64位并行;如果后綴是q,表示128位并行;如果后綴是l,表示長指令,輸出數(shù)據(jù)的基本類型位數(shù)是輸入的2倍;如果后綴是n,表示窄指令,輸出數(shù)據(jù)的基本類型位數(shù)是輸入的一半。
數(shù)據(jù)基本類型簡寫:s8,s16,s32,s64,u8,u16,u32,u64,f16,f32。按照上述的規(guī)則,比如:
● 指令名
-
Neon的指令名主要分為:算術和位運算指令、數(shù)據(jù)移動指令、訪存指令。
算術和位運算指令:包括add(加法),sub(減法),mul(乘法)這些基本指令。 -
實際編程中經(jīng)常要在不同NEON數(shù)據(jù)類型間轉(zhuǎn)移數(shù)據(jù),有時還要按lane來get/set向量值,NEON intrinsics也提供了這類操作。
dup[后綴]n<數(shù)據(jù)基本類型簡寫>:用同一個標量值初始化一個向量全部的lane;
set[后綴]lane<數(shù)據(jù)基本類型簡寫>:對指定的一個lane進行設置
get[后綴]lane<數(shù)據(jù)基本類型簡寫>:獲取指定的一個lane的值
mov[后綴]_<數(shù)據(jù)基本類型簡寫>:數(shù)據(jù)間移動
-
NEON訪存指令可以將內(nèi)存讀到NEON數(shù)據(jù)類型中去,或者將NEON數(shù)據(jù)類型寫進內(nèi)存。可以支持一次讀寫多向量數(shù)據(jù)類型。
ld<向量數(shù)>[后綴]<數(shù)據(jù)基本類型簡寫>:讀內(nèi)存
st<向量數(shù)>[后綴]<數(shù)據(jù)基本類型簡寫>:寫內(nèi)存
實例
- 實例內(nèi)容:對于1280 * 720 * 3的圖片數(shù)據(jù),需要對每個像素點進行同樣的加法和乘法運算,比較非Neon和Neon兩種方式的耗時。源碼:
- 編寫CMakeLists.txt,用于項目編譯:
- 在同級目錄下編寫main.sh,進行項目編譯:
- 將生成的可執(zhí)行文件main,push到設備端進行運行,最終的運行結果:
- 可以看出,使用Neon指令集優(yōu)化,省下了近60.71%的運行時間。
總結
以上是生活随笔為你收集整理的【genius_platform软件平台开发】第二十八讲:NEON指令集优化(附实例)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021-06-09
- 下一篇: ARM SIMD NEON 简介 (翻译