2012-05-15 3 views
6

meiste Zeit ein Zweig in einer CUDA oder OpenCL Programm erforderlich ist, wie:CUDA/openCL; Umschreiben Verzweigungen als nicht-verzweigende Ausdruck

for (int i=0; i<width; i++) 
{ 
    if(i % threadIdx.x == 0) 
    quantity += i*i; 
} 

kann der Code immer (oder zumindest die meiste Zeit) umgeschrieben werden in nicht-Verzweigung Stil:

for (int i=0; i<width; i++) 
{ 
    quantity += i*i* (i % threadIdx.x != 0); 
} 

scheint der Kompromiss in einer einzigen Kette Schlitz wird entweder läuft im Vergleich zu mehr Berechnungen auf all Threads zu tun (im zweiten Fall wird die Summe ausgeführt wird, immer, nur, dass manchmal der Wert Null)

Unter der Annahme, dass Verzweigungsoperationen mehrere Warp-Slots für jeden möglichen Zweig benötigen, würde man erwarten, dass der zweite konsistent besser ist als der erste. Nun ist meine Frage; Kann ich mich auf den Compiler verlassen, um 1) in 2) zu optimieren, wann immer es Sinn macht, oder gibt es keine breit anwendbaren Kriterien, was bedeutet, dass man ohne Probieren nicht generell entscheiden kann, welches besser ist?

+0

Welche Reihenfolge ist Breite? Wenn Sie wissen, dass die Breite ziemlich groß ist, sollten Sie nicht durch eine for-Schleife gehen, um dies zu tun, da Sie wissen, welche Werte Sie verwenden werden. 'While (i 3Pi

Antwort

0

Ich habe nicht viele Erinnerungen an CUDA, aber warum parallelisieren Sie Ihre Schleife nicht? Sie sollten atomare Operationen [1] verwenden, um Ihre Berechnung hinzuzufügen. Ich hoffe, dies wird dir helfen! Tut mir leid, wenn das nicht der Fall ist.

  1. Atomic-Operationen: http://supercomputingblog.com/cuda/cuda-tutorial-4-atomic-operations/
+0

Dies ist in einem Kernel - jeder Thread führt die vollständige Schleife aus. Der Kommentar macht keinen Sinn. –

1

Nach meiner Erfahrung - es ist völlig bis zu den Compiler-Autoren diese Art von Grenzfällen zu optimieren.

Also kann ich an Fälle denken, in denen 1) nicht zu 2) gedreht werden kann? Hier ist einer: Ich habe Kernel geschrieben, wo es effizienter war, bestimmte Teile der Berechnungen alle 10 Threads oder etwas ähnliches auszuführen, in welchem ​​Fall eine solche Optimierung nicht abgeleitet werden kann, obwohl es eine mathematische Operation (Dividieren/Subtrahieren) gibt das gleiche Ergebnis unabhängig von bedingten versus "Lauf bei allen, aber null Ergebnisse".

Aber selbst wenn das Überprüfen auf threadId == 0 ein Szenario ist, das häufig genug ist, habe ich keine Ahnung, ob es tatsächlich optimiert ist. Ich wette, dass es auf die Implementierung und sogar das Gerät selbst (CPU vs GPU) abhängt.

Sie müssen es versuchen, um herauszufinden, was am besten funktioniert, nicht nur aus dem oben genannten Grund, sondern auch weil der Arbeitsplaner sich möglicherweise anders verhält, je nachdem, wie teuer es ist, eine Reihe von Threads zu planen/zu starten/zu stoppen im Gegensatz dazu, dass sie alle laufen (und die meisten ein Null-/Identitätsergebnis liefern).

Hoffe, das hilft!

+0

Also, in Ihrer Erfahrung, können Sie einige Empfehlungen in Bezug auf, wenn sollte ich immer versuchen, Code in Stil 2, unter der Annahme der schlimmsten Szenario zu schreiben? oder kann das unbeabsichtigte Folgen haben? – lurscher

+0

Ich kann nicht in allen Fällen den einen über den anderen rechtfertigen - das ist mein Punkt. Ich würde wahrscheinlich 1) verwenden, wenn ich etwas reduziere, was auf einem CPU-Gerät zu handhaben ist, aber 2) wenn ich wegen der Kosten für die Verzweigung auf älterer Hardware auf einer GPU war - wenn dieses Szenario gültig wäre. Die zu berücksichtigenden Faktoren sind - Gerätetyp, wie nichtparallel die Berechnung ist, ist es möglich, die Berechnung in mehrere Kerneln aufzuteilen (vielleicht die Reduzierung) und schließlich, wenn der Verzweigungsaufwand auf der gesamten Hardware für den ausgewählten Gerätetyp akzeptabel ist. Aber IMO, Experimente würden immer empfohlen werden. – Ani

+0

klar zu sein, ich spreche im speziellen Fall von GPU-Geräten, offensichtlich gibt es keinen Gewinn mit CPU, weil es eine Menge von Verzweigung Vorhersage und Pipelining hilft mit der Latenz versteckt – lurscher

3

Modulo-Operationen sind ziemlich teuer: Ich bin ziemlich sicher, dass das Hinzufügen des Modulo mehr Zeit in Anspruch nehmen würde als nur eine einzige Anweisung, die nur 1 Thread ausführt. Ihre einzige Branching-Anweisung, eine if ohne else, hängt nur die anderen Threads währenddessen, wenn die Statement ausgeführt wird. Da GPUs für sehr schnelle Kontextwechsel optimiert sind, sollten die Kosten dafür sehr gering sein.

Es wird jedoch davon abgeraten, lange Branching-Anweisungen zu verwenden: zu viel serielle Berechnung auf der GPU (dh ein Thread, der all die Arbeit erledigt) negiert den Vorteil der Parallelität.

+0

Wenn Sie nur den CUDA Best Programming Guide einchecken, wird Ihr Code für Branch Prediction einfach zu verwenden ist eine niedrige Priorität. Es gibt wichtigere Dinge, die allgemein optimiert werden müssen. – 3Pi

Verwandte Themen