Datentypen für parallele Datenverarbeitung (SIMD) (seit C++26)
Die Bibliothek stellt Datentypen für parallele Datenverarbeitung und Operationen auf diesen Typen bereit: portable Typen zur expliziten Angabe von paralleler Datenverarbeitung und zur Strukturierung von Daten durch parallele Ausführungsressourcen, wo verfügbar, wie z. B. SIMD-Register und -Instruktionen oder Ausführungseinheiten, die von einem gemeinsamen Instruktionsdekoder gesteuert werden.
Alle Standard-Integer-Typen, Zeichentypen und die meisten Gleitkommatypen sind vektorisierbare Typen. Vektorisierbare Gleitkommatypen umfassen float, double und die ausgewählten erweiterten Gleitkommatypen std::float16_t, std::float32_t und std::float64_t, falls definiert.
Ein Datentyp für parallele Datenverarbeitung besteht aus einem oder mehreren Elementen eines zugrundeliegenden vektorisierbaren Typs, dem sogenannten Elementtyp. Die Anzahl der Elemente, die Breite genannt wird, ist für jeden Datentyp für parallele Datenverarbeitung konstant.
Der Datentyp für parallele Datenverarbeitung bezieht sich auf alle aktivierten Spezialisierungen der Klassenvorlagen basic_simd und basic_simd_mask.
Ein Objekt für parallele Datenverarbeitung vom Typ für parallele Datenverarbeitung verhält sich analog zu Objekten vom Typ T. Während T jedoch einen einzelnen Wert speichert und manipuliert, speichert und manipuliert der Datentyp für parallele Datenverarbeitung mit dem Elementtyp T mehrere Werte.
Jede Operation auf einem Objekt für parallele Datenverarbeitung wirkt elementweise (mit Ausnahme von horizontalen Operationen wie Reduktionen, die als solche klar gekennzeichnet sind) und wendet sich auf jedes Element des Objekts oder auf entsprechende Elemente zweier Objekte an. Jede solche Anwendung ist bezüglich der anderen nicht sequenziert. Diese einfache Regel drückt parallele Datenverarbeitung aus und wird vom Compiler verwendet, um SIMD-Instruktionen und/oder unabhängige Ausführungsströme zu generieren.
Alle Operationen (außer nicht-constexpr mathematischen Funktionsüberladungen) auf Objekten für parallele Datenverarbeitung sind constexpr: es ist möglich, Objekte für parallele Datenverarbeitung zur Auswertung eines konstanten Ausdrucks zu erstellen und zu verwenden.
Alias-Vorlagen simd und simd_mask werden definiert, um es Benutzern zu ermöglichen, die Breite auf eine bestimmte Größe festzulegen. Die Standardbreite wird zur Kompilierzeit von der Implementierung bestimmt.
| Dieser Abschnitt ist unvollständig Grund
|
| Definiert im Header
<simd> |
Inhalt |
[edit] Hauptklassen
| (C++26) |
Datenparalleler Vektortyp (Klassenvorlage) |
| (C++26) |
Bequeme Alias-Vorlage für basic_simd, die ihre Breite angeben kann(Alias-Vorlage) |
| (C++26) |
Datentyp für parallele Datenverarbeitung mit dem Elementtyp bool (Klassenvorlage) |
| (C++26) |
Bequeme Alias-Vorlage für basic_simd_mask, die ihre Breite angeben kann(Alias-Vorlage) |
[edit] Lade- und Speicherflags
| (C++26) |
Lade- und Speicherflags für Datentypen für parallele Datenverarbeitung (Klassenvorlage) |
| (C++26) |
Standardflag, das bei Lade- und Speicheroperationen verwendet wird (Konstante) |
| (C++26) |
Flag, das bei Lade- und Speicheroperationen nicht werterhaltende Konvertierungen ermöglicht (Konstante) |
| (C++26) |
Flag, das die Ausrichtung der Lade-/Speicheradresse an einem bestimmten Speicher auf den Wert von simd_alignment anzeigt(Konstante) |
| (C++26) |
Flag, das die Ausrichtung der Lade-/Speicheradresse an einem bestimmten Speicher auf die angegebene Ausrichtung anzeigt (Variablenvorlage) |
[edit] Lade- und Speicheroperationen
lädt Elemente aus einem zusammenhängenden Bereich in basic_simd(Funktionsvorlage) | |
speichert Elemente aus basic_simd in einen zusammenhängenden Bereich(Funktionsvorlage) |
[edit] Konvertierungen
| (C++26) |
teilt ein einzelnes Objekt für parallele Datenverarbeitung in mehrere auf (Funktionsvorlage) |
| (C++26) |
verknüpft mehrere Objekte für parallele Datenverarbeitung zu einem einzigen (Funktionsvorlage) |
[edit] Algorithmen
| (C++26) |
elementweise Min/Max-Operationen für basic_simd(Funktionsvorlage) |
| (C++26) |
elementweise Clamp-Operation für basic_simd(Funktionsvorlage) |
| (C++26) |
elementweise Auswahl mittels bedingtem Operator (Funktionsvorlage) |
[edit] Reduktionen
| (C++26) |
reduziert alle Werte in basic_simd über eine angegebene binäre Operation auf einen einzelnen Wert(Funktionsvorlage) |
| (C++26) |
Reduktionen von basic_simd_mask auf bool(Funktionsvorlage) |
| (C++26) |
Reduktion von basic_simd_mask auf die Anzahl der true-Werte(Funktionsvorlage) |
Reduktionen von basic_simd_mask auf den Index des ersten oder letzten true-Werts(Funktionsvorlage) |
[edit] Traits
| (C++26) |
erhält eine geeignete Ausrichtung für simd_flag_aligned(Klassenvorlage) |
| (C++26) |
ändert den Elementtyp des Datentyps für parallele Datenverarbeitung (Klassenvorlage) |
| (C++26) |
ändert die Breite des Datentyps für parallele Datenverarbeitung (Klassenvorlage) |
[edit] Mathematische Funktionen
Alle Funktionen in <cmath> sind für basic_simd überladen.
| Dieser Abschnitt ist unvollständig Grund: Beschreibung |
[edit] Implementierungsdetails
[edit] ABI-Tags
Die Datentypen für parallele Datenverarbeitung basic_simd und basic_simd_mask sind mit ABI-Tags verbunden. Diese Tags sind Typen, die die Größe und die binäre Darstellung von Objekten für parallele Datenverarbeitung spezifizieren. Das Design sieht vor, dass Größe und binäre Darstellung je nach Zielarchitektur und Compiler-Flags variieren. Das ABI-Tag bestimmt zusammen mit dem Elementtyp die Breite.
Das ABI-Tag bleibt unabhängig von der Auswahl des Maschinensetups unabhängig. Das gewählte Maschinensetup schränkt die verwendbaren ABI-Tag-Typen ein. Die ABI-Tags ermöglichen es Benutzern, Objekte vom Typ für parallele Datenverarbeitung sicher über Übersetzungseinheitsgrenzen hinweg zu übergeben.
| Dieser Abschnitt ist unvollständig |
[edit] Expositions-nur Entitäten
using /*simd-size-type*/ = /* siehe Beschreibung */; |
(1) | (nur Exposition*) |
template< std::size_t Bytes > using /*integer-from*/ = /* siehe Beschreibung */; |
(2) | (nur Exposition*) |
template< class T, class Abi > constexpr /*simd-size-type*/ /*simd-size-v*/ = /* siehe Beschreibung */; |
(3) | (nur Exposition*) |
template< class T > constexpr std::size_t /*mask-element-size*/ = /* siehe Beschreibung */; |
(4) | (nur Exposition*) |
template< class T > concept /*constexpr-wrapper-like*/ = /* siehe Beschreibung */; |
(5) | (nur Exposition*) |
template< class T > using /*deduced-simd-t*/ = /* siehe Beschreibung */; |
(6) | (nur Exposition*) |
template< class V, class T > using /*make-compatible-simd-t*/ = /* siehe Beschreibung */; |
(7) | (nur Exposition*) |
T, so dass sizeof(T) gleich Bytes ist.basic_simd<T, Abi>, oder 0 andernfalls.T std::basic_simd_mask<Bytes, Abi> bezeichnet, ist /*mask-element-size*/<T> gleich 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), wenn der Typ von x + x eine aktivierte Spezialisierung von
basic_simdist; andernfalls - void.
- /*deduced-simd-t*/<T>, wenn dieser Typ nicht void ist, andernfalls
- std::simd<decltype(x + x), V::size()>.
| Anforderungen an mathematische Funktionen |
||
template< class V > concept /*simd-floating-point*/ = /* siehe Beschreibung */; |
(8) | (nur Exposition*) |
template< class... Ts > concept /*math-floating-point*/ = /* siehe Beschreibung */; |
(9) | (nur Exposition*) |
template< class... Ts > requires /*math-floating-point*/<Ts...> |
(10) | (nur Exposition*) |
template< class BinaryOp, class T > concept /*reduction-binary-operation*/ = /* siehe Beschreibung */; |
(11) | (nur Exposition*) |
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] und TRest ein Pack, so dass T0, T1, TRest... äquivalent zu Ts... ist. Dann ist /*math-common-simd-t*/<Ts...> ein Alias, der äquivalent ist zu- /*deduced-simd-t*/<T0>, wenn sizeof...(Ts) == 1 true ist
- andernfalls, std::common_type_t</*deduced-simd-t*/<T0>, /*deduced-simd-t*/<T1>>, wenn sizeof...(Ts) == 2 true ist und /*math-floating-point*/<T0> && /*math-floating-point*/<T1> true ist,
- andernfalls, std::common_type_t</*deduced-simd-t*/<T0>, T1>, wenn sizeof...(Ts) == 2 true ist und /*math-floating-point*/<T0> true ist,
- andernfalls, std::common_type_t<T0, /*deduced-simd-t*/<T1>>, wenn sizeof...(Ts) == 2 true ist,
- andernfalls, std::common_type_t</*math-common-simd-t*/<T0, T1>, TRest...>, wenn /*math-common-simd-t*/<T0, T1> ein gültiger Typ ist,
- andernfalls, 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> wird nur modelliert, wenn
-
BinaryOpeine binäre elementweise Operation ist, die kommutativ ist, und - Ein Objekt vom Typ
BinaryOpmit zwei Argumenten vom Typ std::basic_simd<T, Abi> für ein nicht spezifiziertes ABI-TagAbiaufrufbar ist und ein std::basic_simd<T, Abi> zurückgibt.
-
| SIMD ABI-Tags |
||
template< class T > using /*native-abi*/ = /* siehe Beschreibung */; |
(12) | (nur Exposition*) |
template< class T, /*simd-size-type*/ N > using /*deduce-abi-t*/ = /* siehe Beschreibung */; |
(13) | (nur Exposition*) |
- /*simd-size-v*/<T, /*deduce-abi-t*/<T, N>> gleich N ist,
- std::basic_simd<T, /*deduce-abi-t*/<T, N>> eine aktivierte Spezialisierung ist, und
- std::basic_simd_mask<sizeof(T), /*deduce-abi-t*/</*integer-from*/<sizeof(T)>, N>> eine aktivierte Spezialisierung ist.
T ein vektorisierbarer Typ ist und N > 0 && N <= M true ist, wobei M ein implementierungsdefinierter Maximalwert ist, der mindestens 64 beträgt und je nach T variieren kann.| Lade- und Speicherflags |
||
struct /*convert-flag*/; |
(14) | (nur Exposition*) |
struct /*aligned-flag*/; |
(15) | (nur Exposition*) |
template< std::size_t N > struct /*overaligned-flag*/; |
(16) | (nur Exposition*) |
std::simd_flags verwendet. Siehe Lade- und Speicherflags für ihre entsprechenden Verwendungen.[edit] Anmerkungen
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_lib_simd |
202411L |
(C++26) | Datentypen und Operationen für parallele Datenverarbeitung |
[edit] Beispiel
#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)); }
Ausgabe
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
[edit] Siehe auch
| numerische Arrays, Array-Masken und Array-Slices (Klassenvorlage) |
[edit] Externe Links
| 1. | Die Implementierung von ISO/IEC TS 19570:2018 Abschnitt 9 "Data-Parallel Types" — github.com |
| 2. | Erreichbarkeit der TS-Implementierung für GCC/libstdc++ (std::experimental::simd wird mit GCC-11 ausgeliefert) — gcc.gnu.org |