2017-12-08 9 views
0

Ich habe derzeit einen Code, der auf einem großen Datensatz funktioniert, ein Array am Ende generieren. Diesem Array muss dann ein globaler Puffer hinzugefügt werden, und das alles geschieht innerhalb einer Schleife. Z.B.Entwerfen eines Algorithmus mit Aufgaben in Openmp

for(i=0;i<10000;i++) 
{ <1. do some processing, generate a 1M-sized array> 
    <2. update this array into global buffer> 
} 

Die Aufgabe 1 auf der GPU passiert, und ich möchte das zweite Teil parallel auf CPU passieren, also dort, wo 1 Faden 1 die GPU für die Task steuert, und alle anderen Threads zu tun Aufgabe 2, wenn 1-Puffer bereit ist, zum Bearbeiten. Für einen grundlegenden Fall, wie kann ich dies mit einer Kopie eines Puffers tun (d. H. Einen auf der GPU-Seite und einen auf der CPU-Seite, um eine GPU-Kopie zu erhalten)?

+0

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

Antwort

0

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.

Verwandte Themen