数据并行类型 (SIMD) (C++26 起)
这个库提供数据并行类型和对这些类型的操作:它们是一组可移植的类型,用于明确指定数据并行性,并于数据并行执行资源可用时在其中组织数据,这种资源包括 SIMD 寄存器和指令,或由某个公共指令解码器驱动的执行单元。
可向量化类型 集合包括:
- 所有的标准整数和字符类型;
- 大多数浮点数类型,包括 float、double 以及选定的扩展浮点数类型:std::float16_t、std::float32_t 和 std::float64_t(若有所定义);以及
- std::complex<T> 其中
T
为可向量化的浮点数类型。
数据并行类型 由一个或多个底层的可向量化类型元素组成,这成为其元素类型 。元素的个数,称为其宽度 ,对于每个数据并行类型来说是常量。
数据并行类型指代类模板 basic_simd
和 basic_simd_mask
的所有启用的特化。
数据并行类型的数据并行对象 的表现模仿 T
类型的对象。但 T
是存储并操作单个值,而元素类型为 T
的数据并行类型则存储并操作多个值。
数据并行对象上的每种运算(除了如归约这样的横向运算,它们都是标明的),都表现为运用于对象的每个元素上或者两个对象的对应元素上的逐元素操作。每次这种运用之间都是无顺序的。这条简单规则表现了数据并行性,并由编译器所运用以生成 SIMD 指令和/或独立执行流。
数据并行类型上,(除了非 constexpr 数学函数重载)的所有运算都是 constexpr 的:在常量表达式的求值中创建并使用数据并行对象是有可能的。
别名模板 simd
和 simd_mask
的定义允许用户指定其具有特定大小的宽度。默认的宽度有实现在编译时确定。
在标头 <simd> 定义 | |
在命名空间 std::datapar 定义 |
目录 |
[编辑]主类
(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) | 拼接多个数据并行对象为一个 (函数模板) |
[编辑]算法
basic_simd 的逐元素 min/max 运算 (函数模板) | |
(C++26) | basic_simd 的逐元素 clamp 运算 (函数模板) |
(C++26) | 使用条件运算符进行逐元素选择 (函数模板) |
[编辑]归约
以指定二元运算规约 basic_simd 中的所有值为单个值 (函数模板) | |
规约 basic_simd_mask 为 bool(函数模板) | |
(C++26) | 规约 basic_simd_mask 为 true 值个数 (函数模板) |
将 basic_simd_mask 归约为第一个或最后一个 true 值的索引 (函数模板) |
[编辑]特征
(C++26) | 获得适于 simd_flag_aligned 的对齐 (类模板) |
(C++26) | 改变数据并行类型的元素类型 (类模板) |
(C++26) | 改变数据并行类型的宽度 (类模板) |
[编辑]数学函数
<cmath> 和 <complex> 中的所有函数都为 basic_simd
进行了重载。
本节未完成 原因:description |
[编辑]为操纵函数
<bit> 中的所有为操纵函数都为 basic_simd
进行了重载。
本节未完成 原因:description |
[编辑]实现细节
[编辑]ABI 标签
数据并行类型 basic_simd
和 basic_simd_mask
都关联一组 ABI 标签 。这些标签是用于指定数据并行对象的大小和二进制表示的类型。其设计意图是使大小和二进制表示基于目标架构和编译器选项而变化。ABI 标签和元素类型一同决定其宽度。
ABI 标签保持与机器指令集的选择无关。所选机器指令集限制了可用的 ABI 标签类型。这些 ABI 标签使得用户可以安全地跨越翻译单元边界而传递数据并行类型的对象。
本节未完成 |
[编辑]仅用于阐释的实体
本节未完成 原因:needs update |
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 > constexprstd::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::datapar::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::datapar::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::datapar::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::datapar::simd<T, 1> v){{ binary_op(v, v)}->std::same_as<std::datapar::simd<T, 1>>;};
仅当以下情况下,/*reduction-binary-operation*/<BinaryOp, T> 被实现:
BinaryOp
是具有交换性的二元逐元素运算,并且BinaryOp
类型的对象,对于某个未指明的 ABI 标签Abi
,可以以两个 std::datapar::basic_simd<T, Abi> 类型的实参调用并返回 std::datapar::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::datapar::basic_simd<T, /*deduce-abi-t*/<T, N>> 是被启用的特化,并且
- std::datapar::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> namespace dp = std::datapar; void println(std::string_view name, autoconst& a){std::cout<< name <<": ";for(std::size_t i{}; i != a.size();++i)std::cout<< a[i]<<' ';std::cout<<'\n';} template<class A>constexpr dp::basic_simd<int, A> my_abs(dp::basic_simd<int, A> x){return dp::select(x <0, -x, x);} int main(){constexpr dp::simd<int> a =1; println("a", a); constexpr dp::simd<int> b([](int i){return i -2;}); println("b", b); constexprauto c = a + b; println("c", c); constexprauto d = my_abs(c); println("d", d); constexprauto e = d * d; println("e", e); constexprauto inner_product = dp::reduce(e);std::cout<<"内积: "<< inner_product <<'\n'; constexpr dp::simd<double, 16> x([](int i){return i;}); println("x", x);// <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 内积: 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. | TS 实现抵达 GCC/libstdc++ (GCC-11 提供 std::experimental::simd ) — gcc.gnu.org |