2022年6月1日 星期三

The Story After HEVC 簡報上線

 

近日應於政大任教的朋友於其課程中分享心得的 "The Story After HEVC" 簡報上線

 

 歡迎不吝指教

2022年4月28日 星期四

初探 clang 14.0 中的 Matrix Type

先前 Clang 14.0 的文章中提到 Clang 14.0 新增了 Matrix Type, 出於個人的好奇, 於是就開始動手玩了一下, 這裡先說結論, Clang 14.0 中的 Matrix Type 還是開發階段, 尚不太好操作. 這裡嘗試使用 Matrix Type 的應用是 Tiled Matrix Multiplication.

比較基準的 vector type 實作

首先是先前已經以 Clang Vector Extension 所實作的 float 型別 8x8 矩陣乘法

typedef float float8 __attribute__((ext_vector_type(8)));
void gemm_vec(
    // buffer, stride
    float *a, int sa,  
    float *b, int sb,
    float *c, int sc
){
    float8 vb[8];
    for(int y = 0; y < 8; y++)
        vb[y] = *((float8*)(b + sb*y));
    }
    for(int y = 0; y < 8; y++){
        float8 vc = *((float8*)(c + sc*y));
        float8 va = *((float8*)(a + sa*y));
        for(int x = 0; x < 8; x++)
            vc += va[x]*vb[x];
        *((float8*)(c + sc*y)) = vc;
    }
}

以 matrix type 實作的過程

接著是參考 Clang Matrix Type 的說明嘗試定義 8x8 的 Matrix Type:

typedef float float8x8 __attribute__((matrix_type(8, 8)));

首先值得一提的是定義的 matrix type 所宣告的變數, 類似於 vector type 內作為 1D array, matrix type 變數能作為 2D array 的語法來存取個別數值. 這裡定義好 float8x8 型別之後, 必須宣告變數並將 2D 資料載入, 於是會發現文件中並沒有說明如何載入資料! 這部份必須參考目前的 Draft Specification, 目前 Clang 僅支援 3 個 function:

  • M2 __builtin_matrix_transpose(M1 matrix)
  • M __builtin_matrix_column_major_load(T *ptr, size_t row, size_t col, size_t columnStride)
  • void __builtin_matrix_column_major_store(M matrix, T *ptr, size_t columnStride)

總之, 資料載入的方式有了, 那麼 column major load /store 的意義為何? 這主要是載入資料的順序, 參考 Wikipedia 關於 Row- and column-major order 頁面的圖解, 可以得知對於 column major load 的意義在於:

有點不太理解 clang 第一時間所支援的竟然不是 row major load / store. 然而使用 column major load store 的結果是所得到的 matrix 為原本所需的 transposed matrix. Anyway, 由於我們只有這個讀取方式, 所以我們先讀取原先 function 中的 a, b, c 矩陣:

float8x8 ma, mb, mc;
ma = __builtin_matrix_column_major_load(a, 8, 8, sa);
mb = __builtin_matrix_column_major_load(b, 8, 8, sb);
mc = __builtin_matrix_column_major_load(c, 8, 8, sc);

然而我們還是可以動些手腳來達成原本的 Tiled Matrix Multiplication, 首先我們原本要計算的資料為

C += A x B

然而這當中的 A, B, C, 透過 __builtin_matrix_column_major_load 所得到的已經是 transposed matrix (這裡我們用 A', B', C' 代表), 因此我們可以透過預先透過 __builtin_matrix_transpose 來轉回 A, B, C. 正確計算後能再轉回原先的 column major matrix 來存回正確的數值. 

ma = __builtin_matrix_transpose(ma);
mb = __builtin_matrix_transpose(mb);
mc = __builtin_matrix_transpose(mc);
mc += ma*mb;
mc = __builtin_matrix_transpose(mc); //transposed again
__builtin_matrix_column_major_store(mc, c, sc);

這裡使用了四次 __builtin_matrix_transpose, 僅僅是為了達到計算上正確的 C=AxB, 由於在回存時必須保持 transposed 狀態, 所以如果計算時能產生對應 C' 的資料即可, 於是我們可以這麼計算:

C'+=B'xA'
於是我們整個實作就可以簡化如下:

void gemm_vec(
    float *a, int sa,
    float *b, int sb,
    float *c, int sc
){
    float8x8 ma, mb, mc;
    ma = __builtin_matrix_column_major_load(a, 8, 8, sa);
    mb = __builtin_matrix_column_major_load(b, 8, 8, sb);
    mc = __builtin_matrix_column_major_load(c, 8, 8, sc);
    mc += mb * ma;
    __builtin_matrix_column_major_store(mc, c, sc);
}

最後是編譯時要注意, clang 必須加入 -fenable-matrix 這個參數

以這種實作搭配 AVX2 在 Intel Core i5-8350U 測試, 對於 naive, vector type 與 matrix type 來測試用於兩個 512x512 matrix multiplication 的效能

因此 single thread 來執行 naive, vector type, matrix type 三種實作的時間分別為 129.9ms, 26.0ms, 27.0ms. 因此以 matrix type 可以得到十分接近的 vector extension 的性能, 但是實作上相當簡潔易懂.

到此許多人應該會關心 clang 未來是否支援 row-major 的形式? 個人認為是會的, 首先 Intel AMX 指令集所使用的即是 row major 的 load/store. 再者是在 2020 年 LLVM 開發者大會中 "Matrix Support in LLVM and Clang" 的演講的倒數第二張投影片 "Remaining Work" 明確標示了 Row-major support.


使用後個人想提的幾個 idea:

定義 matrix 的同時, 應同時定義出 row 與 column 的 vector 型別, 目前不知如何取得定義語法, 這裡暫時以 ".row_vec" 與 ".col_vec" 來表示. 也就是說當我們定義了:

typedef float float8x8 __attribute__((matrix_type(8, 8)));

那麼可以用此定義一併定義出對應用在 row 與 col 的 vector type (或是分開定義但是 compiler 偵測 vector length):

float8x8.row_vec vrow, vrow2;
float8x8.col_vec vcol, vcol2;

如此可以合併使用 vector extension 與 matrix type 來處理 row major 或是 column major 的數值設定:

float8x8 mat;
...
vrow = *((
float8x8.row_vec*)ptr_row);
vcol = *((float8x8.col_vec*)ptr_col);
mat.row[1] = vrow;        // set a row
mat.col[2] = vcol;        // set a column

另外可以做 row vector 與  column vector 與矩陣相乘運算:

float8x8 mat;
...
vrow = *((
float8x8.row_vec*)ptr_row);
vcol = *((float8x8.col_vec*)ptr_col);
vrow2 = vcol*mat;
vcol2 = mat*vrow;


 

2022年3月17日 星期四

Clang 14.0 將增加 vector extension built-in functions

由於 LLVM / Clang 14.0 發佈的接近, 近日就花了點時間看一下對於 vector extension 是否有相關的更新. 目前在 Clang 官方網站文件上已經可以看到.

Clang 14.0 中最令人振奮的莫過於新增的兩個部份

  • Vector Builtins
  • Matrix Types

Vector Builtins

vector builtins 主要能提供 compiler 明確使用對應指令來加速, 而這些不容易透過 C operator 來表示, builtins 分為兩類 elementwise builtins 與 reduction builtins 

Fig. 1 - Elementwise Builtins 列表

Elementwise Builtins 本質上就是 Per-lane operation, 像是 __builtin_elementwise_max, 事實上透過先前分享過的 Clang ternary operator 就可以實作, 也就是說下列的 ternary operation:

vc = va > vb ? va : vb;

基本上等同於:

vc = __builtin_elementwise_max(va, vb);

事實上 elementwise builtins 並不限定於套用在 vector type, 基本的 scalar types 也可以.

Fig. 2 - Reduction Builtins 列表

Reduction Builtins 主要用在可以快速從 vector 中算出單一數值的結果, 舉凡像是總和(sum), 最大值(max)與最小值(min) 等等. 在 Clang 中還有特別的 - 所謂水平方向 AND / OR / XOR. 這對一些特別的應用很有幫助.

Matrix Types

由於現今新一代的 CPU 都加入了對於 matrix 操作加速的指令(Intel 的 AMX, ARM 的 SVE Matrix Extension 與 SME), 因此 Clang 加入 matrix type 也有助於更有效去使用這些.

在 Clang 中可以透過下列方式定義與使用 Matrix:

Fig. 3 - Clang Matrix Type 定義與使用範例

透過定義 Matrix Type, 能夠直接支援基本運算操作. 目前官方說明與範例中並沒有顯示與 vector 的互動. 但以目前的型態撰寫相關應用已便利不少.



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 平台上使用 Function Multi-Versioning (FMV) - 以使用 Android NDK 為例

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