C++
Concurrence avec OpenMP
Recherche…
Introduction
Cette rubrique couvre les bases de la concurrence en C ++ en utilisant OpenMP. OpenMP est documenté plus en détail dans la balise OpenMP .
Le parallélisme ou la simultanéité implique l'exécution de code en même temps.
Remarques
OpenMP ne nécessite pas d'en-tête ou de bibliothèque particulière, car il s'agit d'une fonctionnalité de compilateur intégrée. Toutefois, si vous utilisez des fonctions API OpenMP telles que omp_get_thread_num()
, vous devrez inclure omp.h
et sa bibliothèque.
Les instructions OpenMP pragma
sont ignorées lorsque l'option OpenMP n'est pas activée lors de la compilation. Vous voudrez peut-être faire référence à l'option du compilateur dans le manuel de votre compilateur.
- GCC utilise
-fopenmp
- Clang utilise
-fopenmp
- MSVC utilise
/openmp
OpenMP: Sections parallèles
Cet exemple illustre les bases de l'exécution de sections de code en parallèle.
Comme OpenMP est une fonctionnalité de compilateur intégrée, il fonctionne sur tous les compilateurs pris en charge sans inclure de bibliothèque. Vous souhaiterez peut-être inclure omp.h
si vous souhaitez utiliser l'une des fonctionnalités de l'API openMP.
Exemple de code
std::cout << "begin ";
// This pragma statement hints the compiler that the
// contents within the { } are to be executed in as
// parallel sections using openMP, the compiler will
// generate this chunk of code for parallel execution
#pragma omp parallel sections
{
// This pragma statement hints the compiler that
// this is a section that can be executed in parallel
// with other section, a single section will be executed
// by a single thread.
// Note that it is "section" as opposed to "sections" above
#pragma omp section
{
std::cout << "hello " << std::endl;
/** Do something **/
}
#pragma omp section
{
std::cout << "world " << std::endl;
/** Do something **/
}
}
// This line will not be executed until all the
// sections defined above terminates
std::cout << "end" << std::endl;
Les sorties
Cet exemple produit 2 sorties possibles et dépend du système d'exploitation et du matériel. La sortie illustre également un problème de condition de concurrence qui se produirait à partir d'une telle implémentation.
SORTIE A | SORTIE B |
---|---|
commence bonjour la fin du monde | commence le monde bonjour fin |
OpenMP: Sections parallèles
Cet exemple montre comment exécuter des morceaux de code en parallèle
std::cout << "begin ";
// Start of parallel sections
#pragma omp parallel sections
{
// Execute these sections in parallel
#pragma omp section
{
... do something ...
std::cout << "hello ";
}
#pragma omp section
{
... do something ...
std::cout << "world ";
}
#pragma omp section
{
... do something ...
std::cout << "forever ";
}
}
// end of parallel sections
std::cout << "end";
Sortie
- commence bonjour monde pour toujours fin
- commence le monde bonjour pour toujours fin
- commence bonjour fin du monde
- commencer à jamais bonjour fin du monde
Comme l'ordre d'exécution n'est pas garanti, vous pouvez observer l'une des sorties ci-dessus.
OpenMP: Parallel For Loop
Cet exemple montre comment diviser une boucle en parties égales et les exécuter en parallèle.
// Splits element vector into element.size() / Thread Qty
// and allocate that range for each thread.
#pragma omp parallel for
for (size_t i = 0; i < element.size(); ++i)
element[i] = ...
// Example Allocation (100 element per thread)
// Thread 1 : 0 ~ 99
// Thread 2 : 100 ~ 199
// Thread 2 : 200 ~ 299
// ...
// Continue process
// Only when all threads completed their allocated
// loop job
...
* Veuillez prendre des précautions supplémentaires pour ne pas modifier la taille du vecteur utilisé en parallèle pour les boucles car les index de plage alloués ne sont pas mis à jour automatiquement .
OpenMP: collecte / réduction en parallèle
Cet exemple illustre un concept permettant d'effectuer une réduction ou une collecte à l'aide de std::vector
et OpenMP.
Supposons que nous ayons un scénario où nous voulons que plusieurs threads nous aident à générer un tas de choses, int
est utilisé ici pour simplifier et peut être remplacé par d'autres types de données.
Ceci est particulièrement utile lorsque vous devez fusionner des résultats provenant d'esclaves pour éviter les erreurs de segmentation ou les violations d'accès à la mémoire et que vous ne souhaitez pas utiliser de bibliothèques ou de bibliothèques de conteneurs de synchronisation personnalisées.
// The Master vector
// We want a vector of results gathered from slave threads
std::vector<int> Master;
// Hint the compiler to parallelize this { } of code
// with all available threads (usually the same as logical processor qty)
#pragma omp parallel
{
// In this area, you can write any code you want for each
// slave thread, in this case a vector to hold each of their results
// We don't have to worry about how many threads were spawn or if we need
// to repeat this declaration or not.
std::vector<int> Slave;
// Tell the compiler to use all threads allocated for this parallel region
// to perform this loop in parts. Actual load appx = 1000000 / Thread Qty
// The nowait keyword tells the compiler that the slave threads don't
// have to wait for all other slaves to finish this for loop job
#pragma omp for nowait
for (size_t i = 0; i < 1000000; ++i
{
/* Do something */
....
Slave.push_back(...);
}
// Slaves that finished their part of the job
// will perform this thread by thread one at a time
// critical section ensures that only 0 or 1 thread performs
// the { } at any time
#pragma omp critical
{
// Merge slave into master
// use move iterators instead, avoid copy unless
// you want to use it for something else after this section
Master.insert(Master.end(),
std::make_move_iterator(Slave.begin()),
std::make_move_iterator(Slave.end()));
}
}
// Have fun with Master vector
...