在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

CPU優化技術-NEON自動向量化

安芯教育科技 ? 來源:安謀科技學堂 ? 2023-01-11 14:53 ? 次閱讀

本文選自極術專欄《嵌入式AI》的文章,授權轉自知乎作者高性能計算學院的《移動端算法優化。前面我們學習了如何快速上手開始NEON編程ArmNEON優化技術Arm NEON 匯編與Intrinsics編程,CPU優化技術之NEON介紹CPU 優化技術-NEON 指令介紹本篇將會詳細介紹NEON 自動向量化

一、概述

SIMD 作為一種重要的并行化技術,在提升性能的同時也會增加開發的難度。目前大多數編譯器都具有自動向量化的功能,將 C/C++ 代碼自動替換為 SIMD 指令。

從編譯技術上來說,自動向量化一般包含兩部分:循環向量化(Loop vectorization)和超字并行向量化(SLP,Superword-Level Parallelism vectorization,又稱Basic block vectorization)

演示代碼:

void add(int *a, int *b, int n, int * restrict sum)
{
    // it is assumed that the input n is an integer multiple of 4
    for (int i = 0; i < (n & ~3); ++i)
    {
        sum[i] = a[i] + b[i];
    }
}
  • 循環向量化將循環進行展開,增加循環中的執行代碼來減少循環次數。如以下代碼將循環次數精簡到之前的1/4。
for (int i = 0; i < (n & ~3); i += 4)
{
    sum[i]     = a[i    ] + b[i];
    sum[i + 1] = a[i + 1] + b[i + 1];
    sum[i + 2] = a[i + 2] + b[i + 2];
    sum[i + 3] = a[i + 3] + b[i + 3];
}
  • SLP 向量化:編譯器將多個標量運算綁定到一起,使其成為向量運算。下圖將四次標量運算替換為一次向量運算。

495aabde-9110-11ed-bfe3-dac502259ad0.png

SLP 自動向量化

接下來介紹如何通過編譯器實現自動向量化。

二、編譯器配置

目前支持自動向量化的編譯器有 Arm Compiler 6、Arm C/C++ Compiler、LLVM-clang 以及 GCC,這幾種編譯器間的相互關系如下表所示。

498f1838-9110-11ed-bfe3-dac502259ad0.png

自動向量化默認不會被啟用,編程人員需要向編譯器提供允許自動向量化的“許可證”來對自動向量化功能進行使能

A.Arm Compiler 中使能自動向量化

下文中 Arm Compiler 6 與 Arm C/C++ Compiler 使用 armclang 統稱,armclang 使能自動向量化配置信息如下表所示:

49985a60-9110-11ed-bfe3-dac502259ad0.png

armclang 實現自動向量化示例:

# AArch32
armclang --target=arm-none-eabi -mcpu=cortex-a53 -O1 -fvectorize main.c

# AArch64
armclang --target=aarch64-arm-none-eabi -O2 main.c

B. LLVM-clang中使能自動向量化

Android NDK 從 r13 開始以 clang 為默認編譯器,本節通過 cmake 調用Android NDK r19c 工具鏈展示 clang 的自動向量化方法。

  • 使用 Android NDK 工具鏈使能自動向量化配置參數如下表:

49a5ed38-9110-11ed-bfe3-dac502259ad0.png

  • 在 CMake 中配置自動向量化方式如下:
# method 1
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O1 -fvectorize")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1 -fvectorize")

# method 2
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")

C. GCC 中使能自動向量化

在 gcc 中使能自動向量化配置參數如下:

49b39eec-9110-11ed-bfe3-dac502259ad0.png

  • 在不明確配置 -mcpu 的情況下,編譯器將使用默認配置(取決于編譯工具鏈時的選項設置)進行編譯。

  • 通常情況下 -mfpu 和 -mcpu 的配置存在關聯性,對應關系如下。(如當選取-mcpu為cortex-a8時,-mfpu一般設置為vfpv3或neon)

49c1dd7c-9110-11ed-bfe3-dac502259ad0.png

gcc 中實現自動向量化的編譯配置如下

# AArch32
arm-none-linux-gnueabihf-gcc -mcpu=cortex-a53 -mfpu=neon -ftree-vectorize -O2 main.c

# AArch64
aarch64-none-linux-gnu-gcc -mcpu=cortex-a53 -ftree-vectorize -O2 main.c

此外,gcc 中可以通過 -fopt-info-vec 命令查看自動向量化的詳細信息,比如哪些代碼實現了向量化,哪些代碼沒有實現向量化及沒有進行向量化的原因。

D. 自動向量化實例

我們以上節的求和示例代碼,來對編譯器自動向量化的功能進行演示。編譯器以 32 位 arm-gcc 為例:

# automatic vectorization is not enabled
arm-none-linux-gnueabihf-gcc -O2 main.c -o avtest

# automatic vectorization is enabled
arm-none-linux-gnueabihf-gcc -mfpu=neon -ftree-vectorize -O2 main.c -o avtest
  • 使用 objdump 查看反匯編代碼,反匯編命令如下:
arm-none-linux-gnueabihf-objdump -d avtest > assemble.txt
  • 反匯編結果對比如下圖:

49cba10e-9110-11ed-bfe3-dac502259ad0.png

反匯編代碼

啟用自動向量化之后,編譯器通過矢量化加載 (ldr -> vld1)求和 (add -> vadd)以及保存 (str -> vst1)等指令,將每次循環中處理的數據變為 4 個,循環次數精簡為之前的 1/4。

三、自動向量化友好型代碼

基于一定的編程優化準則,可以更好的協助編譯器完成自動向量化的工作,獲得理想的性能狀態。

A. 避免使用難以向量化的語句

  • 數據依賴

當循環中存在數據依賴時,編譯器無法進行向量化。

下述代碼中計算 a[i] 時依賴上一次循環的輸出,無法被向量化。

// the output of a[i] depends on its last result
for (int i = 1; i < n; ++i)
{
    a[i] = a[i - 1] + 1;
}
  • 多級指針

編譯器無法對間接尋址,多級索引、多級解引用等行為進行向量化,盡量避免使用多級指針。

下述代碼通過 idx 進行了多級索引,無法被向量化。

// idx is unpredictable, so this code cannot be vectorized
for (int i = 0; i < n; ++i)
{
    sum[idx[i]] = a[idx[i]] + b[idx[i]];
}
  • 條件及跳轉語句

當循環中存在條件語句或跳轉語句時,代碼很難被向量化。因此應盡量避免在循環中的使用if、break等語句。當循環中需要調用函數時,盡量使用內聯函數進行替換。

下述代碼通過調用內聯函數 add_single2 避免發生函數跳轉。

__attribute__((noinline)) int add_single1(int a, int b);

__inline__ __attribute__((always_inline)) int add_single2(int a, int b);

void add(const int *a, const int *b, int n, int * restrict sum)
{
    for (int i = 0; i < (n & ~3); ++i)
    {
        // replace normal functions with inline functions
        // sum[i] = add_single1(a[i], b[i]);
        sum[i] = add_single2(a[i], b[i]);
    }
}
  • 長數據類型

neon 對 64 位長數據類型的支持有限,且較小的數據位寬有更高的并行度,應盡量選用較小的數據類型。當程序中存在浮點數據時,指明其數據類型。

下述代碼指明1.0是浮點數據,否則編譯器會優先將其理解為double。

// assume that array sum and a are floating-point arrays
for (int i = 0; i < (n & ~3); ++i)
{
    // replace 1.0 with 1.f
    // sum[i] = a[i] + 1.0;
    sum[i] = a[i] + 1.f;
}

B. 增加自動向量化信息

  • 地址交疊

指針操縱同一片數據區的情況被稱為地址交疊。地址交疊會阻止自動向量化操作。

當程序不會發生地址交疊時,用 restrict 限定符(C99 引入)在代碼中聲明指針所指區域是獨立的。

下述代碼通過restrict限定 sum 與 a、b 間沒有地址交疊的情況

// add restrict before the output parameter sum
void add(const int *a, const int *b, int n, int * restrict sum)
  • 數組尺寸

明確數組尺寸,使其達到向量化處理長度的整數倍。但應注意處理不足向量化部分的剩余數據。

下述代碼通過掩碼操作表明處理循環次數是 4 的整數倍。

// make number of cycles is an integer multiple of 4, 
for (int i = 0; i < (n & ~3); ++i)
// don't forget to process the remaining data
  • 循環展開

在一些編譯器中可以通過在 for 循環之前增加預處理語句告知編譯器循環展開級數。

下述代碼告知 armclang 編譯器希望將循環展開 4 次。

// #pragma unroll (4) // armcc
#pragma clang loop interleave_count(4) //armclang
for (int i = 0; i < n; ++i)
{
    // ...
}
  • 結構體加載

編譯器僅會對每一成員都有操作的結構體加載操作進行自動向量化,可以結合實際需求考慮去除用于結構體對齊的填充數據。

下述代碼中刪除用于填充結構體的變量 padding 以避免無法向量化。

struct st_align  
{    
    char r;    
    char g;
    char b;
    // delete the data used to populate the structure
    // char padding;
};
  • neon 加載指令要求結構體中的所有項有相同的大小。

下述代碼中結構體由于 short 類型與 char 類型不一致而不會被執行自動向量化。

struct st_align  
{    
    short r; // change short to char to get auto-vectoration
    char g;
    char b;  
};
  • 循環構造

盡量通過

下述代碼通過調整i的范圍實現

// use '<' to construct a loop instead of '<='
// for(int i = 1; i <= n; ++i)
for (int i = 1; i < n + 1; ++i)
{
    // ...
}

當對數組進行操作時,使用數組索引替代指針索引。

下述代碼通過 sum[i]進行索引,而不是*(sum + i)。

// replace arrary with pointer
// *(sum + i) = *(a + i) + *(b + i);
sum[i] = a[i] + b[i];

C. 重排數據實現緩存友好

  • 循環合并

當數據連續存儲在結構體中時,可以進行循環合并操作,即在一個循環內處理臨近的數據,提高緩存命中率。

下述代碼將 r、g、b 三個通道的處理合并到一個循環中。

// combine the rgb operation
/*
for (...)
{
    pixels[i].r = ....;
}  
for (...)
{
    pixels[i].g = ....;
}  
for (...)
{
    pixels[i].b = ....;
}
*/

// cache friendly code
for (...) 
{    
    pixels[i].r = ....;    
    pixels[i].g = ....;    
    pixels[i].b = ....;  
}

四、總結

本章節主要介紹了自動向量化的相關內容,其優缺點對比如下:

49e3cf68-9110-11ed-bfe3-dac502259ad0.png

總之,雖然通過自動向量化技術我們可以在一定程度上降低向量化編程難度,增強代碼的可移植性,但是不能完全依賴于編譯器,而且有時為了獲得更高性能的代碼,還是需要通過intrinsic甚至neon匯編進行編程。

審核編輯 :李倩


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • cpu
    cpu
    +關注

    關注

    68

    文章

    10879

    瀏覽量

    212182
  • C++
    C++
    +關注

    關注

    22

    文章

    2112

    瀏覽量

    73705
  • 編譯器
    +關注

    關注

    1

    文章

    1636

    瀏覽量

    49172

原文標題:Arm NEON學習(七)CPU 優化技術-NEON 自動向量化

文章出處:【微信號:Ithingedu,微信公眾號:安芯教育科技】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    CPU優化技術自動向量化實例

    SIMD 作為一種重要的并行化技術,在提升性能的同時也會增加開發的難度。目前大多數編譯器都具有自動向量化的功能,將 C/C++ 代碼自動替換為 SIMD 指令。
    發表于 10-08 09:31 ?2081次閱讀

    小白快速上手Arm NEON編程手冊指南

    開始加速我們的應用了。使用NEON 技術通常有下列四種方式:調用NEON優化過的庫函數使用編譯器自動
    發表于 07-15 15:38

    簡述ARM SVE的發展以及和NEON的區別來探討Vector在AI中的應用

    vector length是在每次循環中通過指令獲得的,這樣同一份代碼是可以不經改動跑在不同長度的sve機器上,很好的解決了代碼移植的問題。第三,隨著編譯技術的發展,對于自動向量化的推進也獲得了很大
    發表于 09-19 15:27

    使用SVE對HACCmk進行矢量化的案例研究

    介紹隨著 Arm 引入 Scalable Vector Extensions (SVE) 作為 ARMv8-2 中的可選擴展,編譯器自動量化器可以在優化 SVE 或 Neon 之間進
    發表于 11-08 11:50

    如何使用Arm Compiler 6自動量化功能為Neon編譯

    作為一名程序員,你可以通過多種方式使用Neon技術: ?霓虹燈支持的開源庫,如Arm計算庫提供了一個最簡單的利用Neon的方法。 ?編譯器中的自動向量化功能可以
    發表于 08-02 19:31

    RealView編譯工具NEON量化編譯器指南

    RVCT提供了armcc--Vectorize,這是ARM編譯器的一個矢量化版本,它以帶有neon單元的ARM處理器為目標,比如Cortex-A8。 向量化意味著編譯器直接從C或C++代碼生成霓虹燈
    發表于 08-12 06:22

    NEON音頻編解碼器優化技術

    本文旨在探討在采用NEON技術的ARM Cortex-A8處理器解決方案中部署音頻編解碼器時使用的各種優化技術
    發表于 09-02 22:59 ?26次下載

    發掘函數級單指令多數據向量化的方法

    首先分析程序的變量屬性,然后利用一組包括向量函數予句、一致子句、線性子句等編譯指示予句指導編譯器實現函數級向量化,最后利用變量屬性結果對向量化代碼進行了優化。從多媒體和圖像處理領域選擇
    發表于 11-29 16:08 ?0次下載
    發掘函數級單指令多數據<b class='flag-5'>向量化</b>的方法

    基于Matrix的Givens旋轉的QR分解向量化方法

    提出一種基于Matrix的Givens旋轉的QR分解向量化方法。針對Matrix的體系結構特點,對向量數據訪存和計算進行優化,使計算均衡分布到各個向量處理單元;設計雙緩沖DMA的數據傳
    發表于 12-05 14:54 ?1次下載
    基于Matrix的Givens旋轉的QR分解<b class='flag-5'>向量化</b>方法

    控制流SIMD向量化方法

    SIMD擴展部件是近年來集成到通用處理器中的加速部件,旨在發掘多媒體和科學計算等程序的數據級并行.控制依賴給發掘程序中的數據級并行帶來了阻礙,當前,無論基于loop-based還是SLP的控制流向量化
    發表于 12-26 14:55 ?0次下載
    控制流SIMD<b class='flag-5'>向量化</b>方法

    DSP的并行指令分析和冗余優化算法

    如今單指令多數據流( SIMD)技術在數字信號處理器(DSP)上得到了廣泛的應用,現有的向量化編譯器大多都實現了自動向量化的功能,但是編譯器并不適合支持DSP為特征的SIMD自動向量化
    發表于 02-24 15:17 ?0次下載
    DSP的并行指令分析和冗余<b class='flag-5'>優化</b>算法

    MATLAB的循環向量化編程方法的詳細資料研究

    在簡要介紹MATLAB軟件基礎上,探討了MABLAB傳統循環結構編程思想及循環向量化編程思想。通過實例對循環結構編程與循環向量化編程進行比較。說明了循環向量化編程的優點。循環向量化方法
    發表于 08-28 17:46 ?4次下載

    NEON編程中的一些常見優化技巧

      讀過上一篇文章“ARM NEON快速上手指南”之后,相信你已經對ARM NEON編程有了基本的認識。但在真正利用ARM NEON優化程序性能時,還有很多編程技巧和注意事項。本文將結
    的頭像 發表于 12-12 09:11 ?2024次閱讀

    CPU優化技術NEON 的基本原理、指令

    Arm NEON 是適用于 Arm Cortex-A 和 Cortex-R 系列處理器的一種 SIMD(Single Instruction Multiple Data)擴展架構。
    發表于 12-19 09:54 ?4639次閱讀

    CPU優化技術層面講解Arm NEON

    NEON intrinsic 函數提供了一種編寫 NEON 代碼的方法,該方法比匯編代碼更易于維護,同時仍然可以控制生成的 NEON 指令。
    發表于 12-26 10:18 ?2216次閱讀
    主站蜘蛛池模板: 国内啪啪| 国产看色免费| 黄频网| videos另类重口tv| 天天射天天射| 国产免费一区二区三区在线 | 明日花绮罗snis-862在线播放| 久久精品国产乱子伦多人| 色噜噜在线视频| 日本xxwwxxww视频免费丝袜| 色多多18免费观看| 在线www天堂资源网| 日韩在线毛片| 亚洲黄色一区二区| 成人欧美一区二区三区小说| 亚洲免费影视| 九九热re| 午夜视频在线网站| 中文字幕亚洲色图| 中国同志chinese小彬tv| 国产精品四虎| 久久亚洲一级毛片| 鲁老汉精品视频在线观看| 中国国产aa一级毛片| 国产精品福利午夜h视频| 国产三级在线观看视频| 五月天婷婷色图| 亚洲欧美一区二区三区麻豆| 久久99热精品这里久久精品| 欧美激情 在线| 成人夜夜| 操插干| 欧美午夜寂寞影院安卓列表| 国产在视频线精品视频2021| 国产欧美亚洲精品第二区首页| 欧美三级视频在线| 男人天堂网在线观看| 日本三级视频在线观看| 最新中文字幕在线资源| 色偷偷亚洲天堂| 欧美一区视频|