1. 程式人生 > 其它 >嵌入式程式優化(1)-內嵌neon彙編

嵌入式程式優化(1)-內嵌neon彙編

1. neon介紹

neon指令集arm 平臺的 SIMD 指令集,也即單指令多資料指令集,如名字所說,一條只能可以同時處理多個數據,這裡常常也使用另外一個名詞來說 向量化程式設計。向量化程式設計在音視訊處理領域中極為常見,隨著人工智慧深度學習等技術在嵌入式平臺上的應用,neon指令集也可以被使用來優化某些後處理函式。本文將通過說明加程式碼例子講解的方式儘量來闡述筆者的理解

PS:本文預設讀者們已經熟悉了arm內嵌彙編語法

2. neon指令基礎

本節將著重講解一下 neon指令集 的基礎知識,從而讓讀者更好地理解如何使用 neon指令

2.1 指令暫存器組

neon指令集 有專用的暫存器組,第一種是Q0-Q15

一共16個,每個暫存器長度為128bit,第二種是D0-D31,一共32個,每個暫存器為64bit

neon暫存器有對照關係,可以理解為他們共同使用一塊儲存資料的區域,但該區域被分別對映為D暫存器和Q暫存器,下面的圖是暫存器之間的對應關係

  暫存器對應關係.png

neon指令集將每個暫存器視為均包含一個向量, 而該向量又包含 1、2、 4、 8 或 16 個大小和型別均相同的元素,也可以將各元素當作標量 加以訪問。比如D0中有8個元素,每個元素是8bit,那麼D0暫存器看成是有8個元素的向量

2.2 指令分類

neon指令 的操作物件為向量,向量有長有短,neon指令常用的有 64bit 的雙字向量和 128bit 的四字向量其中,根據運算元的長度,可以將 neon指令 分為以下幾種型別

  • 通用指令集(Normal instructions):普通指令可以對任何向量型別進行操作,併產生與運算元向量相同大小且通常與型別相同的結果向量。即向量與結果長度相同

  • 長整數指令集(Long instructions):長指令對 雙字向量運算元 進行運算併產生 四字向量 結果。結果元素通常是運算元寬度的兩倍,並且型別相同。即輸入向量的長度為輸出向量的一半(長指令使用附加在指令後的L來指定)。

  • 寬整數指令集(Wide instructions):寬指令對 雙字向量運算元四字向量運算元 進行操作,產生 四字向量 結果 。結果元素和第一個運算元(四字向量運算元)的寬度是第二個運算元元素(雙字向量運算元)的寬度的兩倍。 即雙字向量和四字向量計算後輸出四字向量。寬指令在指令後附加了W。

  • 窄整數指令集(Narrow instructions):窄指令對 四字向量運算元 進行運算,併產生 雙字向量 結果。結果元素通常是運算元元素寬度的一半。即四字向量和四字向量計算後輸出雙字向量。窄指令通過在指令後附加N來指定。

2.3 指令格式

V{<mod>}<op>{<shape>}{<cond>}{.<dt>}{<dest>}, src1, src2

  • mod:即模式,neon指令集有幾種計算模式,如下 Q: 飽和計算,各個資料型別的飽和範圍請查看錶. H: 該指令將結果減半。 它實際上通過向右移一個位來完成此操作,例如VHADD,VHSUB。 D: 使用指令使結果加倍 R: 對截斷的指令結果進行舍入,比如某一個指令的運算元為整形,但指令結果帶有小數,則需要對小數 進行舍入

  • op:執行的操作,比如加法ADD, 減法SUB, 乘法MUL).

  • shape:用於指定長指令(L), 寬指令(W), 窄指令(N),指的就是2.2節中的指令分類

  • cond:條件碼, neon條件碼與普通的arm彙編條件碼使用的是同一套條件碼,但其含義不同,詳情檢視手冊

  • datatype:操作的資料型別,比如U8,U16等

  • dest:目的暫存器

  • src1:源暫存器1

  • src2:源暫存器2

PS:帶花括號的欄位為可選欄位

2.4 指令編譯

在編譯含有neon指令集的程式碼時,是指定使用的fpu和cpu,通常情況下我們使用arm-gcc需要加-ftree-vectorize-mfpu=neon-mcpu=your_chip_arch 來使能編譯器使用neon指令集

neon指令集常常是在C語言中使用,除了使用匯編程式碼來編寫外,我們還可以使用neon的開源庫如Ne10等,也可以使用官方提供的neon內建函式

同時,gcc編譯器 支援對一般的程式碼進行 neon優化,但需要滿足以下條件

• 短而簡單的迴圈

• 不使用break跳出迴圈.

• 迴圈次數為2的冪次.

• 迴圈次數為編譯器支援的範圍.

• 迴圈內部除錯函式時,該函式需要是內聯屬性

• 使用陣列索引而不是指標.

• 間接定址無法向量化.

• 使用strict關鍵字告訴編譯器指標不引用記憶體的重疊區域。

3. 例子說明

下面通過一個簡單的 neon指令 的語句來了解一下具體的語法

  指令示意.png

上面的圖例應該展現得足夠清楚

qd 是mod欄位,表明該指令支援飽和和對結果進行雙倍放大操作

mla是neon指令集操作,標誌相乘並累加結果

l 表示的是指令的操作型別為長整型操作

s16 表示的是向量中的元素長度

dest 表示的是目標暫存器

src 表示的是源暫存器

關於 neon 的具體例子請參加筆者的github,地址為https://github.com/wipping/neon

因為該程式碼例程中有上千行,不便在文章中呈現,請讀者們下載閱讀,程式碼中做了相應的註釋,當然還是要搭配neon指導手冊 進行閱讀。因為程式碼是根據手冊中的內容進行編寫

4. 參考資料

NEON簡介及基本架構:http://zyddora.github.io/2016/02/28/neon_1/
ARM平臺NEON指令的編譯和優化https://blog.csdn.net/heli200482128/article/details/79303286
neon函式速查地址: https://developer.arm.com/architectures/instruction-sets/simd-isas/neon/intrinsics



作者:wipping的技術小棧
連結:https://www.jianshu.com/p/a57a348f35f5
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。