Ich habe zwei Anmerkungen zu Zbosons Antwort:
1. Methode 1 ist sicherlich korrekt, aber die Reduzierungsschleife wird tatsächlich seriell ausgeführt, wegen des #pragma omp critical, das natürlich notwendig ist, da die Teilmatrizen lokal für jeden Thread sind und die entsprechende Reduktion vom Thread durchgeführt werden muss, der die Matrix besitzt.
2. Methode 2: Die Initialisierungsschleife kann außerhalb des Single-Abschnitts verschoben werden und somit parallelisiert werden.
Das folgende Programm implementiert die Array-Reduktion unter Verwendung der OpenMP v4.0 User Defined Reduction-Funktionen:
/* Kompilieren mit:
gcc -Wall -fopenmp -o ar ar.c
Ausführen mit:
OMP_DISPLAY_ENV=TRUE OMP_NUM_THREADS=10 OMP_NESTED=TRUE ./ar
*/
#include
#include
struct m10x1 {int v[10];};
int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13};
struct m10x1 S = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
int n,m=0;
void print_m10x1(struct m10x1 x){
int i;
for(i=0;i<10;i++) printf("%d ",x.v[i]);
printf("\n");
}
struct m10x1 add_m10x1(struct m10x1 x,struct m10x1 y){
struct m10x1 r ={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
int i;
for (i=0;i<10;i++) r.v[i]=x.v[i]+y.v[i];
return r;
}
#pragma omp declare reduction(m10x1Add: struct m10x1: \
omp_out=add_m10x1(omp_out, omp_in)) initializer( \
omp_priv={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} )
int main ()
{
#pragma omp parallel for reduction(m10x1Add: S)
for ( n=0 ; n<10 ; ++n )
{
for (m=0; m<=n; ++m){
S.v[n] += A[m];
}
}
print_m10x1(S);
}
Dies folgt wortwörtlich dem Beispiel für die Reduktion von komplexen Zahlen auf Seite 97 von OpenMP 4.0 Features.
Obwohl die parallele Version korrekt funktioniert, gibt es wahrscheinlich Leistungsprobleme, die ich nicht untersucht habe:
- Die Eingaben und Ausgaben von add_m10x1 werden per Wert übergeben.
- Die Schleife in add_m10x1 wird sequentiell ausgeführt.
Diese "Leistungsprobleme" sind von mir selbst gemacht und es ist vollkommen unkompliziert, sie nicht einzuführen:
- Die Parameter für add_m10x1 sollten per Referenz übergeben werden (über Zeiger in C, Referenzen in C++)
- Die Berechnung in add_m10x1 sollte direkt durchgeführt werden.
- add_m10x1 sollte als void deklariert und die Rückgabeanweisung gelöscht werden. Das Ergebnis wird über den ersten Parameter zurückgegeben.
- Die declare reduction-Pragma sollte entsprechend geändert werden, der Kombinierer sollte nur ein Funktionsaufruf sein und keine Zuweisung (v4.0 Spezifikationen S. 181 Zeilen 9,10).
- Die for-Schleife in add_m10x1 kann über ein omp parallel for-Pragma parallelisiert werden.
- Verschachteltes Parallelisieren sollte aktiviert sein (z.B. über OMP_NESTED=TRUE)
Der modifizierte Teil des Codes sieht dann wie folgt aus:
void add_m10x1(struct m10x1 * x,struct m10x1 * y){
int i;
#pragma omp parallel for
for (i=0;i<10;i++) x->v[i] += y->v[i];
}
#pragma omp declare reduction(m10x1Add: struct m10x1: \
add_m10x1(&omp_out, &omp_in)) initializer( \
omp_priv={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} )