openmp
Unregelmäßiger OpenMP-Parallelismus
Suche…
Bemerkungen
Eine häufige Gefahr besteht darin zu glauben, dass alle Threads einer parallelen Region Tasks instanziieren (erstellen) sollen. Dies ist jedoch normalerweise nicht der Fall, wenn Sie so viele Tasks wie die Anzahl der Threads und der Anzahl der zu verarbeitenden Elemente erstellen möchten. Daher finden Sie in OpenMP-Taskcodes etwas Ähnliches
#pragma omp parallel
#pragma omp single
...
#pragma omp task
{ code for a given task; }
...
Parallele Verarbeitung eines C ++ - Listencontainers mit OpenMP-Tasks
#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;
}
In diesem Beispiel wird die Verarbeitung einer STL-Liste (im Code als lst
) parallel durch die OpenMP-Task-Konstrukte #pragma omp task
(mithilfe der #pragma omp task
). Das Beispiel erstellt / instanziiert eine OpenMP-Task für jedes Element in lst
und die OpenMP-Threads führen die Tasks aus, sobald sie zur Ausführung bereit sind.
$ 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
Rekursive Berechnung für Pi mit OpenMP-Aufgaben
Der folgende Code berechnet den Wert von PI mit einem rekursiven Ansatz. MAX_PARALLEL_RECURSIVE_LEVEL
Wert MAX_PARALLEL_RECURSIVE_LEVEL
, um zu bestimmen, bei welcher Rekursionstiefe das Erstellen von Aufgaben gestoppt wird. Mit diesem Ansatz erstellen Sie Parallelität aus rekursiven Anwendungen: Je mehr Aufgaben Sie erstellen, desto mehr parallele Aufgaben werden erstellt, desto geringer ist jedoch auch die Arbeit pro Aufgabe. Daher ist es praktisch, mit der Anwendung zu experimentieren, um zu verstehen, auf welcher Ebene die Erstellung weiterer Aufgaben keinen Nutzen für die Leistung hat.
#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;
}