Wie unterscheiden sich firstprivate und lastprivate von privaten Klauseln in OpenMP?

76

Ich habe mir die offiziellen Definitionen angesehen, bin aber immer noch ziemlich verwirrt.

firstprivate: Gibt an, dass jeder Thread eine eigene Instanz einer Variablen haben soll und dass die Variable mit dem Wert der Variablen initialisiert werden soll, da sie vor dem parallelen Konstrukt vorhanden ist.

Für mich klingt das sehr nach privat. Ich habe nach Beispielen gesucht, aber ich scheine nicht zu verstehen, wie es besonders ist oder wie es verwendet werden kann.

lastprivate: Gibt an, dass die Version der Variablen des umschließenden Kontexts der privaten Version des Threads entspricht, der die letzte Iteration (For-Loop-Konstrukt) oder den letzten Abschnitt (# Pragma-Abschnitte) ausführt.

Ich habe das Gefühl, dass ich dies aufgrund des folgenden Beispiels etwas besser verstehe:

#pragma omp parallel
{
   #pragma omp for lastprivate(i)
      for (i=0; i<n-1; i++)
         a[i] = b[i] + b[i+1];
}
a[i]=b[i];

In diesem Beispiel verstehe ich also, dass lastprivatees möglich ist i, als letzter Wert außerhalb der Schleife zurückgegeben zu werden.

Ich habe heute gerade angefangen, OpenMP zu lernen.

SaiyanGirl
quelle

Antworten:

151

privateVariablen werden nicht initialisiert, dh sie beginnen wie jede andere lokale automatische Variable mit Zufallswerten (und werden häufig mithilfe automatischer Variablen auf dem Stapel jedes Threads implementiert). Nehmen Sie dieses einfache Programm als Beispiel:

#include <stdio.h>
#include <omp.h>

int main (void)
{
    int i = 10;

    #pragma omp parallel private(i)
    {
        printf("thread %d: i = %d\n", omp_get_thread_num(), i);
        i = 1000 + omp_get_thread_num();
    }

    printf("i = %d\n", i);

    return 0;
}

Mit vier Threads gibt es ungefähr Folgendes aus:

thread 0: i = 0
thread 3: i = 32717
thread 1: i = 32717
thread 2: i = 1
i = 10

(another run of the same program)

thread 2: i = 1
thread 1: i = 1
thread 0: i = 0
thread 3: i = 32657
i = 10

Dies zeigt deutlich, dass der Wert von iinnerhalb des parallelen Bereichs zufällig (nicht initialisiert) ist und dass Änderungen daran nach dem parallelen Bereich nicht sichtbar sind (dh die Variable behält ihren Wert vor dem Eintritt in den Bereich bei).

Wenn dies igemacht wird firstprivate, wird es mit dem Wert initialisiert, den es vor dem parallelen Bereich hat:

thread 2: i = 10
thread 0: i = 10
thread 3: i = 10
thread 1: i = 10
i = 10

Änderungen am Wert iinnerhalb des parallelen Bereichs sind danach nicht mehr sichtbar.

Sie wissen bereits Bescheid lastprivate(und es ist nicht auf das einfache Demonstrationsprogramm anwendbar, da es keine Worksharing-Konstrukte gibt).

Also ja, firstprivateund lastprivatesind nur Sonderfälle von private. Der erste führt dazu, dass Werte aus dem äußeren Kontext in den parallelen Bereich gebracht werden, während der zweite Werte aus dem parallelen Bereich in den äußeren Kontext überträgt. Das Grundprinzip dieser Datenfreigabeklassen besteht darin, dass innerhalb des parallelen Bereichs alle privaten Variablen diejenigen aus dem externen Kontext beschatten, dh es ist nicht möglich, eine Zuweisungsoperation zu verwenden, um den äußeren Wert ivon innerhalb des parallelen Bereichs zu ändern .

Hristo Iliev
quelle
1
Das ist eine großartige Antwort! Vielen Dank!
SaiyanGirl
1
Nun, ich würde sagen, firstprivateund lastprivatesind sehr spezielle Fälle von private. Normalerweise müssen Sie nicht einmal verwenden private(definieren Sie einfach die Variable innerhalb des parallelen Blocks), um möglicherweise den äußeren Bereich zu beschatten. Interessant ist die Verwendung von C ++ - Objekten als automatische firstprivateVariablen - sie werden einmal durch Kopierkonstruktion initialisiert und nach dem Block zerstört, und Sie müssen die Anzahl der Threads nicht im Voraus kennen - es werden so viele Kopien erstellt wie möglich erforderlich.
Tomasz Gandor
So ein tolles Beispiel! Mag es am meisten. Schätzen Sie Hristo.
Fusionmate
1
@TomaszGandor Für C ++ würde ich nicht zustimmen. Mit privatewird standardmäßig nur einmal pro Thread statt für jede Iteration erstellt. Besonders nützlich, wenn Speicher zugewiesen wird.
Barabas
firstprivate eignet sich auch zum effizienten Nachschlagen der Werte kleiner gemeinsam genutzter Variablen, die im parallelen Block nicht geändert werden - z. B. Parameterflags. Privater Speicher ist häufig schneller als gemeinsam genutzter Speicher. Wenn es sich nur um boolesche / logische Flags handelt, belegen die redundanten Kopien keinen nennenswerten Speicherplatz.
Astrokiwi
6

firstprivateund lastprivatesind nur Sonderfälle von private.

Der erste führt dazu, dass Werte aus dem äußeren Kontext in den parallelen Bereich gebracht werden, während der zweite Werte aus dem parallelen Bereich in den äußeren Kontext überträgt.

Amol Jain
quelle
Könnten Sie einen Hinweis darauf geben?
J. Chomel