資料平行型別 (SIMD) (C++26 起)
此函式庫提供資料平行型別及針對這些型別的操作:用於明確宣告資料平行化(data-parallelism)的輕便型別,並透過資料平行執行資源(若可用)來建構資料,例如 SIMD 暫存器與指令,或是由通用指令解碼器驅動的執行單元。
所有標準整數型別、字元型別以及大多數浮點型別皆為可向量化型別 (vectorizable types)。可向量化的浮點型別包含 float、double,以及若有定義時的特定延伸浮點型別 std::float16_t、std::float32_t 與 std::float64_t。
資料平行型別由一個或多個底層可向量化型別的元素組成,稱為元素型別 (element type)。元素的數量稱為寬度 (width),對於每個資料平行型別而言該數值為常數。
資料平行型別指的是類別模板 basic_simd 與 basic_simd_mask 的所有啟用特例化。
資料平行型別的資料平行物件之運作方式與型別 T 的物件相似。然而,T 儲存並操作單一值,而元素型別為 T 的資料平行型別則儲存並操作多個值。
對資料平行物件的每個操作皆以逐元素 (element-wise) 方式執行(水平操作如歸約除外,該類操作會明確標註),適用於物件的每個元素或兩個物件的對應元素。此類應用相對於其他應用是未排序的。此簡單規則表達了資料平行性,並將由編譯器用於生成 SIMD 指令和/或獨立執行流。
針對資料平行物件的所有操作(非 constexpr 數學函式多載除外)皆為 constexpr:這使得在常數表示式的求值中建立與使用資料平行物件成為可能。
別名模板 simd 與 simd_mask 經定義以允許使用者指定寬度至特定大小。預設寬度由實作在編譯時期決定。
| 本節尚不完整 原因
|
| 定義於標頭檔
<simd> |
目錄 |
[編輯] 主要類別
| (C++26) |
資料並行向量型別 (類別模板) |
| (C++26) |
用於 basic_simd 的便捷別名模板,可指定其寬度(別名模板) |
| (C++26) |
元素型別為 bool 的資料平行型別 (類別模板) |
| (C++26) |
用於 basic_simd_mask 的便捷別名模板,可指定其寬度(別名模板) |
[編輯] 載入與儲存旗標
| (C++26) |
用於資料平行型別的載入與儲存旗標 (類別模板) |
| (C++26) |
載入與儲存操作中使用的預設旗標 (常數) |
| (C++26) |
啟用在載入與儲存操作中無法保持數值之轉換的旗標 (常數) |
| (C++26) |
指示載入-儲存位址對齊至特定儲存空間(數值為 simd_alignment)的旗標(常數) |
| (C++26) |
指示載入-儲存位址對齊至特定儲存空間(指定對齊值)的旗標 (變數模板) |
[編輯] 載入與儲存操作
從連續區間載入元素至 basic_simd(函式模板) | |
從 basic_simd 儲存元素至連續區間(函式模板) |
[編輯] 轉型
| (C++26) |
將單一資料平行物件分割為多個物件 (函式模板) |
| (C++26) |
將多個資料平行物件串接為單一物件 (函式模板) |
[編輯] 演算法
| (C++26) |
針對 basic_simd 的逐元素 min/max 操作(函式模板) |
| (C++26) |
針對 basic_simd 的逐元素 clamp 操作(函式模板) |
| (C++26) |
使用條件運算子的逐元素選擇 (函式模板) |
[編輯] 歸約
| (C++26) |
將 basic_simd 中的所有值透過指定的二元操作歸約為單一值(函式模板) |
| (C++26) |
將 basic_simd_mask 歸約為 bool(函式模板) |
| (C++26) |
將 basic_simd_mask 歸約為 true 值的數量(函式模板) |
將 basic_simd_mask 歸約為第一個或最後一個 true 值的索引(函式模板) |
[編輯] 特性
| (C++26) |
取得適用於 simd_flag_aligned 的對齊方式(類別模板) |
| (C++26) |
變更資料平行型別的元素型別 (類別模板) |
| (C++26) |
變更資料平行型別的寬度 (類別模板) |
[編輯] 數學函式
<cmath> 中的所有函式皆已為 basic_simd 進行多載。
| 本節尚不完整 原因:描述 |
[編輯] 實作細節
[編輯] ABI 標籤
資料平行型別 basic_simd 與 basic_simd_mask 與 ABI 標籤 關聯。這些標籤是指定資料平行物件大小與二進位表示方式的型別。其設計意圖在於大小與二進位表示方式會根據目標架構與編譯器旗標而變動。ABI 標籤連同元素型別,決定了寬度。
ABI 標籤維持與機器指令集選擇的獨立性。所選的機器指令集限制了可用的 ABI 標籤型別。ABI 標籤使使用者能夠在翻譯單元邊界安全地傳遞資料平行型別的物件。
| 本節尚不完整 |
[編輯] 僅供說明之實體
using /*simd-size-type*/ = /* 見描述 */; |
(1) | (僅供說明*) |
template< std::size_t Bytes > using /*integer-from*/ = /* 見描述 */; |
(2) | (僅供說明*) |
template< class T, class Abi > constexpr /*simd-size-type*/ /*simd-size-v*/ = /* 見描述 */; |
(3) | (僅供說明*) |
template< class T > constexpr std::size_t /*mask-element-size*/ = /* 見描述 */; |
(4) | (僅供說明*) |
template< class T > concept /*constexpr-wrapper-like*/ = /* 見描述 */; |
(5) | (僅供說明*) |
template< class T > using /*deduced-simd-t*/ = /* 見描述 */; |
(6) | (僅供說明*) |
template< class V, class T > using /*make-compatible-simd-t*/ = /* 見描述 */; |
(7) | (僅供說明*) |
T 的別名,使得 sizeof(T) 等於 Bytes。basic_simd<T, Abi> 的寬度,否則為 0。T 代表 std::basic_simd_mask<Bytes, Abi>,則 /*mask-element-size*/<T> 等於 Bytes。template< class T > concept /*constexpr-wrapper-like*/ = std::convertible_to<T, decltype(T::value)> && std::equality_comparable_with<T, decltype(T::value)> && std::bool_constant<T() == T::value>::value && std::bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
- decltype(x + x)(若 x + x 的型別為已啟用的
basic_simd特例化);否則為 - void.
- /*deduced-simd-t*/<T>(若該型別不是 void);否則為
- std::simd<decltype(x + x), V::size()>.
| 數學函式要求 |
||
template< class V > concept /*simd-floating-point*/ = /* 見描述 */; |
(8) | (僅供說明*) |
template< class... Ts > concept /*math-floating-point*/ = /* 見描述 */; |
(9) | (僅供說明*) |
template< class... Ts > requires /*math-floating-point*/<Ts...> |
(10) | (僅供說明*) |
template< class BinaryOp, class T > concept /*reduction-binary-operation*/ = /* 見描述 */; |
(11) | (僅供說明*) |
template< class V > concept /*simd-floating-point*/ = std::same_as<V, std::basic_simd<typename V::value_type, typename V::abi_type>> && std::is_default_constructible_v<V> && std::floating_point<typename V::value_type>;
template< class... Ts > concept /*math-floating-point*/ = (/*simd-floating-point*/</*deduced-simd-t*/<Ts>> || ...);
T0 代表 Ts...[0],T1 代表 Ts...[1],而 TRest 代表一個封包(pack),使得 T0, T1, TRest... 等同於 Ts...。則 /*math-common-simd-t*/<Ts...> 為等同於以下的別名:- /*deduced-simd-t*/<T0>(若 sizeof...(Ts) == 1 為 true)
- 否則為 std::common_type_t</*deduced-simd-t*/<T0>, /*deduced-simd-t*/<T1>>(若 sizeof...(Ts) == 2 為 true 且 /*math-floating-point*/<T0> && /*math-floating-point*/<T1> 為 true),
- 否則為 std::common_type_t</*deduced-simd-t*/<T0>, T1>(若 sizeof...(Ts) == 2 為 true 且 /*math-floating-point*/<T0> 為 true),
- 否則為 std::common_type_t<T0, /*deduced-simd-t*/<T1>>(若 sizeof...(Ts) == 2 為 true),
- 否則為 std::common_type_t</*math-common-simd-t*/<T0, T1>, TRest...>(若 /*math-common-simd-t*/<T0, T1> 為有效型別),
- 否則為 std::common_type_t</*math-common-simd-t*/<TRest...>, T0, T1>。
template< class BinaryOp, class T > concept /*reduction-binary-operation*/ = requires (const BinaryOp binary_op, const std::simd<T, 1> v) { { binary_op(v, v) } -> std::same_as<std::simd<T, 1>>; };
/*reduction-binary-operation*/<BinaryOp, T> 僅在下列情況建模:
-
BinaryOp為交換性的二元逐元素操作,且 BinaryOp型別的物件可以兩個 std::basic_simd<T, Abi> 型別的引數調用(對於未指定的 ABI 標籤Abi),並返回 std::basic_simd<T, Abi>。
-
| SIMD ABI 標籤 |
||
template< class T > using /*native-abi*/ = /* 見描述 */; |
(12) | (僅供說明*) |
template< class T, /*simd-size-type*/ N > using /*deduce-abi-t*/ = /* 見描述 */; |
(13) | (僅供說明*) |
- /*simd-size-v*/<T, /*deduce-abi-t*/<T, N>> 等於 N,
- std::basic_simd<T, /*deduce-abi-t*/<T, N>> 為已啟用的特例化,且
- std::basic_simd_mask<sizeof(T), /*deduce-abi-t*/</*integer-from*/<sizeof(T)>, N>> 為已啟用的特例化。
T 為可向量化型別,且 N > 0 && N <= M 為 true 時定義(其中 M 為實作定義的上限,至少為 64,且可能因 T 而異)。| 載入與儲存旗標 |
||
struct /*convert-flag*/; |
(14) | (僅供說明*) |
struct /*aligned-flag*/; |
(15) | (僅供說明*) |
template< std::size_t N > struct /*overaligned-flag*/; |
(16) | (僅供說明*) |
[編輯] 註解
| 功能測試巨集 | 數值 | 標準 | 功能 |
|---|---|---|---|
__cpp_lib_simd |
202411L |
(C++26) | 資料平行型別與操作 |
[編輯] 範例
#include <iostream> #include <simd> #include <string_view> void println(std::string_view name, auto const& a) { std::cout << name << ": "; for (std::size_t i{}; i != a.size(); ++i) std::cout << a[i] << ' '; std::cout << '\n'; } template<class A> constexpr std::basic_simd<int, A> my_abs(std::basic_simd<int, A> x) { return std::simd_select(x < 0, -x, x); } int main() { constexpr std::simd<int> a = 1; println("a", a); constexpr std::simd<int> b([](int i) { return i - 2; }); println("b", b); constexpr auto c = a + b; println("c", c); constexpr auto d = my_abs(c); println("d", d); constexpr auto e = d * d; println("e", e); constexpr auto inner_product = std::reduce(e); std::cout << "inner product: " << inner_product << '\n'; constexpr std::simd<double, 16> x([](int i) { return i; }); println("x", x); // overloaded math functions are defined in <simd> println("cos²(x) + sin²(x)", std::pow(std::cos(x), 2) + std::pow(std::sin(x), 2)); }
輸出
a: 1 1 1 1 b: -2 -1 0 1 c: -1 0 1 2 d: 1 0 1 2 e: 1 0 1 4 inner product: 6 x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cos²(x) + sin²(x): 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[編輯] 參閱
| 數值陣列、陣列遮罩與陣列切片 (類別樣板) |
[編輯] 外部連結
| 1. | ISO/IEC TS 19570:2018 第 9 節 "資料平行型別" 的實作 — github.com |
| 2. | 針對 GCC/libstdc++ 的 TS 實作範疇 (std::experimental::simd 隨 GCC-11 發佈) — gcc.gnu.org |