openmp
Parallelismo OpenMP irregolare
Ricerca…
Osservazioni
Un errore comune è quello di credere che tutti i thread di una regione parallela debbano creare istanze (creare) attività, ma questo non è in genere il caso, a meno che non si desideri creare un numero di attività pari al numero di thread per il numero di elementi da elaborare. Pertanto, nei codici di attività di OpenMP troverai qualcosa di simile a
#pragma omp parallel
#pragma omp single
...
#pragma omp task
{ code for a given task; }
...
Elaborazione parallela di un contenitore dell'elenco c ++ che utilizza le attività 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;
}
Questo esempio simula l'elaborazione di un elenco AWL (denominato lst
nel codice) in parallelo tramite i costrutti di attività OpenMP (utilizzando la direttiva #pragma omp task
). L'esempio crea / crea un'istanza di un'attività OpenMP per ogni elemento in lst
e i thread OpenMP eseguono le attività non appena sono pronte per l'esecuzione.
$ 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
Calcolo ricorsivo per pi usando le attività OpenMP
Il codice sottostante calcola il valore di PI usando un approccio ricorsivo. Modificare il valore MAX_PARALLEL_RECURSIVE_LEVEL
per determinare a quale profondità di ricorsione interrompere la creazione di attività. Con questo approccio per creare il parallelismo con le applicazioni ricorsive: più attività create, più attività parallele create ma anche meno lavoro per attività. Quindi è conveniente sperimentare con l'applicazione per capire a quale livello la creazione di ulteriori compiti non avvantaggia in termini di prestazioni.
#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;
}