GPU-Hardware hat zwei besondere Stärken: Raw Computing (FLOPs) und Speicherbandbreite. Die schwierigsten Rechenprobleme fallen in eine dieser beiden Kategorien. Beispielsweise liegt die Dichte der linearen Algebra (A * B = C oder Lösen [Ax = y] oder Diagonalisieren [A] usw.) abhängig von der Systemgröße irgendwo im Spektrum der Berechnungs- / Speicherbandbreite. Schnelle Fourier-Transformationen (FFT) erfüllen auch diese Anforderungen an die Gesamtbandbreite. Wie andere Transformationen, Gitter- / Gitter-basierte Algorithmen, Monte Carlo usw. Wenn Sie sich die NVIDIA SDK- Codebeispiele ansehen , können Sie ein Gefühl für die Art von Problemen bekommen, die am häufigsten angesprochen werden.
Ich denke, die lehrreichere Antwort ist die Frage: "In welchen Problemen sind GPUs wirklich schlecht?" Die meisten Probleme, die nicht in diese Kategorie fallen, können auf der GPU ausgeführt werden, obwohl einige mehr Aufwand erfordern als andere.
Probleme, die sich nicht gut abbilden lassen, sind im Allgemeinen zu klein oder zu unvorhersehbar. Bei sehr kleinen Problemen fehlt die Parallelität, die erforderlich ist, um alle Threads auf der GPU zu verwenden, und / oder sie können in einen Cache auf niedriger Ebene auf der CPU passen, wodurch die CPU-Leistung erheblich gesteigert wird. Unvorhersehbare Probleme weisen zu viele wichtige Verzweigungen auf, die ein effizientes Streaming von Daten vom GPU-Speicher zu den Kernen verhindern oder die Parallelität verringern können, indem das SIMD- Paradigma gebrochen wird (siehe „ Abweichende Verzerrungen “). Beispiele für solche Probleme sind:
- Die meisten Grafikalgorithmen (zu unvorhersehbar, besonders im Speicherbereich)
- Sparsame lineare Algebra (aber das ist auch schlecht für die CPU)
- Kleine Signalverarbeitungsprobleme (z. B. FFTs mit weniger als 1000 Punkten)
- Suche
- Sortieren
__synchtreads()
. B. mit ).Probleme, die eine hohe arithmetische Intensität und regelmäßige Speicherzugriffsmuster aufweisen, sind in der Regel auf GPUs leicht (ier) zu implementieren und funktionieren auf diesen gut.
Die grundlegende Schwierigkeit bei der Verwendung von Hochleistungs-GPU-Code besteht darin, dass Sie über eine Tonne Kerne verfügen und möchten, dass alle Kerne so weit wie möglich ausgenutzt werden. Probleme mit unregelmäßigen Speicherzugriffsmustern oder nicht hoher Rechenintensität erschweren dies: Entweder verbringen Sie viel Zeit damit, Ergebnisse zu kommunizieren, oder Sie verbringen viel Zeit damit, Daten aus dem Speicher abzurufen (was langsam ist!), Und nicht genug Zeit, um Zahlen zu berechnen. Natürlich ist das Potenzial für Parallelität in Ihrem Code von entscheidender Bedeutung, damit er auch auf einer GPU gut implementiert werden kann.
quelle
Dies ist nicht als eigenständige Antwort gedacht, sondern als Ergänzung zu den anderen Antworten von maxhutch und Reid.Atcheson .
Um das Beste aus GPUs herauszuholen, muss Ihr Problem nicht nur hoch (oder massiv) parallel sein, sondern auch der Kernalgorithmus, der auf der GPU ausgeführt wird, sollte so klein wie möglich sein. In OpenCL- Begriffen wird dies meist als Kernel bezeichnet .
Genauer gesagt sollte der Kernel in das Register jeder Multiprozessor-Einheit (oder Recheneinheit ) der GPU passen . Die genaue Größe des Registers ist abhängig von der GPU.
Da der Kernel klein genug ist, müssen die Rohdaten des Problems in den lokalen Speicher der GPU passen (gelesen: lokaler Speicher (OpenCL) oder gemeinsamer Speicher (CUDA) einer Recheneinheit). Andernfalls ist selbst die hohe Speicherbandbreite der GPU nicht schnell genug, um die Verarbeitungselemente ständig zu beschäftigen.
Normalerweise ist dieser Speicher etwa 16 bis 32 KiByte groß .
quelle
Wahrscheinlich eine technischere Ergänzung zu den vorherigen Antworten: CUDA-GPUs (dh Nvidia) können als eine Reihe von Prozessoren beschrieben werden, die autonom mit jeweils 32 Threads arbeiten. Die Threads in jedem Prozessor arbeiten im Lock-Step (denken Sie an SIMD mit Vektoren der Länge 32).
Obwohl die verlockendste Art, mit GPUs zu arbeiten, darin besteht, vorzutäuschen, dass absolut alles im Gleichschritt abläuft, ist dies nicht immer die effizienteste Art, Dinge zu tun.
Wenn Ihr Code nicht ordnungsgemäß / automatisch mit Hunderten / Tausenden von Threads parallelisiert wird, können Sie ihn möglicherweise in einzelne asynchrone Tasks aufteilen, die sich gut parallelisieren lassen, und diejenigen mit nur 32 Threads ausführen, die im Sperrschritt ausgeführt werden. CUDA bietet eine Reihe atomarer Anweisungen, mit denen Mutexe implementiert werden können, die es den Prozessoren wiederum ermöglichen, sich untereinander zu synchronisieren und eine Liste von Aufgaben in einem Thread-Pool- Paradigma zu verarbeiten. Ihr Code würde dann genauso funktionieren wie auf einem Multi-Core-System. Denken Sie jedoch daran, dass jeder Core dann 32 Threads für sich hat.
Hier ist ein kleines Beispiel mit CUDA, wie dies funktioniert
Sie müssen dann den Kernel mit aufrufen
main<<<N,32>>>(tasks,nr_tasks)
, um sicherzustellen, dass jeder Block nur 32 Threads enthält und somit in eine einzelne Verzerrung passt. In diesem Beispiel habe ich der Einfachheit halber auch angenommen, dass die Aufgaben keine Abhängigkeiten haben (z. B. hängt eine Aufgabe von den Ergebnissen einer anderen ab) oder Konflikte (z. B. Arbeiten mit demselben globalen Speicher). Wenn dies der Fall ist, wird die Aufgabenauswahl etwas komplizierter, aber der Aufbau ist im Wesentlichen der gleiche.Dies ist natürlich komplizierter als nur die Durchführung einer großen Anzahl von Zellen, erweitert jedoch die Art der Probleme, für die GPUs verwendet werden können, erheblich.
quelle
Ein Punkt, der bisher nicht erwähnt wurde, ist, dass die aktuelle Generation von GPUs bei Gleitkommaberechnungen mit doppelter Genauigkeit nicht so gut abschneidet wie bei Berechnungen mit einfacher Genauigkeit. Wenn Ihre Berechnungen mit doppelter Genauigkeit ausgeführt werden müssen, können Sie davon ausgehen, dass sich die Laufzeit gegenüber der einfachen Genauigkeit um den Faktor 10 erhöht.
quelle
Aus metaphorischer Sicht kann die GPU als eine Person gesehen werden, die auf einem Nagelbett liegt. Die Person, die oben liegt, enthält die Daten und in der Basis jedes Nagels befindet sich ein Prozessor. Der Nagel ist also ein Pfeil, der vom Prozessor zum Speicher zeigt. Alle Nägel haben ein regelmäßiges Muster wie ein Gitter. Wenn der Körper gut verteilt ist, fühlt er sich gut an (Leistung ist gut), wenn der Körper nur einige Stellen des Nagelbettes berührt, sind die Schmerzen schlecht (schlechte Leistung).
Dies kann als ergänzende Antwort zu den hervorragenden Antworten oben verstanden werden.
quelle
Alte Frage, aber ich denke, dass diese Antwort von 2014 - bezogen auf statistische Methoden, aber verallgemeinerbar für jeden, der weiß, was eine Schleife ist - besonders anschaulich und informativ ist.
quelle
GPUs haben lange Latenzzeiten für E / A, daher müssen viele Threads verwendet werden, um den Speicher zu überlasten. Um einen Warp beschäftigt zu halten, sind viele Threads erforderlich. Wenn der Codepfad 10 Takte und die E / A-Latenz 320 Takte beträgt, sollten 32 Threads der Sättigung des Warps nahe kommen. Wenn der Codepfad 5 Takte beträgt, verdoppeln Sie die Threads.
Suchen Sie mit tausend Kernen nach Tausenden von Threads, um die GPU voll auszunutzen.
Der Speicherzugriff erfolgt über eine Cache-Zeile, normalerweise 32 Byte. Das Laden eines Bytes ist mit 32 Bytes vergleichbar. Verschmelzen Sie den Speicher, um die Nutzungslokalität zu erhöhen.
Es gibt viele Register und einen lokalen RAM für jeden Warp, so dass die Nachbarn sich teilen können.
Näherungssimulationen großer Mengen sollten sich gut optimieren lassen.
Zufällige I / O und Single Threading ist ein Vergnügen ...
quelle
Stellen Sie sich ein Problem vor, das mit viel roher Gewalt gelöst werden kann, wie z. B. Travelling Salesman. Stellen Sie sich vor, Sie haben Server-Racks mit jeweils 8 verspielten Grafikkarten und jede Karte hat 3000 CUDA-Kerne.
Lösen Sie einfach ALLE möglichen Routen des Verkäufers und sortieren Sie sie nach Zeit / Entfernung / Metrik. Sicher, Sie werfen fast 100% Ihrer Arbeit weg, aber manchmal ist rohe Gewalt eine praktikable Lösung.
quelle
Wenn ich viele technische Ideen studiere, würde ich sagen, dass eine GPU eine Form der Fokussierung von Aufgaben, des Speichermanagements, der wiederholbaren Berechnung ist.
Viele Formeln sind zwar einfach zu schreiben, aber schwierig zu berechnen. In der Matrixmathematik erhalten Sie nicht eine einzige Antwort, sondern viele Werte.
Dies ist wichtig, wenn Sie berechnen, wie schnell ein Computer Werte berechnet und Formeln ausführt, da einige Formeln nicht ohne alle berechneten Werte ausgeführt werden können (daher langsamer). Ein Computer weiß nicht genau, in welcher Reihenfolge Formeln ausgeführt oder Werte berechnet werden müssen, um sie in diesen Programmen zu verwenden. Es zwingt sich hauptsächlich mit hoher Geschwindigkeit durch und zerlegt Formeln in Spannfutter, um zu berechnen, aber viele Programme erfordern heutzutage diese berechneten Spannfutter und warten in Ques (und Ques of Ques und mehr Ques of Ques).
Zum Beispiel in einem Simulationsspiel, das zuerst bei Kollisionen den Schaden der Kollision, die Position der Objekte, die neue Geschwindigkeit berechnet werden soll? Wie viel Zeit sollte das dauern? Wie kann eine CPU mit dieser Last umgehen? Außerdem sind die meisten Programme sehr abstrakt und erfordern mehr Zeit für die Datenverarbeitung. Sie sind nicht immer für Multithreading ausgelegt oder bieten in abstrakten Programmen keine guten Möglichkeiten, dies effektiv zu tun.
Als die CPU immer besser wurde, wurden die Leute beim Programmieren schlampig und wir mussten auch für viele verschiedene Computertypen programmieren. Eine GPU ist so konzipiert, dass sie durch viele einfache Berechnungen gleichzeitig brachiale Gewalt ausübt (Speicher (sekundär / RAM) und Heizung, Kühlung sind die wichtigsten Engpässe beim Rechnen). Eine CPU verwaltet viele viele Fragen gleichzeitig oder wird in viele Richtungen gezogen, um herauszufinden, was zu tun ist, wenn man nicht in der Lage ist, dies zu tun. (Hey, es ist fast menschlich)
Eine GPU ist Grunzarbeiter die mühsame Arbeit. Eine CPU verwaltet das gesamte Chaos und kann nicht mit jedem Detail umgehen.
Also, was lernen wir? Eine GPU erledigt mühsame Detailarbeiten auf einmal und eine CPU ist eine Multitask-Maschine, die sich nicht sehr gut auf zu viele Aufgaben konzentrieren kann. (Es ist wie es gleichzeitig Aufmerksamkeitsstörung und Autismus hat).
Engineering gibt es die Ideen, Design, Realität und viel Grunzarbeit.
Wenn ich gehe, denke daran, einfach zu beginnen, schnell zu beginnen, schnell zu scheitern, schnell zu scheitern und nie aufzuhören, es zu versuchen.
quelle