Namespaces
Variants

Data-parallel types (SIMD) (since C++26)

From cppreference.net

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 > T est 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

type vectoriel parallèle aux données
(modèle de classe)
alias de commodité pour basic_simd qui peut spécifier sa largeur
(modèle d'alias)
type parallèle aux données avec le type d'élément bool
(modèle de classe)
alias de commodité pour basic_simd_mask qui peut spécifier sa largeur
(modèle d'alias)

Drapeaux de chargement et de stockage

indicateurs de chargement et de stockage pour les types parallèles de données
(modèle de classe)
indicateur par défaut utilisé pour les opérations de chargement et de stockage
(constante)
indicateur activant les conversions non conservatrices de valeurs pour les opérations de chargement et de stockage
(constante)
indicateur d'alignement de l'adresse de chargement-stockage sur un stockage spécifié à la valeur de datapar::alignment
(constante)
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

divise un objet parallèle de données unique en plusieurs objets
(modèle de fonction)
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)
opération élément par élément clamp pour basic_simd
(modèle de fonction)
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)
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

obtient un alignement approprié pour datapar::flag_aligned
(modèle de classe)
modifie le type d'élément du type parallèle de données
(modèle de classe)
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 .

Fonctions de manipulation de bits

Toutes les fonctions de manipulation de bits dans <bit> sont surchargées pour basic_simd .

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.

Entités d'exposition uniquement

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* )
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 à :
  • decltype ( x + x ) , si le type de x + x est une spécialisation activée de basic_simd ; sinon
  • void .
7) Soit x une lvalue de type const T . /*make-compatible-simd-t*/ < V, T > est un alias équivalent à :
  • /*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... >

using /*math-common-simd-t*/ = /* voir description */ ;
(10) ( exposition uniquement* )
template < class BinaryOp, class T >
concept /*reduction-binary-operation*/ = /* voir description */ ;
(11) ( exposition uniquement* )
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 à :
  • /*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 > .
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 :

  • BinaryOp est une opération binaire élément par élément qui est commutative, et
  • Un objet de type BinaryOp est invocable avec deux arguments de type std :: datapar :: basic_simd < T, Abi > pour une balise ABI non spécifiée Abi qui 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* )
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 :
  • /*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.
Il est défini uniquement si 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* )
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

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