ARM neon详解
NEON 學習參考文檔:
ARM NEON優化(一)——NEON簡介及基本架構 - Orchid Bloghttp://zyddora.github.io/2016/02/28/neon_1/
neon intrinsics函數
Intrinsics – Arm Developerhttps://developer.arm.com/architectures/instruction-sets/intrinsics/
三大主流芯片架構
1、ARM 2、MIPS 3、x86
編譯器自動向量化,往往發揮不了neon的最佳性能,這時候可能需要你借組內聯的Neon Intrinsics(arm_neon.h提供),甚至嵌套neon的匯編指令來進行優化
1、neon簡述
NEON是指適用于Arm Cortex-A系列處理器的一種高級SIMD(單指令多數據)擴展指令集。NEON 技術可加速多媒體和信號處理算法(如視頻編碼/解碼、2D/3D 圖形、游戲、音頻和語音處理、圖像處理技術、電話和聲音合成)。Single Instruction Multiple Data (SIMD)顧名思義就是“一條指令處理多個數據(一般是以2為底的指數數量)”的并行處理技術
NEON 指令可執行并行數據處理:
個人理解 neon就是通過對arm內部的一些特有寄存器做運算操作,以便能加速運算
2、Arm 高級SIMD發展歷史
3、?為什么要用NEON
(1)支持整數和浮點操作,以確保適合從編解碼器、高性能計算到 3D 圖形等廣泛應用領域。
(2)與 Arm處理器緊密結合,提供指令流和內存的統一視圖,編程比外部硬件加速器更簡單。
4、NEON指令格式
NEON寄存器:
- 16×128-bit寄存器(Q0-Q15);
- 或32×64-bit寄存器(D0-D31)
- 或上述寄存器的組合。
- ps每一個Q0-Q15寄存器映射到一對D寄存器
?
Q寄存器是虛的實際不在存在
數據處理指令類別:
(1)Long instructions
操作雙字vectors,生成四倍長字vectors 結果的寬度一般比操作數加倍,同類型 在指令中加L
(2) Wide instructions
操作雙字 + 四倍長字,生成四倍長字 結果和第一個操作數都是第二個操作數的兩倍寬度 在指令中加W
(3)Narrow instructions
操作四倍長字,生成雙字 結果寬度一般是操作數的一半 在指令中加N
數據類型表示:
(u)int8x8_t;
(u)int8x16_t;
(u)int16x4_t;
(u)int16x8_t;
(u)int32x2_t;
(u)int32x4_t;
(u)int64x1_t;
其中,第一個數字代表的是數據類型寬度為8/16/32/64位,第二個數字代表的是一個寄存器中該類型數據的數量。如int16x8_t代表16位有符號數,寄存器中共有8個數據 ,
為什么 數據類型為64 ,因為本來 寄存器大小 16個 128bit 寄存器, 映射處理后變為 32 * 64bit
參考示例
int16x8_t vqaddq_s16 (int16x8_t, int16x8_t)
int16x4_t vqadd_s16 (int16x4_t, int16x4_t)
第一個字母'v'指明是vector向量指令,也就是NEON指令;
第二個字母'q'指明是飽和指令,即后續的加法結果會自動飽和;
第三個字段'add'指明是加法指令;
第四個字段'q'指明操作寄存器寬度,為'q'時操作QWORD, 為128位;未指明時操作寄存器為DWORD,為64位;
第五個字段's16'指明操作的基本單元為有符號16位整數,其最大表示范圍為-32768 ~ 32767;
形參和返回值類型約定與C語言一致。
其它可能用到的助記符包括:
l 長指令,數據擴展
w 寬指令,數據對齊
n 窄指令, 數據壓縮
指令格式
V{<mod>}<op>{<shape>}{<cond>}{.<dt>}{<dest>}, src1, src2
<mod>
<op> 表示 操作運算符 如ADD, SUB, MUL
<shape> - Shape,即前文中的Long (L), Wide (W), Narrow (N).
<.dt> - Data type, such as s8, u8, f32 etc.
<dest> - Destination.
<src1> - Source operand 1.
<src2> - Source operand 2.
注: {} 表示可選的參數。
VADD.I16 D0, D1, D2 @ 16位加法 VMLAL.S16 Q2, D8, D9 @ 有符號16位乘加
5、 NEON編程基礎
現在我們可以開始使用NEON開始加速我們的應用了。使用NEON 技術通常有下列四種方式:
- 調用NEON優化過的庫函數
- 使用編譯器自動矢量化選項
- 使用NEON intrinsics指令
- 手寫NEON匯編
(1)調用庫函數
用戶只需要在程序中直接調用NEON優化過的庫函數就可以了,簡單易用。目前你有下列庫可以選擇:
Arm Compute library
一系列經過Arm CPU和GPU優化過的底層函數庫。用于圖像處理、機器學習和計算機視覺。更多信息: https://developer.Arm.com/technologies/compute-library
Ne10開源庫
由Arm主導開發的,目前提供了比較通用的數學函數,部分圖像處理函數,以及FFT函數。Project Ne10: An Open Optimized Software Library Project for the Arm Architecture @ GitHubhttp://projectne10.github.io/Ne10/
PS arm 平臺 系列處理器說明 其中 ARM CORTEX-A系列對應 ARMV7 參考如下
ARM平臺處理器簡介-ARMv7_WangMark的專欄-CSDN博客_armv7https://blog.csdn.net/petib_wangwei/article/details/40187207
(2)使用編譯器自動矢量化選項
如 gcc 編譯加上 參數 -mfpu=neon -mcpu
(3)使用NEON intrinsics指令
? ? ? ? ?見下面實際應用參考中描述
(4)?手寫NEON匯編
實際除非對匯編很了解,否則一般開發比較少用,實際intrinsics函數底層也是調用neon匯編的
6、neon 實際應用參考
(1)C語言__attribute__的使用
__attribute__?語法格式為:__attribute__ ((attribute-list))
attribute-list 參數為可以設置函數屬性(Function Attribute?)、變量屬性(Variable Attribute?)和類型屬性(Type Attribute?)
其位置約束為:放于聲明的尾部“ ;” 之前
關鍵字__attribute__ 也可以對結構體(struct )或共用體(union )進行屬性設置。大致有六個參數值可以被設定,即:aligned, packed, transparent_union, unused, deprecated 和 may_alias 。
在使用__attribute__ 參數時,你也可以在參數的前后都加上“__” (兩個下劃線),例如,使用__aligned__而不是aligned ,這樣,你就可以在相應的頭文件里使用它而不用關心頭文件里是否有重名的宏定義。
如調用neon作用函數 表示以下函數調用 arm_neon.h Intrinsics函數
__attribute__((target("fpu=neon")))
static void neon_accelator_vector_2_bias(short *input, float *output, float *bias)
{
/*
c code logic
for(int i=0;i<2;i++)
{
float temp = (float)(*(input+i));
*(output+i) = temp*(*bias);
}
*/
int16x4_t input_s16vec0 ;
memset(&input_s16vec0, 0, sizeof(input_s16vec0));
vld1_lane_s16(input,input_s16vec0,0);
input++;
vld1_lane_s16(input,input_s16vec0,1);
int32x4_t input_s32vec0 = vmovl_s16(input_s16vec0);
float32x4_t bias_f32vec = vld1q_f32 (bias);
float32x4_t input_f32vec0 = vcvtq_f32_s32(input_s32vec0);
float32x4_t output_f32vec0= vmulq_f32 (input_f32vec0, bias_f32vec);
vst1q_lane_f32(output,output_f32vec0,0);
output++;
vst1q_lane_f32(output,output_f32vec0,1);
}
(2)使用 arm_neon.h Intrinsics函數 需要加入-mfloat-abi=softfp -mfpu=neon指令
(3)指令函數使用
正常指令:生成大小同樣且類型通常與操作數向量同樣的結果向量;
Vector add(正常指令): vadd -> ri = ai + bi; r, a, b have equal lane sizes--*/
int8x8_t vadd_s8 (int8x8_t __a, int8x8_t __b);//_mm_add_epi8
int16x4_t vadd_s16 (int16x4_t __a, int16x4_t __b);//_mm_add_epi16
int32x2_t vadd_s32 (int32x2_t __a, int32x2_t __b);//_mm_add_epi32
int64x1_t vadd_s64 (int64x1_t __a, int64x1_t __b);//_mm_add_epi64
長指令:對雙字向量操作數運行運算,生成四字向量的結果。所生成的元素通常是操作數元素寬度的兩倍
Vector long add(長指令): vaddl -> ri = ai + bi; a, b have equal lane sizes,
result is a 128 bit vector of lanes that are twice the width--*/
int16x8_t vaddl_s8 (int8x8_t __a, int8x8_t __b);
int32x4_t vaddl_s16 (int16x4_t __a, int16x4_t __b);
int64x2_t vaddl_s32 (int32x2_t __a, int32x2_t __b);
uint16x8_t vaddl_u8 (uint8x8_t __a, uint8x8_t __b);
uint32x4_t vaddl_u16 (uint16x4_t __a, uint16x4_t __b);
uint64x2_t vaddl_u32 (uint32x2_t __a, uint32x2_t __b);
并屬于同一類型。
寬指令:一個雙字向量操作數和一個四字向量操作數運行運算,生成四字向量結果。
vaddw -> ri = ai + bi--*/
int16x8_t vaddw_s8 (int16x8_t __a, int8x8_t __b);
int32x4_t vaddw_s16 (int32x4_t __a, int16x4_t __b);
int64x2_t vaddw_s32 (int64x2_t __a, int32x2_t __b);
uint16x8_t vaddw_u8 (uint16x8_t __a, uint8x8_t __b);
uint32x4_t vaddw_u16 (uint32x4_t __a, uint16x4_t __b);
uint64x2_t vaddw_u32 (uint64x2_t __a, uint32x2_t __b);
窄指令:四字向量Vector rounding halving add: vrhadd -> ri = (ai + bi + 1) >> 1;
shifts each result right one bit, Results are rounded(四舍五入)--*/
int8x8_t vrhadd_s8 (int8x8_t __a, int8x8_t __b);
int16x4_t vrhadd_s16 (int16x4_t __a, int16x4_t __b);
int32x2_t vrhadd_s32 (int32x2_t __a, int32x2_t __b);操作數運行運算,并生成雙字向量結果,所生成的元素通常是操作數元素寬度的一半。
飽和指令:當超過數據類型指定的范圍則自己主動限制在該范圍內。*/
vqadd -> ri = sat(ai + bi);
the results are saturated if they overflow--*/
int8x8_t vqadd_s8 (int8x8_t __a, int8x8_t __b);//_mm_adds_epi8
int16x4_t vqadd_s16 (int16x4_t __a, int16x4_t __b);//_mm_adds_epi16
int32x2_t vqadd_s32 (int32x2_t __a, int32x2_t __b);
int64x1_t vqadd_s64 (int64x1_t __a, int64x1_t __b)
vrhadd -> ri = (ai + bi + 1) >> 1;
vmul -> ri = ai * bi
vmla -> ri = ai + bi * ci
vmlsl -> ri = ai - bi * ci
總結
以上是生活随笔為你收集整理的ARM neon详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员不得不学的操作系统知识(二)
- 下一篇: 使用 Acrobat 将 PDF 转换为