日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

neon浮点运算_ARM NEON指令集优化理论与实践

發布時間:2023/12/8 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 neon浮点运算_ARM NEON指令集优化理论与实践 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

ARM NEON指令集優化理論與實踐

一.簡介

NEON就是一種基于SIMD思想的ARM技術,相比于ARMv6或之前的架構,NEON結合了64-bit和128-bit的SIMD指令集,提供128-bit寬的向量運算(vector operations)。NEON技術從ARMv7開始被采用,目前可以在ARM Cortex-A和Cortex-R系列處理器中采用。NEON在Cortex-A7、Cortex-A12、Cortex-A15處理器中被設置為默認選項,但是在其余的ARMv7 Cortex-A系列中是可選項。NEON與VFP共享了同樣的寄存器,但它具有自己獨立的執行流水線。

二. NEON寄存器

三. NEON指令集

所有的支持NEON指令都有一個助記符V,下面以32位指令為例,說明指令的一般格式:

V{}{}{}{.}{}, src1, src2Q: The instruction uses saturating arithmetic, so that the result is saturated within the range of the specified data type, such as VQABS, VQSHL etc.

H: The instruction will halve the result. It does this by shifting right by one place (effectively a divide by two with truncation), such as VHADD, VHSUB.

D: The instruction doubles the result, such as VQDMULL, VQDMLAL, VQDMLSL and VQ{R}DMULH.

R: The instruction will perform rounding on the result, equivalent to adding 0.5 to the result before truncating, such as VRHADD, VRSHR.

- the operation (for example, ADD, SUB, MUL).

- Shape,即前文中的Long (L), Wide (W), Narrow (N).

- Condition, used with IT instruction.

<.dt> - Data type, such as s8, u8, f32 etc.

- Destination.

- Source operand 1.

- Source operand 2.

注: {} 表示可選的參數。

比如:

VADD.I16 D0, D1, D2 @ 16位加法

VMLAL.S16 Q2, D8, D9 @ 有符號16位乘加

四.NEON支持的指令總結運算:和、差、積、商

共享的 NEON 和 VFP 指令:涉及加載、多寄存器間的傳送、存儲

五. NEON 優化技術

在利用NEON優化程序時,有下述幾項比較通用的優化技巧。

1. 降低數據依賴性

在ARM v7-A NEON指令通常需要3~9個指令周期,NEON指令比ARM指令需要更多周期數。因此,為了減少指令延時,最好避免將當前指令的目的寄存器當作下條指令的源寄存器。如下例所示:

/***************************************************************/

// C代碼

float SumSquareError_C(const float* src_a, const float* src_b, int count)

{

float sse = 0u;

int i;

for (i = 0; i < count; ++i) {

float diff = src_a[i] - src_b[i];

sse += (float)(diff * diff);

}

return sse;

}

// NEON實現一

float SumSquareError_NEON1(const float* src_a, const float* src_b, int count)

{

float sse;

asm volatile (

"veor q8, q8, q8 \n"

"veor q9, q9, q9 \n"

"veor q10, q10, q10 \n"

"veor q11, q11, q11 \n"

"1: \n"

"vld1.32 {q0, q1}, [%0]! \n"

"vld1.32 {q2, q3}, [%0]! \n"

"vld1.32 {q12, q13}, [%1]! \n"

"vld1.32 {q14, q15}, [%1]! \n"

"subs %2, %2, #16 \n"

// q0, q1, q2, q3 是vsub的目的地寄存器.

// 也是vmla的源寄存器。

"vsub.f32 q0, q0, q12 \n"

"vmla.f32 q8, q0, q0 \n"

"vsub.f32 q1, q1, q13 \n"

"vmla.f32 q9, q1, q1 \n"

"vsub.f32 q2, q2, q14 \n"

"vmla.f32 q10, q2, q2 \n"

"vsub.f32 q3, q3, q15 \n"

"vmla.f32 q11, q3, q3 \n"

"bgt 1b \n"

"vadd.f32 q8, q8, q9 \n"

"vadd.f32 q10, q10, q11 \n"

"vadd.f32 q11, q8, q10 \n"

"vpadd.f32 d2, d22, d23 \n"

"vpadd.f32 d0, d2, d2 \n"

"vmov.32 %3, d0[0] \n"

: "+r"(src_a),

"+r"(src_b),

"+r"(count),

"=r"(sse)

:

: "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11","q12", "q13","q14", "q15");

return sse;

}

// NEON實現二

float SumSquareError_NEON2(const float* src_a, const float* src_b, int count)

{

float sse;

asm volatile (

"veor q8, q8, q8 \n"

"veor q9, q9, q9 \n"

"veor q10, q10, q10 \n"

"veor q11, q11, q11 \n"

"1: \n"

"vld1.32 {q0, q1}, [%0]! \n"

"vld1.32 {q2, q3}, [%0]! \n"

"vld1.32 {q12, q13}, [%1]! \n"

"vld1.32 {q14, q15}, [%1]! \n"

"subs %2, %2, #16 \n"

"vsub.f32 q0, q0, q12 \n"

"vsub.f32 q1, q1, q13 \n"

"vsub.f32 q2, q2, q14 \n"

"vsub.f32 q3, q3, q15 \n"

"vmla.f32 q8, q0, q0 \n"

"vmla.f32 q9, q1, q1 \n"

"vmla.f32 q10, q2, q2 \n"

"vmla.f32 q11, q3, q3 \n"

"bgt 1b \n"

"vadd.f32 q8, q8, q9 \n"

"vadd.f32 q10, q10, q11 \n"

"vadd.f32 q11, q8, q10 \n"

"vpadd.f32 d2, d22, d23 \n"

"vpadd.f32 d0, d2, d2 \n"

"vmov.32 %3, d0[0] \n"

: "+r"(src_a),

"+r"(src_b),

"+r"(count),

"=r"(sse)

:

: "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13","q14", "q15");

return sse;

}

/***************************************************************/

在NEON實現一中,我們把目的寄存器立刻當作源寄存器;在NEON實現二中,我們重新排布了指令,并給予目的寄存器盡量多的延時。經過測試實現二比實現一快30%。由此可見,降低數據依賴性對于提高程序性能有重要意義。一個好消息是編譯器能自動調整NEON intrinsics以降低數據依賴性。這個利用NEON intrinsics的一個很大優勢。

2. 減少跳轉

NEON指令集沒有跳轉指令,當需要跳轉時,我們需要借助ARM指令。在ARM處理器中,分支預測技術被廣泛使用。但是一旦分支預測失敗,懲罰還是比較高的。因此我們最好盡量減少跳轉指令的使用。其實,在有些情況下,我們可以用邏輯運算來代替跳轉,如下例所示:

ARM NEON指令集提供了下列指令來幫助用戶實現上述邏輯實現:

/***************************************************************/

// C實現

if( flag )

{

dst[x * 4] = a;

dst[x * 4 + 1] = a;

dst[x * 4 + 2] = a;

dst[x * 4 + 3] = a;

}

else

{

dst[x * 4] = b;

dst[x * 4 + 1] = b;

dst[x * 4 + 2] = b;

dst[x * 4 + 3] = b;

}

// NEON實現

//dst[x * 4] = (a&Eflag) | (b&~Eflag);

//dst[x * 4 + 1] = (a&Eflag) | (b&~Eflag);

//dst[x * 4 + 2] = (a&Eflag) | (b&~Eflag);

//dst[x * 4 + 3] = (a&Eflag) | (b&~Eflag);

VBSL qFlag, qA, qB

/***************************************************************/

? VCEQ, VCGE, VCGT, VCLE, VCLT……

? VBIT, VBIF, VBSL……

減少跳轉,不僅僅是在NEON中使用的技巧,是一個比較通用的問題。即使在C程序中,這個問題也是值得注意的。

3. 其它技巧

在ARM NEON編程時,一種功能有時有多種實現方式,但是更少的指令不總是意味著更好的性能,要依據測試結果和profiling數據,具體問題具體分析。下面列出來我遇到的一些特殊情況。

4. 浮點累加指令

通常情況下,我們會用VMLA/VMLS來代替VMUL + VADD/ VMUL + VSUB,這樣使用較少的指令,完成更多的功能。但是與浮點VMUL相比,浮點VMLA/VMLS具有更長的指令延時,如果在指令延時中間不能插入其它計算的情況下,使用浮點VMUL + VADD/ VMUL + VSUB反而具有更好的性能。

一個真實例子就是Ne10庫函數的浮點FIR函數。代碼片段如下所示:

實現1:在兩條VMLA指令之間,僅有VEXT指令。而根據指令延時表,VMLA需要9個周期。

實現2:對于qAcc0,依然存在指令延時。但是VADD/VMUL只需要5個周期。

總結

以上是生活随笔為你收集整理的neon浮点运算_ARM NEON指令集优化理论与实践的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。