1. 程式人生 > >ARM NEON常用函式總結

ARM NEON常用函式總結

原文地址:https://blog.csdn.net/may0324/article/details/72847800 NEON 技術是 ARM Cortex™-A 系列處理器的 128 位 SIMD(單指令,多資料)架構擴充套件,旨在為消費性多媒體應用程式提供靈活、強大的加速功能,從而顯著改善使用者體驗。它具有 32 個暫存器,64 位寬(雙倍檢視為 16 個暫存器,128 位寬。) 目前主流的iPhone手機和大部分android手機都支援ARM NEON加速,因此在編寫移動端演算法時,可利用NEON技術進行演算法加速,以長度為4的暫存器大小為例,相應的提速倍數約是原始的4倍。

NEON 指令可執行“打包的 SIMD”處理:

暫存器被視為同一資料型別的元素的向量 
資料型別可為:簽名/未簽名的 8 位、16 位、32 位、64 位單精度浮點 
指令在所有通道中執行同一操作

如下圖所示: 在這裡插入圖片描述 本文主要介紹float32x4_t相關的結構及函式, float32x4_t 可以理解為vector (4),同理typexN_t即為vector(N)。

在NEON程式設計中,對單個數據的操作可以擴充套件為對暫存器,也即同一型別元素向量的操作,因此大大減少了操作次數。 這裡以一個小例子來解釋如何利用NEON內建函式來加速實現統計一個數組內的元素之和。

以C++程式碼為例: 原始演算法程式碼如下:

#include <iostream> 
using namespace std; 
float sum_array(float *arr, int len) 
{ 
    if(NULL == arr || len < 1) 
    { 
        cout<<"input error\n"; 
        return 0; 
    } 
    float sum(0.0); 
    for(int i=0; i<len; ++i) {
        sum += *arr++; 
    } 
    return sum; 
}   

對於長度為N的陣列,上述演算法的時間複雜度時O(N)。 採用NEON函式進行加速:

#include <iostream> 
#include <arm_neon.h> //需包含的標頭檔案 
using namespace std; 
float sum_array(float *arr, int len) 
{ 
    if(NULL == arr || len < 1) { 
        cout<<"input error\n"; 
        return 0; 
    } 
    int dim4 = len >> 2; // 陣列長度除4整數 
    int left4 = len & 3; // 陣列長度除4餘數 
    float32x4_t sum_vec = vdupq_n_f32(0.0);//定義用於暫存累加結果的暫存器且初始化為0 
    for (; dim4>0; dim4--, arr+=4) //每次同時訪問4個數組元素 
    { 
        float32x4_t data_vec = vld1q_f32(arr); //依次取4個元素存入暫存器
        vec sum_vec = vaddq_f32(sum_vec, data_vec);//ri = ai + bi 計算兩組暫存器對應元素之和並存放到相應結果 
    } 
    float sum = vgetq_lane_f32(sum_vec, 0)+vgetq_lane_f32(sum_vec, 1)+vgetq_lane_f32(sum_vec, 2)+vgetq_lane_f32(sum_vec, 3);//將累加結果暫存器中的所有元素相加得到最終累加值 
    for (; left4>0; left4--, arr++) 
        sum += (*arr) ; //對於剩下的少於4的數字,依次計算累加即可 
    return sum; 
}

上述演算法的時間複雜度時O(N/4) 從上面的例子看出,使用NEON函式很簡單,只需要將依次處理,變為批處理(如上面的每次處理4個)。

上面用到的函式有: float32x4_t vdupq_n_f32 (float32_t value) 將value複製4分存到返回的暫存器中

float32x4_t vld1q_f32 (float32_t const * ptr) 從陣列中依次Load4個元素存到暫存器中

相應的 有void vst1q_f32 (float32_t * ptr, float32x4_t val) 將暫存器中的值寫入陣列中

float32x4_t vaddq_f32 (float32x4_t a, float32x4_t b) 返回兩個暫存器對應元素之和 r = a+b

相應的 有float32x4_t vsubq_f32 (float32x4_t a, float32x4_t b) 返回兩個暫存器對應元素之差 r = a-b

float32_t vgetq_lane_f32 (float32x4_t v, const int lane) 返回暫存器某一lane的值

其他常用的函式還有:

float32x4_t vmulq_f32 (float32x4_t a, float32x4_t b) 返回兩個暫存器對應元素之積 r = a*b

float32x4_t vmlaq_f32 (float32x4_t a, float32x4_t b, float32x4_t c) r = a +b*c

float32x4_t vextq_f32 (float32x4_t a, float32x4_t b, const int n) 拼接兩個暫存器並返回從第n位開始的大小為4的暫存器 0<=n<=3 例如 a: 1 2 3 4 b: 5 6 7 8 vextq_f32(a,b,1) -> r: 2 3 4 5 vextq_f32(a,b,2) -> r: 3 4 5 6 vextq_f32(a,b,3) -> r: 4 5 6 7 其他常用的函式可以參考開發網站 https://developer.arm.com/technologies/neon/intrinsics

總之,NEON學習入門很快,但如果想要更精深,就需要多花些時間和功夫在上面。