class sequenced_policy { /* unspecified */ };
|
(1) |
(depuis C++17) |
class parallel_policy { /* unspecified */ };
|
(2) |
(depuis C++17) |
class parallel_unsequenced_policy { /* unspecified */ };
|
(3) |
(depuis C++17) |
class unsequenced_policy { /* unspecified */ };
|
(4) |
(depuis C++20) |
| | |
1) Type de politique d'exécution utilisé comme type unique pour lever l'ambiguïté de la surcharge des algorithmes parallèles et exiger que l'exécution d'un algorithme parallèle ne soit pas parallélisée. Les appels des fonctions d'accès aux éléments dans les algorithmes parallèles invoqués avec cette politique (généralement spécifiée comme std::execution::seq) sont séquencés de manière indéterminée dans le thread appelant.
2) Type de politique d'exécution utilisé comme type unique pour lever l'ambiguïté de la surcharge des algorithmes parallèles et indiquer que l'exécution d'un algorithme parallèle peut être parallélisée. Les appels des fonctions d'accès aux éléments dans les algorithmes parallèles invoqués avec cette politique (généralement spécifiée comme std::execution::par) sont autorisés à s'exécuter soit dans le thread appelant, soit dans un thread créé implicitement par la bibliothèque pour supporter l'exécution parallèle de l'algorithme. Tous ces appels s'exécutant dans le même thread sont séquencés de manière indéterminée les uns par rapport aux autres. Si les threads d'exécution créés par std::thread ou std::jthread fournissent des garanties de progression concurrente, alors les threads d'exécution créés par la bibliothèque fournissent des garanties de progression parallèle. Sinon, la garantie de progression fournie est définie par l'implémentation. Remarque : la garantie de progression parallèle assure que si un thread d'exécution fait un pas, il finira par en faire un autre, ce qui permet aux threads d'entrer dans des sections critiques et de prendre des verrous, car le thread qui détient le verrou sera finalement réordonnancé et pourra le libérer.
3) Type de politique d'exécution utilisé comme type unique pour lever l'ambiguïté de la surcharge des algorithmes parallèles et indiquer que l'exécution d'un algorithme parallèle peut être parallélisée, vectorisée ou migrée entre threads (par exemple par un ordonnanceur avec vol de parent). Les appels des fonctions d'accès aux éléments dans les algorithmes parallèles invoqués avec cette politique sont autorisés à s'exécuter de manière non ordonnée dans des threads non spécifiés, et sans séquencement les uns par rapport aux autres au sein de chaque thread. Les appels des fonctions d'accès aux éléments dans les algorithmes parallèles invoqués avec cette politique ne sont pas autorisés à invoquer des opérations non sécurisées pour la vectorisation, comme celles spécifiées par la bibliothèque standard pour se synchroniser, y compris celles de std::atomic et d'autres primitives de concurrence. Si les threads d'exécution créés par std::thread ou std::jthread fournissent des garanties de progression concurrente, alors les threads d'exécution créés par la bibliothèque fournissent des garanties de progression faiblement parallèle. Sinon, la garantie de progression fournie est celle du thread invoquant l'algorithme parallèle. Remarque : la garantie de progression faiblement parallèle assure que l'un des threads d'exécution qui a fait un pas finira par en faire un autre, ce qui ne permet pas aux threads d'entrer dans des sections critiques ou de prendre des verrous, car le thread qui détient le verrou peut ne pas être réordonnancé avant qu'un thread tentant de prendre le verrou ne soit sorti.
4) Type de politique d'exécution utilisé comme type unique pour lever l'ambiguïté de la surcharge des algorithmes parallèles et indiquer que l'exécution d'un algorithme parallèle peut être vectorisée, par exemple exécutée sur un seul thread en utilisant des instructions qui opèrent sur plusieurs éléments de données.
Pendant l'exécution d'un algorithme parallèle avec l'une de ces politiques d'exécution, si l'appel d'une fonction d'accès aux éléments se termine par une exception non capturée, std::terminate est appelé, mais les implémentations peuvent définir des politiques d'exécution supplémentaires qui traitent les exceptions différemment.
Remarques
Lors de l'utilisation d'une politique d'exécution parallèle, il est de la responsabilité du programmeur d'éviter les courses de données et les interblocages :
int a[] = {0, 1};
std::vector<int> v;
std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int i)
{
v.push_back(i * 2 + 1); // Error: data race
});
std::atomic<int> x {0};
int a[] = {1, 2};
std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int)
{
x.fetch_add(1, std::memory_order_relaxed);
while (x.load(std::memory_order_relaxed) == 1) { } // Error: assumes execution order
});
int x = 0;
std::mutex m;
int a[] = {1, 2};
std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int)
{
std::lock_guard<std::mutex> guard(m);
++x; // correct
});
Les politiques d'exécution non séquencées sont le seul cas où les appels de fonctions sont non séquencés les uns par rapport aux autres, ce qui signifie qu'ils peuvent être entrelacés. Dans toutes les autres situations en C++, ils sont indéterminément séquencés (ne peuvent pas être entrelacés). Pour cette raison, les utilisateurs ne sont pas autorisés à allouer ou désallouer de la mémoire, acquérir des mutex, utiliser des spécialisations non atomiques sans verrou de std::atomic, ou en général, effectuer des opérations non sécurisées pour la vectorisation lors de l'utilisation de ces politiques (les fonctions non sécurisées pour la vectorisation sont celles qui se synchronisent avec une autre fonction, par exemple std::mutex::unlock se synchronise avec le prochain std::mutex::lock).
int x = 0;
std::mutex m;
int a[] = {1, 2};
std::for_each(std::execution::par_unseq, std::begin(a), std::end(a), [&](int)
{
std::lock_guard<std::mutex> guard(m); // Error: lock_guard constructor calls m.lock()
++x;
});
Si l'implémentation ne peut pas paralléliser ou vectoriser (par exemple en raison d'un manque de ressources), toutes les politiques d'exécution standard peuvent revenir à une exécution séquentielle.
Voir aussi
(C++17)(C++17)(C++17)(C++20)
|
objets globaux de politique d'exécution (constante)
|