データ並列型 (SIMD) (C++26 以降)
このライブラリは、データ並列型とそれらの型に対する操作を提供します。これらは、SIMD レジスタや命令、あるいは共通の命令デコーダによって駆動される実行ユニットなど、利用可能なデータ並列実行リソースを通じて、データ並列性を明示的に記述し、データを構造化するための移植可能な型です。
すべての標準整数型、文字型、およびほとんどの浮動小数点型は、ベクトル化可能型 (vectorizable types) です。ベクトル化可能な浮動小数点型には、float、double、および定義されていれば、選択された拡張浮動小数点型 std::float16_t、std::float32_t、std::float64_t が含まれます。
データ並列型は、要素型 (element type) と呼ばれる、基礎となるベクトル化可能型の1つ以上の要素で構成されます。要素の数、すなわち幅 (width) は、各データ並列型で一定です。
データ並列型は、クラステンプレート basic_simd および basic_simd_mask の有効なすべての特殊化を指します。
データ並列型のデータ並列オブジェクトは、型 T のオブジェクトと同様に振る舞います。しかし、T が単一の値を格納し操作するのに対し、要素型 T を持つデータ並列型は複数の値を格納し操作します。
データ並列オブジェクトに対するすべての操作は、要素ごと (element-wise) に作用します (リダクションのような水平操作は例外で、そのように明記されています)。これはオブジェクトの各要素、または2つのオブジェクトの対応する要素に適用されます。各適用は、他の適用に対して順序付けされません。この単純な規則がデータ並列性を表現しており、コンパイラはこれを利用して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 に対する要素ごとの最小/最大操作(関数テンプレート) |
| (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 のエイリアスです。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 を 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型のオブジェクトが、未指定のABIタグAbiに対して std::basic_simd<T, Abi> 型の2つの引数で呼び出し可能であり、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節 "Data-Parallel Types" の実装 — github.com |
| 2. | GCC/libstdc++ のTS実装 (std::experimental::simd は GCC-11 に同梱) — gcc.gnu.org |