Data-parallel types (SIMD) (since C++26)
La bibliothèque fournit des types parallèles de données et des opérations sur ces types : des types portables pour exprimer explicitement le parallélisme de données et structurer les données via des ressources d'exécution parallèles de données lorsqu'elles sont disponibles, telles que SIMD registres et instructions ou unités d'exécution pilotées par un décodeur d'instructions commun.
L'ensemble des vectorizable types comprend :
- tous les types entiers et caractères standards ;
- la plupart des types à virgule flottante incluant float , double , et les types étendus à virgule flottante sélectionnés : std:: float16_t , std:: float32_t , et std:: float64_t s'ils sont définis ; et
-
std::
complex
<
T
>
où
Test un type à virgule flottante vectorisable.
Un type à parallélisme de données est constitué d'un ou plusieurs éléments d'un type vectorisable sous-jacent, appelé le type d'élément . Le nombre d'éléments, appelé la largeur , est constant pour chaque type à parallélisme de données.
Le type parallèle de données fait référence à toutes les spécialisations activées des modèles de classe
basic_simd
et
basic_simd_mask
.
Un
objet parallèle de données
de type parallèle de données se comporte de manière analogue aux objets de type
T
. Mais alors que
T
stocke et manipule une seule valeur, le type parallèle de données avec le type d'élément
T
stocke et manipule plusieurs valeurs.
Chaque opération sur un objet parallèle de données agit élément par élément (à l'exception des opérations horizontales, telles que les réductions, qui sont clairement indiquées comme telles) s'appliquant à chaque élément de l'objet ou aux éléments correspondants de deux objets. Chaque application de ce type est non séquencée par rapport aux autres. Cette règle simple exprime le parallélisme de données et sera utilisée par le compilateur pour générer des instructions SIMD et/ou des flux d'exécution indépendants.
Toutes les opérations (sauf les surcharges de fonctions mathématiques non- constexpr ) sur les objets data-parallel sont constexpr : il est possible de créer et d'utiliser des objets data-parallel dans l'évaluation d'une expression constante.
Les modèles d'alias
simd
et
simd_mask
sont définis pour permettre aux utilisateurs de spécifier la largeur à une taille déterminée. La largeur par défaut est déterminée par l'implémentation au moment de la compilation.
|
Défini dans l'en-tête
<simd>
|
|
|
Défini dans l'espace de noms
std::datapar
|
Sommaire
Classes principales
Drapeaux de chargement et de stockage
Opérations de chargement et de stockage
Casts
Algorithmes
Réductions
Permutations
Traits
Fonctions mathématiques
Toutes les fonctions dans
<cmath>
et
<complex>
sont surchargées pour
Fonctions de manipulation de bitsToutes les fonctions de manipulation de bits dans <bit> sauf std::bit_cast sont surchargées pour
Détails d'implémentationÉtiquettes ABI
Les types de données parallèles
L'étiquette ABI reste indépendante de la sélection du jeu d'instructions machine. Le jeu d'instructions machine choisi limite les types d'étiquettes ABI utilisables. Les étiquettes ABI permettent aux utilisateurs de transmettre en toute sécurité des objets de type data-parallel à travers les limites des unités de traduction.
Entités d'exposition uniquement
1)
/*simd-size-type*/
est un alias pour un type entier signé. L'implémentation est libre de choisir n'importe quel type entier signé.
2)
/*integer-from*/
<
Bytes
>
est un alias pour un type entier signé
T
tel que
sizeof
(
T
)
soit égal à
Bytes
.
3)
/*simd-size-v*/
<
T, Abi
>
désigne la largeur de la spécialisation activée
basic_simd<T, Abi>
, ou
0
dans le cas contraire.
4)
Si
T
désigne
std
::
datapar
::
basic_simd_mask
<
Bytes, Abi
>
,
/*mask-element-size*/
<
T
>
est égal à
Bytes
.
5)
Le concept
/*constexpr-wrapper-like*/
est défini comme suit :
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;
6)
Soit
x
une lvalue de type
const
T
.
/*deduced-simd-t*/
<
T
>
est un alias équivalent à :
7)
Soit
x
une lvalue de type
const
T
.
/*make-compatible-simd-t*/
<
V, T
>
est un alias équivalent à :
8)
Le concept
/*simd-floating-point*/
est défini comme suit :
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>;
9)
Le concept
/*math-floating-point*/
est défini comme :
template< class... Ts > concept /*math-floating-point*/ = (/*simd-floating-point*/</*deduced-simd-t*/<Ts>> || ...);
10)
Soit
T0
désignant
Ts...
[
0
]
,
T1
désignant
Ts...
[
1
]
, et
TRest
désignant un pack tel que
T0, T1, TRest...
soit équivalent à
Ts...
. Alors,
/*math-common-simd-t*/
<
Ts...
>
est un alias équivalent à :
11)
Le concept
/*reduction-binary-operation*/
est défini comme suit :
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 > est modélisé seulement si :
12)
/*native-abi*/
<
T
>
est un alias défini par l'implémentation pour un tag ABI. C'est le tag ABI principal à utiliser pour une vectorisation explicite efficace. En conséquence,
basic_simd
<
T,
/*native-abi*/
<
T
>>
est une spécialisation activée.
13)
/*deduce-abi-t*/
<
T, N
>
est un alias qui désigne un type de balise ABI tel que :
T
est un type vectorisable, et
N
>
0
&&
N
<=
M
est
true
, où
M
est un maximum défini par l'implémentation qui est au moins
64
et peut différer selon
T
.
14-16)
Ces types de balises sont utilisés comme argument template de
std::datapar::flags
. Voir
les indicateurs de chargement et de stockage
pour leurs utilisations correspondantes.
Notes
Exemple
Exécuter ce code
#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::datapar::basic_simd<int, A> my_abs(std::datapar::basic_simd<int, A> x) { return std::datapar::select(x < 0, -x, x); } int main() { constexpr std::datapar::simd<int> a = 1; println("a", a); constexpr std::datapar::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::datapar::reduce(e); std::cout << "inner product: " << inner_product << '\n'; constexpr std::datapar::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)); } Sortie : 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 Voir aussi
|