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