Neon 基础入门
Neon 基礎入門
1 ARM 基礎知識
1.1 ARM 架構
? ? ? ?ARM是微處理器行業的一家知名企業,其芯片架構和對應處理器如表1所示。
| 表1 ARM 架構及對應處理器 | |
| 架構 | 處理器家族 |
| ARMv1 | ARM1 |
| ARMv2 | ARM2、ARM3 |
| ARMv3 | ARM6、ARM7 |
| ARMv4 | StrongARM、ARM7TDMI、ARM9TDMI |
| ARMv5 | ARM7EJ、ARM9E、ARM10E、XScale |
| ARMv6 | ARM11、ARM Cortex-M |
| ARMv7 | ARM Cortex-A、ARM Cortex-M、ARM Cortex-R |
| ARMv8 | Cortex-A35、Cortex-A50系列、Cortex-A72、Cortex-A73 |
1.1.1 ARM 寄存器
? ? ? ARM處理器共有37個寄存器,被分為若干個組(BANK),這些寄存器包括:
- 31個通用寄存器,包括程序計數器(PC指針),均為32位的寄存器。
- 6個狀態寄存器,用以標識CPU的工作狀態及程序的運行狀態,均為32位,只使用了其中的一部分。
ARMV7架構包含:
- 16個通用寄存器(32bit),R0-R15
- 16個NEON寄存器(128bit),Q0-Q15(同時也可以被視為32個64bit的寄存器,D0-D31)
- 16個VFP寄存器(32bit),S0-S15
NEON和VFP的區別在于VFP是加速浮點計算的硬件不具備數據并行能力,同時VFP更盡興雙精度浮點數(double)的計算,NEON只有單精度浮點計算能力。
1.1.2 ARM指令結構
? ? ? ? ARM微處理器在較新的體系結構中支持兩種指令集:ARM指令集和Thumb指令集。其中,ARM指令為32位的長度,Thumb指令為16位長度。Thumb指令集為ARM指令集的功能子集,但與等價的ARM代碼相比較,可節省30%~40%以上的存儲空間,同時具備32位代碼的所有優點。
1.1.3?SIMD和SISD
? ? ? ?1966年,MichealFlynn根據指令和數據流的概念對計算機的體系結構進行了分類,這就是所謂的Flynn分類法。Flynn將計算機劃分為四種基本類型,即SISD、MIMD、SIMD、MISD。
SISD(Single Instruction?Single Data)
? ? ? ?
? ? ? ?SISD機器是一種傳統的串行計算機,它的硬件不支持任何形式的并行計算,所有的指令都是串行執行。并且在某個時鐘周期內,CPU只能處理一個數據流。因此這種機器被稱作單指令流單數據流機器。早期的計算? 機都是SISD機器,如馮諾.依曼架構,如IBM PC機,早期的巨型機和許多8位的家用機等。?
SIMD(Single Instruction Multiple Data)
? ? ? ?
? ? ? ? ?SIMD是采用一個指令流處理多個數據流。這類機器在數字信號處理、圖像處理、以及多媒體信息處理等領域非常有效。?
MISD(Multiple??Instruction?Single?Data)
? ? ? ?
? ? ? ? ? MISD是多指令流單數據流,采用多個指令流來處理單個數據流。由于實際情況中,采用多指令流處理多數據流才是更有效的方法,因此MISD只是作為理論模型出現,沒有投入到實際應用之中。
MIMD(Multiple Instruction Multiple Data)
? ? ? ?
? ? ? ?MIMD多指令流多數據流,機器可以同時執行多個指令流,這些指令流分別對不同數據流進行操作。最新的多核計算平臺就屬于MIMD的范疇,例如Intel和AMD的雙核處理器等都屬于MIMD。
? ? ? ?單指令單數據(SISD)的CPU對加法指令譯碼后,執行部件先訪問內存,取得第一個操作數;之后再一次訪問內存,取得第二個操作數;隨后才能進行求和運算。而在SIMD型的CPU中,指令譯碼后幾個執行部件同時訪問內存,一次性獲得所有操作數進行運算。這個特點使SIMD特別適合于多媒體應用等數據密集型運算。如下圖所示:
2 NEON 簡介
? ? ??NEON就是一種基于SIMD思想的ARM技術,結合了64-bit和128-bit的SIMD指令集,提供128-bit寬的向量運算(vector operations)。NEON技術從ARMv7開始被采用,目前在Cortex-A7、Cortex-A12、Cortex-A15處理器中被設置為默認選項,但是在其余的ARMv7 Cortex-A系列中NEON是可選項。NEON與VFP共享了同樣的寄存器,但它具有自己獨立的執行流水線。
2.1 NEON 架構(數據類型/寄存器/指令集)
2.1.1 NEON 寄存器
? ? ? NEON有自己的執行管道和寄存器組,neon寄存器組包含32個64位的寄存器和16個128位的寄存器,它們分別被標識為(D0-D31),(Q0-Q15)。 實際上D寄存器和Q寄存器是重疊復用的,如下圖所示。
? ? ? NEON 寄存器有幾種形式:
- 或32×64-bit寄存器(D0-D31)
- 16×128-bit寄存器(Q0-Q15);
- 或上述寄存器的組合。
每一個Q0-Q15寄存器映射到一對D寄存器,寄存器之間的映射關系:
- D<2n> 映射到 Q?的最低有效半部;
- D<2n+1> 映射到 Q?的最高有效半部;
結合NEON支持的數據類型,NEON寄存器有如下圖的幾種形態:
NEON 關鍵概念:向量,向量線(管道 lane)
- 向量(vector)
? ? ?在neon中一個寄存器可以看作是一個向量。比如一個64bit的寄存器D0,當這個寄存器存放4個int16類型數據時,可以把它看作是一個向量,它包含了4個類型為int16的元素。
- 管道(lane)---元素
? ?? ?當一個D寄存器(向量)D0存放4個int16數據時,則此時D0有4個管道,管道0到管道3。管道0位于寄存器的低bit位。這個管道實際就對應了向量的元素概念。
NEON 數據裝載順序?
? ? ? ?假設一個uint16類型的數組為{0x0201,0x0403,0x0605,0x0807},則它在內存中低地址到高地址存放的順序為0x01,0x02,0x03````0x08(小端模式)。內存中的數據裝入neon寄存器時,是低地址內存數據放入neon寄存器的低地址上,高地址內存數據放入neon寄存器的高地址上。當neon寄存器數據裝入內存中時,同樣是neon寄存器的低地址數據放入的內存低地址上,neon寄存器的高地址數據放入的內存高地址上。所以內存中的數據經過neon處理后,數據的順序是不會發生變化的。
2.1.2 NEON 數據類型
NEON支持的數據類型:
注意:數據類型只針對操作數,而不是目標數,這點在寫的時候要特別注意,很容易搞錯,尤其是對那些長指令寬指令的時候,因為經常Q和D一起操作。
2.1.3 NEON 指令集
NEON指令按照操作數類型可以分為:
- 正常指令(q):生成大小相同且類型通常與操作數向量相同到結果向量。
- 長指令(l):對雙字向量操作數執行運算,生產四字向量到結果。所生成的元 素一般是操作數元素寬度到兩倍,并屬于同一類型。L標記,如VMOVL。
- 寬指令(w):一個雙字向量操作數和一個四字向量操作數執行運算,生成四字向量結果。W標記,如VADDW。
- 窄指令(n):四字向量操作數執行運算,并生成雙字向量結果,所生成的元素一般是操作數元素寬度的一半。N標記,如VMOVN。
- 飽和指令(vq):當超過數據類型指定到范圍則自動限制在該范圍內。Q標記,如VQSHRUN
下面給出幾幅圖解釋上述指令的操作原理?
長指令:
寬指令:
窄指令:
NEON 編程可以用內聯函數(intrinsics) 和 匯編兩種。
內聯函數(intrinsics)?
? ? ? ?內聯函數(intrinsics)將NEON指令(匯編)封裝成內置函數。使用NEON intrinsics時,雖然像是在調用各種結構體和函數,但將生成的代碼反匯編后可以發現,其實沒有調用函數,只是在使用NEON寄存器和指令。
內聯函數數據類型
? ? ? ?NEON 向量數據類型格式:?<類型><大小>x<向量線條數>_t
例如:int16x4_t ,表示一個包含四條管道的向量,每條向量線包含一個有符號16位整數。
NEON 根據64寄存器(D)和128位寄存器(Q)分別有兩種數據類型。
- 64bit數據類型,映射至寄存器即為D0-D31
? ? ?int8x8_t?
? ? ?int16x4_t?
? ? ?int32x2_t?
? ? ?int64x1_t?
? ? ?uint8x8_t?
? ? ?uint16x4_t?
? ? ?uint32x2_t?
? ? ?uint64x1_t?
? ? float32x2_t?
? ? poly8x8_t?
? ? poly16x4_t
- 128bit數據類型,映射至寄存器即為Q0-Q15
? ? ?int8x16_t?
? ? ?int16x8_t?
? ? ?int32x4_t?
? ? ?int64x2_t?
? ? ?uint8x16_t?
? ? ?uint16x8_t?
? ? ?uint32x4_t?
? ? ?uint64x2_t?
? ? ?float32x4_t?
? ? ?poly8x16_t?
? ? ?poly16x8_t?
? ? ? ?有些內聯函數使用以下格式的向量類型數組:?<基本類型>x<lane個數>x<向量個數>_t?
? ? ? ?"向量個數"表示向量數組,如果省略表示只有一個向量(寄存器),如int8x8_t。uint16x8x2_t:uint16表示向量中的數據類型, x8表示向量中的元素個數,x2表示 uint16x8_t這樣的向量類型有兩個,這是個向量數組。以下是一個結構定義示例:
NEON 內聯函數數據結構?展開源碼
內聯函數命名
? ? ? ?內聯函數命令格式:<指令名>[后綴]_<數據基本類型簡寫>
? ? ? ?指令名如果沒有后綴,表示64位并行;如果后綴是q,表示128位并行。如果后綴是l,表示長指令,輸出數據的基本類型位數是輸入的2倍;如果后綴是n,表示窄指令,輸出數據的基本類型位數是輸入的一半。數據基本類型簡寫:s8,s16,s32,s64,u8,u16,u32,u64,f16,f32
例如:
vadd_u16: 兩個uint16x4相加為一個uint16x4?
vaddq_u16:兩個uint16x8相加為一個uint16x8?
vaddl_u16: 兩個uint8x8相加為一個uint16x8
? ? ?處理數組時要注意數組元素個數不能被NEON向量lane個數整除的情況,多出的元素應補齊或者通過非SIMD方式處理。
posted on 2018-08-29 16:58 sundaygeek 閱讀(...) 評論(...) 編輯 收藏
總結
- 上一篇: 128-Vue中的事件修饰符-阻止冒泡事
- 下一篇: 使用 content-visibilit