Sie könnten einen Producer-Consumer-Satz von Tasks haben, so dass Schritt 1 einen Puffer erzeugt und Schritt 2 diesen Puffer verbraucht, während die Ergebnisse in Ihr globales Array integriert werden.
So etwas (in der üblichen parallel
+ single
):
const int buffer_size = 100000;
type global[buffer_size];
type buffer[2][buffer_size];
for(int i=0;i<10000;i++)
{
type *buf[buffer_size] = buffer[i % 2];
#pragma omp task depend(out: [buffer_size]buf)
{
<1. write to buf>
}
#pragma omp task depend(in: [buffer_size]buf) depend(inout: [buffer_size]global)
{
<2. update global using buf>
}
}
Dieser Schritt 2 mit Schritt 1, mit einer Iteration der "Lag" zu überlappen können, ist, dass Schritt 1 der Iteration i = 1 wird Führen Sie gleichzeitig zur Iteration i = 0 von Schritt 2 aus. Sie benötigen einen Puffer pro überlappende Iteration, damit jede gleichzeitige Operation unabhängig voneinander ausgeführt werden kann.
Jetzt ist die Semantik von buf (in
/out
) einfach, da sie einfache Procoder-Verbraucher-Beziehungen sind. Für das Globale benötigen Sie inout
, während Sie den Puffer an Ort und Stelle aktualisieren: Holen Sie sich das aktuelle Array (in
) und weisen Sie ihm neue Werte zu (out
). Dies bedeutet, dass alle Aufgaben von Schritt 2 aufgrund dieser Abhängigkeit serialisiert werden, was bedeutet, dass höchstens 2 Iterationen überlappt werden können (und dass es keinen Sinn macht, mehr als 2 Puffer zu verwenden).
Dies ist eine Einschränkung der aktuellen OpenMP (4.5) Tasks, obwohl a proposal for task reductions existiert, die dies beheben würde. Eine alternative Möglichkeit wäre, global als shared
zu deklarieren, in diesem Fall müssen Sie es mit atomaren Operationen ändern.
Sie können eine target
Klausel verwenden, um Task 1 auf der GPU auszuführen. Stellen Sie sicher, dass die Daten vom ausgelagerten Gerät (d. H. Der GPU) in die CPU kopiert werden. Innerhalb der Aufgabe von Schritt 2 können Sie dann den Schritt 2 selbst parallelisieren, indem Sie entweder eine Parallele für das Konstrukt oder wieder Aufgaben verwenden.
Eigentlich brauche ich beide Dinge, machen Schritt 1 passieren parallel zu Schritt 2 und Multithread Schritt 2 auch, wenn möglich. Um s/w-Schritt 1 und 2 parallel zu machen, brauche ich eine Synchronisation, so dass Schritt 2 weiß, dass ein neuer Puffer erzeugt wird, damit er arbeiten kann, und Schritt 1 weiß, wann der alte Puffer überschrieben werden muss. In Schritt 2 wird Element für Element durch das 1M große Array geleitet, und wenn einige Bedingungen erfüllt sind, müssen Sie einige Berechnungen durchführen, um den geeigneten Speicherort im globalen Pufferarray zu finden, in den ich ihn einfügen muss . – udyank