2017-01-30 9 views
0

Nach this link Verständnis, ich versuche, den Betrieb von Kernel-Code zu verstehen (es gibt 2 Versionen dieses Kernel-Code, eine mit volatile local float *source und das andere mit volatile global float *source, das heißt local und global Versionen). Unten Ich nehme local Version:das Verfahren zur OpenCL Reduktion auf float

float sum=0; 
void atomic_add_local(volatile local float *source, const float operand) { 
    union { 
     unsigned int intVal; 
     float floatVal; 
    } newVal; 

    union { 
     unsigned int intVal; 
     float floatVal; 
    } prevVal; 

    do { 
     prevVal.floatVal = *source; 
     newVal.floatVal = prevVal.floatVal + operand; 
    } while (atomic_cmpxchg((volatile local unsigned int *)source, prevVal.intVal, newVal.intVal) != prevVal.intVal); 
} 

Wenn ich gut verstehen, jede Arbeit Punkt Aktien der Zugriff auf source Variable dank der Qualifikation „volatile“, nicht wahr?

Danach, wenn ich ein Arbeitselement nehme, fügt der Code operand Wert zu newVal.floatVal Variable hinzu. Dann rufe ich nach dieser Operation die Funktion atomic_cmpxchg auf, die prüft, ob die vorherige Zuweisung (preVal.floatVal = *source; und newVal.floatVal = prevVal.floatVal + operand;) erfolgt ist, d. H. Durch Vergleichen des in der Adresse source gespeicherten Wertes mit der preVal.intVal.

Während dieser atomaren Operation (die nicht unter die Definition UNINTERRUPTIBLE wird), als Wert bei source gespeichert aus prevVal.intVal unterschiedlich ist, wird der neue Wert bei source gespeichert ist newVal.intVal, die eigentlich ein Schwimmer ist (weil es auf 4 Bytes codiert, wie ganze Zahl).

Können wir sagen, dass jedes Work-Item einen Mutex-Zugriff (ich meine einen gesperrten Zugriff) auf den Wert source address hat.

Aber für each work-item Thread, gibt es nur eine Iteration in die while loop?

Ich denke, es wird eine Iteration geben, weil der Vergleich "*source== prevVal.int ? newVal.intVal : newVal.intVal" immer newVal.intVal Wert auf Wert bei source address gespeichert wird, nicht wahr?

Jede Hilfe ist willkommen, weil ich nicht alle Feinheiten dieses Tricks für diesen Kernel-Code verstanden habe.

UPDATE 1:

Sorry, ich alle Subtilitäten fast verstehen, vor allem im while loop:

Ersten Fall: für einen bestimmten einzelnen Thread, vor dem Aufruf von atomic_cmpxchg, wenn prevVal.floatVal ist immer noch gleich *source, dann atomic_cmpxchg wird den Wert in source Zeiger ändern und den Wert in old pointer, die gleich prevVal.intVal enthält, zurückgegeben, so dass wir von derbrechen.

Zweiter Fall: Wenn zwischen derprevVal.floatVal = *source; Anweisung und dem Aufruf von atomic_cmpxchg, *source der Wert (von einem anderen Thread ??), dann kehrt atomic_cmpxchg old Wert, der nicht geändert hat prevVal.floatVal mehr gleich ist, so dass der Zustand in while loop ist wahr und wir bleiben in dieser Schleife, bis die vorherige Bedingung nicht mehr überprüft wird.

Meine Interpretation ist richtig?

Dank

+1

Sorry, wenn dies für Sie liegt auf der Hand (ich glaube, ich voll und ganz verstehe die Frage nicht noch) nicht, aber ... die 'while' Schleife ist eine Standardmethode Unteilbarkeit zu erreichen, wie von https: //en.wikipedia. org/wiki/compare-and-Swap – Marco13

+0

Dies ist eine klassische vergleichen Austauschschleife, wie von Marco erwähnt. Ignoriere die Gewerkschaftstricks aus Gründen der Übersichtlichkeit, sie sind nur hier für die Art Punning. Auch wenn Sie OpenCL 2+ haben, gibt es eingebaute Atomics für Floats. –

+0

: Marco13,: Aldwin ok, danke. Nehmen wir einen einfachen Fall mit 2 Threads. Wenn die erste in der while-Schleife ist, dann, bis die zweite ändert den Wert von „prevVal.floatVal“, die while-Schleife für den ersten Thread dauert, nicht wahr? Aber in diesem Fall ist die Operation der Inkrementierung "prevVal.floatVal + operand;" unendlich wird, und so gespeicherte Wert bei der Adresse „source“ (it, bis zweiten Gewindeanschlag) ist sehr hoch, weil ich mit einer sehr großen Anzahl von „Operanden“ Werten tue Summierung.Grüße – youpilat13

Antwort

1

Wenn ich gut verstehen, jede Arbeit Punkt teilt die Zugriffsvariablen dank der Qualifikation „volatile“, zu beziehen, nicht wahr?

volatile ist ein Schlüsselwort der C-Sprache, die von dem Compiler optimiert Zugriffe auf eine bestimmte Stelle im Speicher verhindert (in anderen Worten, an denen jeweils ein Lade-/Speicher-zwingt Lese-/Schreib des Speicherplatz). Es hat keinen Einfluss auf die Eigentümerschaft des zugrunde liegenden Speichers. Es wird hier verwendet, um den Compiler zu erzwingen bei jeder Schleifeniteration source aus dem Gedächtnis wieder lesen (sonst würde der Compiler gestattet wird, dass die Last außerhalb der Schleife zu bewegen, was den Algorithmus bricht).

do { 
    prevVal.floatVal = *source; // Force read, prevent hoisting outside loop. 
    newVal.floatVal = prevVal.floatVal + operand; 
} while(atomic_cmpxchg((volatile local unsigned int *)source, prevVal.intVal, newVal.intVal) != prevVal.intVal) 

Nach Qualifier (der Einfachheit halber) und Umbenennen von Parametern, die Signatur von atomic_cmpxchg ist die folgende Entfernung:

int atomic_cmpxchg(int *ptr, int expected, int new) 

Was sie tut, ist:

atomically { 
    int old = *ptr; 

    if (old == expected) { 
     *ptr = new; 
    } 

    return old; 
} 

Um es zusammenzufassen, jeder Faden einzeln tut:

  1. Laststromwert von *source aus dem Speicher in preVal.floatVal
  2. Compute gewünschter Wert von *source in newVal.floatVal
  3. Execute das Atom Vergleichen Austausch oben beschrieben (die Art-punned Werten)
  4. Wenn das Ergebnis des atomic_cmpxchg == newVal.intVal es bedeutet, der Vergleichsaustausch war erfolgreich, Pause. Andernfalls ist der Austausch nicht erfolgt, gehe zu 1 und versuche es erneut.

Die obige Schleife schließlich trennt, weil das schließlich gelingt es ihr jeder Faden atomic_cmpxchg dabei.

Können wir sagen, dass jedes Arbeitselement einen Mutex-Zugriff (ich meine einen gesperrten Zugriff) auf den Wert an der Quelladresse hat.

Mutexes sind Sperren, während dies ein Lock-Free-Algorithmus ist. OpenCL kann Mutexe mit Spinlocks simulieren (auch mit Atomics implementiert), aber das ist keiner.

+0

Wenn du sagst "Wenn das Ergebnis von atomic_cmpxchg == newVal.intVal, bedeutet dies, dass der Vergleich-Austausch erfolgreich war, breche" mit "do {} while (atomic_cmpxchg ((flüchtige lokale unsigned int *) Quelle, prevVal.intVal, newVal.intVal)! = prevVal.intVal) ", aber ich denke, dass wir stattdessen schreiben sollten:" do {} while (atomic_cmpxchg ((flüchtige lokale unsigned int *) Quelle, prevVal.intVal, newVal.intVal) == prevVal. intVal) "weil wir die while-Schleife unterbrechen, wenn das Ergebnis von atomic_cmpxchg newVal.intVal ist, wenn" * source == preVal.floatVal ", oder? – youpilat13

+0

Nein, atomic_cmpxchg gibt den * alten * Wert von '* ptr' zurück, wie oben dargestellt, daher möchten Sie brechen, wenn es gleich' prevVal' ist, weil es erfolgreich war. Mit dem, was Sie vorschlagen, würde die Schleife sofort beendet, wenn der Thread seinen atomaren Austausch nicht bestanden hat. –

+0

Ich denke ich habe verstanden, könntest du bitte meine Interpretation in meinem UPDATE 1 oben sehen? danke – youpilat13