顯示具有 sve 標籤的文章。 顯示所有文章
顯示具有 sve 標籤的文章。 顯示所有文章

2022年3月12日 星期六

ARM SVE 研讀筆記 Part-6 - Extended floating-point horizontal reductions 與 SVE/SVE2 optionals

這篇是系列文的最後一篇, 主要談兩個列於標題的項目.

Extended floating-point horizontal reductions

在討論 Extended floating-point horizontal reduction 時必須要知道 ARM 在 ARMv8 時增加了一些 instruction / intrinsic functions. 從官方的 ARM NEON intrinsics reference 可以查找出這次要討論的主角 vaddvq_f32 / vaddvq_f64. 這兩個 intrinsics 的目的在於把向量中所有 float / double elements 加總回傳. 雖然相當簡單, 但是 floating-point 的操作對於驗證有個很重要的議題 - order. 透過 reference 查找出此兩者, 可以得知在 ARMv8 是透過 FADDP 指令來達成的. 這個指令是將相鄰的成對數值相加. 從 vaddvq_f32 可以看出是利用兩個 FADDP 指令達成加總的目的. 這樣做的好處是可以減少 instruction latency. 然而這樣的次序在驗證上需要特別處理.

Fig. 1 - SVE 同時提供了 floating-point  in-order 與 tree-based reduction 處理

在 SVE 中同時提供了 in-order (由左至右) 與 tree-base (成對相加) 的作法. 如此可以取決於效能還是結果驗證從這兩者選擇. 在 ARM C Language Extension 可以看到下列的段落:

Fig. 2 - 兩種不同順序浮點數加總的 SVE intrinsic function

依照先前說明轉換 NEON intrinsics 至 SVE intrinsics 的規則, 可以輕易的了解 NEON 中的 vaddvq_f32 對應的 SVE intrinsic function 為 svaddv_f32. 也映證了原有的方式是基於效能的 tree-based. 而新增加的是由左至右的作法是 svadda_* 系列. 而由於 in-order 的特性, 在執行上也較為沒有效率, 從 Arm Cortex-X2 Core Software Optimization Guide 可以看出 FADDP 與 FADDA 兩者有著相當大落差的 exec latency (也就是需要幾個 CPU cycle 才能產生所需結果)

SVE/SVE2 options

在 ARM 制定 SVE 的過程中, SVE 與 SVE2 各自有其核心目標, 這篇主要是說明 SVE 到 SVE2 制定上功能上的演進. 搭配 ARM A-Profile 網站與 ARM C Language Extension 文件閱讀會比較有感.

Fig. 3 - SVE Base functions

SVE base 中建立了 SVE Programming Model 的基本需求. 基本上大部分的 intrinsic functions 都會落在這個類別.

Fig. 4 - SVE optional

在 ARMv8.6 提出時對於 SVE 增加對 BFloat 16 與 GEMM 的支援. GEMM 上主要是能以 vector 來完成 8x2 & 2x8 (8bit) 或是 2x2 (floating-point) 的 Matrix Multiply. 詳情建議閱讀官方 Blog 介紹文章. 值得一提的是 SVE option 的提出時間點 (ARMv9.1) 比 SVE2 推出的時間(ARMv9.0)還晚.

Fig. 5 - SVE2 Base 新增功能列表

將 SVE 列為核心功能是在 ARMv9.0 的事情, 當時是以 SVE2 的版本加入. 相較於一開始與 Fuji 合作於 A64FX 超級電腦核心的 ARMv8.2 中採用的 SVE 已有相當的時日. SVE2 很大的部份在補足 SVE 設計上的缺漏. 特別是在 SVE 中缺少的 data width widening & narrowing 的運算. 另外像是 while loop control 對於 decremental counter 的支援, 複數與大數的支援等等 ...

Fig. 7 - SVE2 optional 增加的功能

最後是 SVE optional functions, 這一部份主要集中於 Crypto 應用的部份. 透過這些指令可以加速 AES-128, SHA-3  與 SM4 的加解密應用. 另外是一個較大的 extension - SME. SME 基於 SVE / SVE2 提供了更有效處理 Matrix 操作的功能. 除了提供 outer-product engine 外也額外增加新的執行模式 - "Streaming Mode". 軟體實作可以藉由使用這個模式來支援更大的 vector length 與達到更高的 throughput. 詳情可以參考 ARM 官方 SME 介紹文.

至此大略介紹了 ARM SVE/SVE2 的功能與相關 extensions. 後續希望有機會在撰寫相關實務文章.

2022年3月4日 星期五

ARM SVE 研讀筆記 Part-5 - Gather & Scatter

在 Processor 中開始出現 Gather and Scatter 相關指令之前之前, 許多 Hardware IP 就俱備有 Gather & Scatter DMA. 像是由於影像處理的需求, 一些 DSP 就俱備 2D DMA, 許多商業市場上的應用 DSP 都俱備這種能力(e.g: CEVA, Cadence Tensilica, Synopsys Arch ...), 而因為近年的 ML 對於 Tensor 操作的部份, 所以像是 >= 3D 或是俱備更多操作彈性的 DMA 在這幾年之中都出現了. 而 ARM SVE 迎合這個發展方向與相關的應用, 在推出時也針對 vector loading 做了補強, 增加了 Gather & Scatter 的相關指令.

Fig. 1 - Gather & Scatter 示意圖

首先必須說明 Gather 與 Scatter 的意義為何, 這是兩個對應的動作. 所謂的 Gather 是透過硬體所提供的規則將指定不連續的資料於 load in 後整併為單一個 vector 的動作; 而 Scatter 則是一個反向的行為, 也就是將所指定的 vector 中的資料, 透過規則所指定的方式, 將資料存放到各個不連續的記憶體位置中. 以這樣的方式看來 ARM NEON 原本就俱備的 vld2/vst2, vld3/vst3 與 vld4/vst4 事實上也是一個規則固定的 Gather / Scatter 退化型操作.  然而 vldN / vstN 因為規則太過固定, 因此實用性還是有所侷限. (話說 vld3 其實相較 x86 的 SSE/AVX 對於 3-channel 的資料, 像是 RGB/YUV 等等的操作真的方便不少)

Fig. 2 - LD1 scalar base, vector offset 的例子

在 ARM 官方的文件 ARM C Ext. for SVE 中, 對於 Gather / Scatter 並非是匯整在同一個章節, 相對地這些是分散在 6.2 Loads 與 6.3 Stores 兩節中. 對於 Gather / Scatter 中提供不連續記憶體位置的方式有三:

  1. base only: 也就是每個 lane 使用對應 lane 指定的 address
  2. base + offset: 每個 lane 的 address 計算方式為: base + offset (in bytes) 的方式
  3. base + index: 每個 lane 的 address 計算方式為: base + index (of element) 的方式, 也就是位置計算是 index * sizeof(element)

Fig. 3 - AoS 與 SoA 的不同考量示意

而 2. 與 3. 當中的 base  + offset/index 的組合有著 (scalar, vector) 或是 (vector, scalar) 的組合, 一共有 4 種類型. 從使用面向上來看這兩種有著不同的應用:

  • scalar base + vector offset/index - 使用單一的 base, 搭配個別的 offset/index, 也就是說這適合單一起點的 structure of arrays 的方式
  • vector base + scalar offset/index - 使用單一的 offset/index, 搭配個別的 base, 也就是說這實際上非常適合應用在讀取 array of structures.


Fig. 4 - Vector Address 計算的相關 intrinsic 說明

最後是輔助 Gather & Scatter 的 vector address 計算的功能, 在 ARM SVE 中提供了 ADR 以及 INDEX 這兩個指令, 前者提供了 per-lane 的個別計算, 後者是以 base 與 steps 兩個參數來規律地計算產生整個 vector (事實上這個 index function 也可以用在需要等差級數的 vector 時候, 像是 shuffle 這類的情況)

下一篇會進入 ARM SVE 研讀筆記的最後一篇 Part-6, 除了介紹最後一個功能特性 - extended floating-point horizontal reductions 之外也討論 SVE / SVE2 各自涵蓋的功能特性範圍.



2022年2月28日 星期一

ARM SVE 研讀筆記 Part-4 - Predicate-driven loop control and management 與 Vector partitioning & software-managed speculation

Predication-driven loop control and management

Fig. 1 - Part-3 中使用的 ARM 官方 SVE 範例

事實上在 Part-3 的 ARM 官方範例中, 就已經展示了這個特性, 在 ARM SVE 中提供了 WHILELT 與 WHILELE 指令來作為遞增 for loop 中的 > 與 >= 的比較, 並產生對應所需的 predication 結果來處理在一般 SIMD 當中較不方便處理的 loop 結尾, 這是由於 loop 的結尾所需處理的數量很可能並非是 vector-size 的倍數, 而記憶體存取的位置也很可能並非 vector-size aligned. 在範例中可以看到使用了 svwhilelt_b64, 這即為 SVE 提供的 loop control / management 功能. 在 ARM SVE  中針對處理資料寬度提供了各自對應的 intrinsic functions:

  • 8bit - svwhilelt_b8 / svwhilele_b8 / svcntb 
  • 16b -  svwhilelt_b16 / svwhilele_b16 / svcnth
  • 32b - svwhilelt_b32 / svwhilele_b32 / svcntw
  • 64b - svwhilelt_b64 / svwhilele_b64 / svcntd

而在 ARM SVE2 中針對遞減 for loop 增加了 WHILEGT 與 WHILEGE, 很直覺地這是用來處理 loop control 中的 < 與 <= 的比較.

  • 8bit - svwhilegt_b8 / svwhilege_b8 
  • 16b -  svwhilegt_b16 / svwhilege_b16
  • 32b - svwhilegt_b32 / svwhilege_b32
  • 64b - svwhilegt_b64 / svwhilege_b64

Vector partitioning & software-managed speculation

在許多時候 buffer 的長度並非 vector size 的倍數, 這造成 vector load 的行為可能會超過 buffer 邊界, 或是要處理的資料的長度未知, 可能在處理過程中造成 memory fault 的行為. 而在讀取過程中, 可能有因計算需求的其他對應的 vector load. 對於這樣的需求, ARM SVE 導入了 First-Faulting 與 Non-Faulting vector load 的設計.

Fig. 2 - ARM SVE 使用的 register set

在 ARM SVE 的 register set 中有著一個不起眼的 register - first-fault register (FFR), 其寬度與 predicate register 相同. FFR 的目的在於存放執行 first-faulting or non-faulting vector load 中每個 lane 成功與否的結果. 

Fig. 3 - first-fault register 運作機制

而 first-faulting 與 non-faulting 的差異在於 first-faulting vector load 的 VLDFF1 系列指令中, 只有第一個 active lane 會產生造成例外情形的 memory fault. 而 non-faulting vector load 的 VLDNF1 系列指令按照字義上而言就是不會產生 memory fault 的指令. 此外, 無論使用 first-faulting 或是 non-faulting vector load 的時候, 在 FFR 中每個 lane 對應的值若事先已被設為 0 / false, 那麼該 lane 的結果將會是 unknown 狀態. 

Fig. 4 - First-Faulting Vector Load 範例

在 Fig. 4 中為 ARM C Language Extension 對於 first-faulting 的範例. 一開始執行 SETFFR 之後, FFR 的所有值都會為 1/true, 接著連續執行三次 LDFF1 指令, 將指定資料讀取至 Z0 ~ Z3. 最後透過 RDFFR 指令將 FFR 的內容存放至 P3 當中. 而 P3 所存放的數值表示哪些 lanes 在 Z0 ~ Z3 當中皆為 valid 狀態. 此資訊對於一些不定數目但需要合併處理的情況相當有幫助.


3/21 - 感謝來自 FB 臉友 王北北 的指正, SVE 與 SVE2 支援的 WHILELT&WHILELE 與 WHILEGT&WHILEGE 一時不察寫反了, 已修正

2022年2月26日 星期六

ARM SVE 研讀筆記 Part-3 - Per-lane predication

在切入 ARM SVE 第二個特性 - Per-lane predication 之前, 讓我們先以 ARM 官方 SVE 範例程式為例, 說明 SVE 的  C/C++ 程式看起來會是怎樣的型態.

使用 SIMD Intrinsics 依然是性價比相當高的方式, 除了可以直接與 C/C++ 程式碼混合撰寫外, 也避免了直接使用 assembly 撰寫的曠日費時與難以除錯. 當然使用 SIMD intrinsics 的缺點是可攜性與跨平台問題. 這點就在先前文章多少提及, 在此就不綴述. 這裡讓我們以第一個範例來切入 SVE intrinsics 的使用

Fig. 1 - SVE  intrinsics 使用範例程式 double dot

Fig. 1 中的程式來自 ARM 官方 SVE intrinsics 的範例, 這當中有幾個重點:

  • 首先是第一行藍色標示的部份, "arm_sve.h" 的使用對 SVE intrinsics 是必要的
  • 在上一篇有談到 SVE vector type 型別的使用來宣告變數, 上圖中以草綠色標示
  • 如同一般的 ARM NEON 程式, 呼叫所需要的 intrinsic function, 這裡以暗紅色標示. 值得一提的是, 雖然相較於 NEON intrinsics,  SVE 增加了 postfix 來做特定用途. 但多數基本功能, 都可以將 ARM NEON intrinsics 原有的名稱在開頭加上一個 "s" 並且移除用來判別處理 64/128-bit 寬度的 "q", 就可以索引到所需的功能. 例如, 原本做 float 型別的vector element 總和會使用 vaddvq_f32, 這裡就可以去搜尋到 Fig. 2 中所列出的 svaddv_f32
  • 最後是紫色標示的部份, 這也是本文要說明的 Per-lane predication, 在 ARM SVE 中新增了在 DSP SIMD 中常見的 Per-lane predication, 這能用來做更為細緻的控制, 來減少在程式撰寫中因 SIMD 限制而不得不以 scalar code 來處理的部份. 這個範例精簡地展示了 predicate 用在 SVE 中的 3 個 predication 特性 - 本文的 per-lane predication 與後續的Predicate-driven loop control and management 與 Vector partitioning & software-managed speculation.
Fig. 2 - 透過 NEON vaddvq_f32 能找到的 SVE intrinsics 系列

所謂的 Per-lane predication 功能是指在 SIMD 中在套用的操作上, 對於每個 lane 提供一個 bit 來做 active or inactive 的控制. 而 active lane 即表示需要直接套用所使用的操作. 對於 inactive lane 部份則有幾個模式, 這點會在稍候解釋.

Fig. 3 - predication 圖示

在 ARM SVE 的設計上對於多數 intrinsic function 都加入了 svbool_t 型別的參數 pg, 這即是用來作為 lane control 的 predication. 其基本概念如 Fig. 3 所示, 一旦該 lane predication 值為 1/true 的即表示該 lane 狀態是 active; 而若該 lane 的 predication 值為 0/false 的, 該 lane 狀態即為 inactive. 若 intrinsic function 沒有特別的 postfix, 那麼 inactive lane 則是維持原本的數值.

Fig. 4 - Predication register 對應 Vector register 的方式.

若對 Part-1 的內容還有印象的話, ARM SVE 的 vector register 為 128b 的倍數, 可為 128~2048b 的任何選擇. 而 predication register 為 16b 的倍數, 可為 16~256b 的任何選擇. 但 predication register 與 vector register 長度的關係是連動的. 一旦決定了 ARM SVE 中關於 vector length 的 LEN 參數, 就固定了兩者的長度. 這也表示 vector / predication 是以 8:1 的比例對應的. 對於 8b 以上的型別會有額外的 bit. 這時的選擇方式如 Fig. 4 所圖解的, 對於 16/32/64b 的 data types, 是以對應的多個 bits 之中最低位的 bit 來控制該 lane.
Fig. 5 - Predication 產生與操作的方式

到此可以輕易地理解, Predication 通常是用來處理 SIMD 中通常難以處理的 if/else 或是 switch case 等等的流程. 因此 predication 的產生最直觀的就是對應 C++ boolean type 中的許多 logical operations - 舉凡數值間各種比較運算 !=, ==, <, <= , >, >= 與 boolean type 間各類 !, &&, || 等等.

Fig. 6 - Inactive Lane 的模式

最後是 inactive lane 的動作, 依照所使用的 intrinsic function 共有 3 種, 也就是在一些 intrinsic functions 的名稱中在 data type 之後還可以看到 "_z", "_m" 或是 "_z" 的 postfix. 這是對於 inactive lane 模式的選擇. 使用了 "_z" 結尾的 intrinsic function 表示了希望 inactive lane 的數值填為 0; 而使用了 "_m" 結尾的 function 則是代表維持原本輸入的第一個 vector 參數並不做任何的更動; 最後是 "_x", 這表示不在意 inactive lane 的數值, 讓 compiler 去針對 compilation 參數(performance or code size)去作較佳的選擇( _z, _m 或是甚至不使用 predication ), 一旦使用 "_x" 的 intrinsic function 必須清楚地知道, inactive lane 當中並沒有預設或保證當中為何值.

下一篇將延續 Per-lane predication 來同時講述 Predicate-driven loop control and management 與 Vector partitioning & software-managed speculation.



ARM SVE 研讀筆記 Part-2 - Vector Length Agnostic (VLA)

個人認為 Vector Length Agnostic (VLA)  是討論 SVE 時必須先討論的特性. 談到這特性必須先了解多數的 SIMD 指令集都有固定使用的 vector width/length, 像是 intel 對於 x86 平台的 SIMD 發展過程, 從 MMX 的 64-bit 開始, 一路發展 SSE / 128-bit , AVX & AVX2 / 256-bit 到最近的 AVX-512 / 512-bit 每一組都有固定的 vector length, 但是即便使用相同的運算, 對於使用了不同的 SIMD instruction set, 這也必須對應到 CPU 內不同的 opcode. 因此使用舊的 SIMD instruction 的程式並無法受益於新一代 SIMD ISA 的好處.

而 ARM SVE 在制定時一開始就允許 IC 商能夠依照應用的需求的考量, 去配置硬體內的 SIMD vector length. 另外制定 SVE 專屬的 VLA vector type 與 intrinsics, 讓開發者能夠達到撰寫所撰寫的 ARM SVE 程式無需特別針對 vector length 改寫或重新編譯, 即可執行運作. 而 IC vendor 也能更為安心地藉由增加 vector length 達到效能增加而無需擔心程式的重新客製問題.

Fig. 1 - Vector Length Agnostic 的圖解與程式特性

類似於其他各 SIMD 指令集的 C intrinsics, 需要對應的 vector type 與 intrinsics function. ARM 對於 SVE,  也提供了對應的 vector type 以及 function 介面. 如 Fig. 2 中所列出的, 其 vector type 涵蓋了基本的 8/16/32/64-bit signed / unsigned int 與 float / double 等等, 然而還增加了 boolean, float16 與 bfloat16 等等新的型別.
Fig. 2 - ARM SVE intrinsics提供的 vector types

然而這些 Vector types 與其他 SIMD intrinsics 所提供的最大不同是這些是 "sizeless", 也就是對於 compiler 而言,  這些 vector types 並沒有俱備任何 size 資訊. 於第一時間或許覺得沒大問題, 但是事實上 VLA 帶來了許多在語言特性以及工具使用上的限制: 

Fig. 3 - Sizeless 帶來的實務限制

如 Fig. 3 所列出的, VLA 帶來的限制主要都是因為缺乏 size 資訊造成需要此資訊的語言特性使用的限制. 這些包含了無法去使用 sizeof / alignment, pointer arithmetic. 比較不直覺的像是無法作為 union / struct / class 的 member field 與宣告用來存放的 STL container , 另外無法成為 exception handling 中 throw / catch 機制的 object. 最後是 lambda 中 capature 機制無法 capture by value. (但是能夠使用 capture by reference )

 

2022年2月16日 星期三

ARM SVE 研讀筆記 Part-1 - ARM SVE 基本概述

ARM 於 ARMv8.2 時期開發了新一代 SIMD 指令集, 以選擇性擴充的方式來接替已使用多年的 NEON, 並在 ARMv9 中預設涵蓋, 該 SIMD ISA 全名為 Scalable Vector Extension 簡寫為 SVE. 這中中最著名的使用案例是與富士合作的 ARM 超級電腦平台中使用了配置為 512b vector length 的 SVE 實作. 相較 NEON 而言, SVE 除了新增功能特性的設計上, 除了增進便利性, 也同時一併考慮了 SIMD 效能擴充 / 延伸性的問題. 

Fig.1 - ARM SVE vector register 與 NEON register 關係圖.

ARM SVE 的設計最特別的地方在於它採用了不同於現今主流 CPU / DSP 皆以 fixed vector length 來制定出一組 SIMD 指令集的方式. 在設計上採用了硬體實作中可對於內部 vector length 作可調整配置的方式, 這也是名稱上使用了 "Scalable" 一詞的原因. ARM SVE 允許硬體商因應用效能上需求的考量而去決定 vector length, 越長的 vector 因為需要更多的 ALU 來提供 ILP, 也會直接地反映在 die area 上. 而 ARM SVE 的 vector length 選擇上必須是介於 128~2048b 而且為 128b 的倍數.

Fig. 2 - ARM SVE 新增的 predicate register.
設計上與 ARM NEON 的另一個重大差異, 在於 ARM SVE 導入 16 個 predicate register.  這是用在於 DSP SIMD 中相當常見的 Per-lane predication 用途上. 在此必須特別說明的是於 Fig. 2 中的 LEN 參數與 Fig. 1 的是連動的相同數值, 這表示 IC vendor 選擇了 LEN 就同時固定了 vector 與 predicate register 的長度, 因此兩者長度的比例固定為 8:1. 在 ARM SVE 中透過使用  predicate, 能夠精確的控制個別資料的計算與否, 如此處理一些 if/else 等等的狀況會更為便利. 由於 SVE 的進階功能都圍繞著 predication 的使用, 因而 SVE 也有著對 predication 運算操作的指令.
Fig. 3 - 總結 ARM SVE 的重大特性

個人統整認為 SVE 的主要特性有 1(個人認為應該加入) + 5, 分別是:

  • Vector Length Agnostic (VLA)
  • Gather-load and Scatter-Store
  • Per-lane predication
  • Predicate-driven loop control and managment
  • Vector partitioning for software-managed speculation
  • Extended floating-point and bitwise horizontal reductions.

後續陸續將這些心得一一分享. 下一篇將介紹說明 Vector Length Agnostic (VLA)

在 ARM 平台上使用 Function Multi-Versioning (FMV) - 以使用 Android NDK 為例

Function Multi-Versioning (FMV) 過往的 CPU 發展歷程中, x86 平台由於因應各種應用需求的提出, 而陸陸續續加入了不同的指令集, 此外也可能因為針對市場做等級區隔, 支援的數量與種類也不等. 在 Linux 平台上這些 CPU 資訊可以透過...