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
|
Table des matières |
Classes principales
|
(C++26)
|
type vectoriel parallèle aux données
(modèle de classe) |
|
(C++26)
|
alias de commodité pour
basic_simd
qui peut spécifier sa largeur
(modèle d'alias) |
|
(C++26)
|
type parallèle aux données avec le type d'élément
bool
(modèle de classe) |
|
(C++26)
|
alias de commodité pour
basic_simd_mask
qui peut spécifier sa largeur
(modèle d'alias) |
Drapeaux de chargement et de stockage
|
(C++26)
|
indicateurs de chargement et de stockage pour les types parallèles de données
(modèle de classe) |
|
(C++26)
|
indicateur par défaut utilisé pour les opérations de chargement et de stockage
(constante) |
|
(C++26)
|
indicateur activant les conversions non conservatrices de valeurs pour les opérations de chargement et de stockage
(constante) |
|
(C++26)
|
indicateur d'alignement de l'adresse de chargement-stockage sur un stockage spécifié à la valeur de
datapar::alignment
(constante) |
|
(C++26)
|
indicateur d'alignement de l'adresse de chargement-stockage sur un stockage spécifié à l'alignement spécifié
(modèle de variable) |
Opérations de chargement et de stockage
charge les éléments d'une plage contiguë vers
basic_simd
(modèle de fonction) |
|
stocke les éléments de
basic_simd
vers une plage contiguë
(modèle de fonction) |
Casts
|
(C++26)
|
divise un objet parallèle de données unique en plusieurs objets
(modèle de fonction) |
|
(C++26)
|
concatène plusieurs objets parallèles de données en un seul
(modèle de fonction) |
Algorithmes
opérations élément par élément min/max pour
basic_simd
(modèle de fonction) |
|
|
(C++26)
|
opération élément par élément clamp pour
basic_simd
(modèle de fonction) |
|
(C++26)
|
sélection élément par élément utilisant l'opérateur conditionnel
(modèle de fonction) |
Réductions
réduit toutes les valeurs dans
basic_simd
via une opération binaire spécifiée en une seule valeur
(modèle de fonction) |
|
réductions de
basic_simd_mask
en
bool
(modèle de fonction) |
|
|
(C++26)
|
réduction de
basic_simd_mask
en nombre de valeurs
true
(modèle de fonction) |
réductions de
basic_simd_mask
en l'index du premier ou dernier
true
(modèle de fonction) |
Traits
|
(C++26)
|
obtient un alignement approprié pour
datapar::flag_aligned
(modèle de classe) |
|
(C++26)
|
modifie le type d'élément du type parallèle de données
(modèle de classe) |
|
(C++26)
|
modifie la largeur du type parallèle de données
(modèle de classe) |
Fonctions mathématiques
Toutes les fonctions dans
<cmath>
et
<complex>
sont surchargées pour
basic_simd
.
|
Cette section est incomplète
Raison : description |
Fonctions de manipulation de bits
Toutes les fonctions de manipulation de bits dans
<bit>
sont surchargées pour
basic_simd
.
|
Cette section est incomplète
Raison : description |
Détails d'implémentation
Étiquettes ABI
Les types de données parallèles
basic_simd
et
basic_simd_mask
sont associés à des
étiquettes ABI
. Ces étiquettes sont des types qui spécifient la taille et la représentation binaire des objets de données parallèles. La conception prévoit que la taille et la représentation binaire varient selon l'architecture cible et les indicateurs du compilateur. L'étiquette ABI, conjointement avec le type d'élément, détermine la largeur.
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.
| Cette section est incomplète |
Entités d'exposition uniquement
|
Cette section est incomplète
Raison : nécessite une mise à jour |
|
using
/*simd-size-type*/
=
/* voir description */
;
|
(1) | ( exposition uniquement* ) |
|
template
<
std::
size_t
Bytes
>
using /*integer-from*/ = /* voir description */ ; |
(2) | ( exposition uniquement* ) |
|
template
<
class
T,
class
Abi
>
constexpr /*simd-size-type*/ /*simd-size-v*/ = /* voir description */ ; |
(3) | ( exposition uniquement* ) |
|
template
<
class
T
>
constexpr std:: size_t /*mask-element-size*/ = /* voir description */ ; |
(4) | ( exposition uniquement* ) |
|
template
<
class
T
>
concept /*constexpr-wrapper-like*/ = /* voir description */ ; |
(5) | ( exposition uniquement* ) |
|
template
<
class
T
>
using /*deduced-simd-t*/ = /* voir description */ ; |
(6) | ( exposition uniquement* ) |
|
template
<
class
V,
class
T
>
using /*make-compatible-simd-t*/ = /* voir description */ ; |
(7) | ( exposition uniquement* ) |
T
tel que
sizeof
(
T
)
soit égal à
Bytes
.
basic_simd<T, Abi>
, ou
0
dans le cas contraire.
T
désigne
std
::
datapar
::
basic_simd_mask
<
Bytes, Abi
>
,
/*mask-element-size*/
<
T
>
est égal à
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
)
, si le type de
x
+
x
est une spécialisation activée de
basic_simd; sinon - void .
- /*deduced-simd-t*/ < T > , si ce type n'est pas void , sinon
- std :: datapar :: simd < decltype ( x + x ) , V :: size ( ) > .
|
Exigences des fonctions mathématiques
|
||
|
template
<
class
V
>
concept /*simd-floating-point*/ = /* voir description */ ; |
(8) | ( exposition uniquement* ) |
|
template
<
class
...
Ts
>
concept /*math-floating-point*/ = /* voir description */ ; |
(9) | ( exposition uniquement* ) |
|
template
<
class
...
Ts
>
requires
/*math-floating-point*/
<
Ts...
>
|
(10) | ( exposition uniquement* ) |
|
template
<
class
BinaryOp,
class
T
>
concept /*reduction-binary-operation*/ = /* voir description */ ; |
(11) | ( exposition uniquement* ) |
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
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 à :
- /*deduced-simd-t*/ < T0 > , si sizeof... ( Ts ) == 1 est true
- sinon, std:: common_type_t < /*deduced-simd-t*/ < T0 > , /*deduced-simd-t*/ < T1 >> , si sizeof... ( Ts ) == 2 est true et /*math-floating-point*/ < T0 > && /*math-floating-point*/ < T1 > est true ,
- sinon, std:: common_type_t < /*deduced-simd-t*/ < T0 > , T1 > si sizeof... ( Ts ) == 2 est true et /*math-floating-point*/ < T0 > est true ,
- sinon, std:: common_type_t < T0, /*deduced-simd-t*/ < T1 >> si sizeof... ( Ts ) == 2 est true ,
- sinon, std:: common_type_t < /*math-common-simd-t*/ < T0, T1 > , TRest... > , si /*math-common-simd-t*/ < T0, T1 > est un type valide,
- sinon, 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 > est modélisé seulement si :
-
-
BinaryOpest une opération binaire élément par élément qui est commutative, et -
Un objet de type
BinaryOpest invocable avec deux arguments de type std :: datapar :: basic_simd < T, Abi > pour une balise ABI non spécifiéeAbiqui retourne un std :: datapar :: basic_simd < T, Abi > .
-
|
Balises ABI SIMD
|
||
|
template
<
class
T
>
using /*native-abi*/ = /* voir description */ ; |
(12) | ( exposition uniquement* ) |
|
template
<
class
T,
/*simd-size-type*/
N
>
using /*deduce-abi-t*/ = /* voir description */ ; |
(13) | ( exposition uniquement* ) |
- /*simd-size-v*/ < T, /*deduce-abi-t*/ < T, N >> est égal à N ,
- std :: datapar :: basic_simd < T, /*deduce-abi-t*/ < T, N >> est une spécialisation activée, et
- std :: datapar :: basic_simd_mask < sizeof ( T ) , /*deduce-abi-t*/ < /*integer-from*/ < sizeof ( T ) > , N >> est une spécialisation activée.
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
.
|
Indicateurs de chargement et de stockage
|
||
|
struct
/*convert-flag*/
;
|
(14) | ( exposition uniquement* ) |
|
struct
/*aligned-flag*/
;
|
(15) | ( exposition uniquement* ) |
|
template
<
std::
size_t
N
>
struct /*overaligned-flag*/ ; |
(16) | ( exposition uniquement* ) |
std::datapar::flags
. Voir
les indicateurs de chargement et de stockage
pour leurs utilisations correspondantes.
Notes
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_lib_simd
|
202411L
|
(C++26) | Types et opérations parallèles de données |
__cpp_lib_simd_complex
|
202502L
|
(C++26) |
Prise en charge des valeurs complexes entrelacées dans
std::datapar::simd
|
Exemple
#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
|
tableaux numériques, masques de tableau et tranches de tableau
(modèle de classe) |
Liens externes
| 1. | L'implémentation de ISO/IEC TS 19570:2018 Section 9 "Types de traitement parallèle de données" — github.com |
| 2. |
Implémentation TS disponible pour
GCC/libstdc++
(
std::experimental::simd
est livré avec GCC-11) — gcc.gnu.org
|