openmp
Parallélisme OpenMP irrégulier
Recherche…
Remarques
Un écueil courant consiste à croire que tous les threads d'une région parallèle doivent instancier (créer) des tâches, mais ce n'est généralement pas le cas, sauf si vous souhaitez créer autant de tâches que le nombre de threads multiplié par le nombre d'éléments à traiter. Par conséquent, dans les codes de tâches OpenMP, vous trouverez quelque chose de similaire à
#pragma omp parallel
#pragma omp single
...
#pragma omp task
{ code for a given task; }
...
Traitement parallèle d'un conteneur de liste c ++ à l'aide des tâches OpenMP
#include <omp.h>
#include <unistd.h>
#include <iostream>
#include <list>
static void processElement (unsigned n)
{
// Tell who am I. The #pragma omp critical ensures that
// only one thread sends data to std::cout
#pragma omp critical
std::cout <<
"Thread " << omp_get_thread_num() << " processing element " << n
<< std::endl;
// Simulate some work
usleep (n*1000);
}
int main (void)
{
std::list<unsigned> lst;
// Fill the list
for (unsigned u = 0; u < 16; ++u)
lst.push_back (1+u);
// Now process each element of the list in parallel
#pragma omp parallel // Create a parallel region
#pragma omp single // Only one thread will instantiate tasks
{
for (auto element : lst)
{
#pragma omp task firstprivate (element)
processElement (element);
}
// Wait for all tasks to be finished
#pragma omp taskwait
}
return 0;
}
Cet exemple simule le traitement d'une liste STL (nommée lst
dans le code) en parallèle via les constructions de tâches OpenMP (à l'aide de la directive de #pragma omp task
). L'exemple crée / instancie une tâche OpenMP pour chaque élément de lst
et les threads OpenMP exécutent les tâches dès qu'ils sont prêts à être exécutés.
$ OMP_NUM_THREADS=4 ./a.out
Thread 0 processing element 16
Thread 3 processing element 3
Thread 2 processing element 1
Thread 1 processing element 2
Thread 2 processing element 4
Thread 1 processing element 5
Thread 3 processing element 6
Thread 2 processing element 7
Thread 1 processing element 8
Thread 3 processing element 9
Thread 2 processing element 10
Thread 1 processing element 11
Thread 0 processing element 15
Thread 3 processing element 12
Thread 2 processing element 13
Thread 1 processing element 14
Calcul récursif pour pi en utilisant les tâches OpenMP
Le code ci-dessous calcule la valeur de PI en utilisant une approche récursive. Modifiez la valeur MAX_PARALLEL_RECURSIVE_LEVEL
pour déterminer à quelle profondeur de récursivité cesser de créer des tâches. Avec cette approche pour créer du parallélisme à partir d'applications récursives: plus vous créez de tâches, plus les tâches créées sont parallèles, mais moins le travail est important par tâche. Il est donc pratique d’expérimenter l’application pour comprendre à quel niveau la création de tâches supplémentaires n’est pas rentable.
#include <stdio.h>
#include <omp.h>
double pi_r (double h, unsigned depth, unsigned maxdepth, unsigned long long begin, unsigned long long niters)
{
if (depth < maxdepth)
{
double area1, area2;
// Process first half
#pragma omp task shared(area1)
area1 = pi_r (h, depth+1, maxdepth, begin, niters/2-1);
// Process second half
#pragma omp task shared(area2)
area2 = pi_r (h, depth+1, maxdepth, begin+niters/2, niters/2);
#pragma omp taskwait
return area1+area2;
}
else
{
unsigned long long i;
double area = 0.0;
for (i = begin; i <= begin+niters; i++)
{
double x = h * (i - 0.5);
area += (4.0 / (1.0 + x*x));
}
return area;
}
}
double pi (unsigned long long niters)
{
double res;
double h = 1.0 / (double) niters;
#pragma omp parallel shared(res)
{
#define MAX_PARALLEL_RECURSIVE_LEVEL 4
#pragma omp single
res = pi_r (h, 0, MAX_PARALLEL_RECURSIVE_LEVEL, 1, niters);
}
return res * h;
}
int main (int argc, char *argv[])
{
#define NITERS (100*1000*1000ULL)
printf ("PI (w/%d iters) is %lf\n", NITERS, pi(NITERS));
return 0;
}