Zunächst sollten Sie sich der Tatsache bewusst sein, dass CUDA Berechnungen nicht automatisch schneller macht. Einerseits, weil die GPU-Programmierung eine Kunst ist und es sehr, sehr schwierig sein kann, sie richtig zu machen . Zum anderen, weil GPUs nur für bestimmte Arten von Berechnungen gut geeignet sind .
Dies mag verwirrend klingen, da Sie grundsätzlich alles auf der GPU berechnen können . Der entscheidende Punkt ist natürlich, ob Sie eine gute Beschleunigung erreichen oder nicht. Die wichtigste Klassifizierung hierbei ist, ob ein Problem aufgabenparallel oder datenparallel ist . Der erste bezieht sich grob gesagt auf Probleme, bei denen mehrere Threads mehr oder weniger unabhängig voneinander an ihren eigenen Aufgaben arbeiten. Der zweite bezieht sich auf Probleme, bei denen viele Threads alle dasselbe tun - jedoch auf verschiedenen Teilen der Daten.
Letzteres ist das Problem, bei dem GPUs gut sind: Sie haben viele Kerne, und alle Kerne tun dasselbe, arbeiten jedoch mit verschiedenen Teilen der Eingabedaten.
Sie haben erwähnt, dass Sie "einfache Mathematik, aber mit einer großen Datenmenge" haben. Obwohl dies wie ein perfekt datenparalleles Problem klingt und daher für eine GPU gut geeignet ist, ist noch ein weiterer Aspekt zu berücksichtigen: GPUs sind in Bezug auf die theoretische Rechenleistung (FLOPS, Gleitkommaoperationen pro Sekunde) lächerlich schnell. Sie werden jedoch häufig durch die Speicherbandbreite gedrosselt.
Dies führt zu einer weiteren Klassifizierung von Problemen. Nämlich, ob Probleme speicher- oder rechnergebunden sind .
Der erste bezieht sich auf Probleme, bei denen die Anzahl der Anweisungen, die für jedes Datenelement ausgeführt werden, gering ist. Betrachten Sie beispielsweise eine parallele Vektoraddition: Sie müssen zwei Datenelemente lesen , dann eine einzelne Addition durchführen und dann die Summe in den Ergebnisvektor schreiben . Wenn Sie dies auf der GPU tun, wird keine Beschleunigung angezeigt, da der einzelne Zusatz den Aufwand für das Lesen / Schreiben des Speichers nicht kompensiert.
Der zweite Begriff "rechnergebunden" bezieht sich auf Probleme, bei denen die Anzahl der Befehle im Vergleich zur Anzahl der Lese- / Schreibvorgänge im Speicher hoch ist. Betrachten Sie beispielsweise eine Matrixmultiplikation: Die Anzahl der Anweisungen ist O (n ^ 3), wenn n die Größe der Matrix ist. In diesem Fall kann man erwarten, dass die GPU eine CPU bei einer bestimmten Matrixgröße übertrifft. Ein anderes Beispiel könnte sein, wenn viele komplexe trigonometrische Berechnungen (Sinus / Cosinus usw.) an "wenigen" Datenelementen durchgeführt werden.
Als Faustregel gilt: Sie können davon ausgehen, dass das Lesen / Schreiben eines Datenelements aus dem "Haupt" -GPU-Speicher eine Latenz von ca. 500 Anweisungen hat ....
Ein weiterer wichtiger Punkt für die Leistung von GPUs ist daher die Datenlokalität : Wenn Sie Daten lesen oder schreiben müssen (und in den meisten Fällen müssen Sie ;-)), sollten Sie sicherstellen, dass die Daten so nah wie möglich gehalten werden möglich zu den GPU-Kernen. GPUs haben daher bestimmte Speicherbereiche (als "lokaler Speicher" oder "gemeinsam genutzter Speicher" bezeichnet), die normalerweise nur wenige KB groß sind, aber besonders effizient für Daten sind, die in eine Berechnung einbezogen werden sollen.
Um dies noch einmal zu betonen: Die GPU-Programmierung ist eine Kunst, die nur aus der Ferne mit der parallelen Programmierung auf der CPU zusammenhängt. Dinge wie Threads in Java, mit allen Gleichzeitigkeit Infrastruktur wie ThreadPoolExecutors
, ForkJoinPools
usw. könnte den Eindruck erwecken , dass Sie nur irgendwie Ihre Arbeit aufteilen müssen und es auf mehrere Prozessoren zu verteilen. Auf der GPU können Herausforderungen auf einer viel niedrigeren Ebene auftreten: Belegung, Registerdruck, Druck auf den gemeinsamen Speicher, Zusammenführen des Speichers ... um nur einige zu nennen.
Wenn Sie jedoch ein datenparalleles, rechengebundenes Problem lösen müssen, ist die GPU der richtige Weg.
Eine allgemeine Bemerkung: Sie haben speziell nach CUDA gefragt. Ich würde Ihnen jedoch dringend empfehlen, sich auch OpenCL anzuschauen. Es hat mehrere Vorteile. Erstens ist es ein herstellerunabhängiger, offener Industriestandard, und es gibt Implementierungen von OpenCL von AMD, Apple, Intel und NVIDIA. Darüber hinaus gibt es in der Java-Welt eine viel breitere Unterstützung für OpenCL. Der einzige Fall, in dem ich mich lieber mit CUDA zufrieden geben möchte, ist, wenn Sie die CUDA-Laufzeitbibliotheken wie CUFFT für FFT oder CUBLAS für BLAS (Matrix / Vector-Operationen) verwenden möchten. Obwohl es Ansätze gibt, ähnliche Bibliotheken für OpenCL bereitzustellen, können sie nicht direkt von Java-Seite verwendet werden, es sei denn, Sie erstellen Ihre eigenen JNI-Bindungen für diese Bibliotheken.
Vielleicht ist es auch interessant zu hören, dass die OpenJDK HotSpot-Gruppe im Oktober 2012 das Projekt "Sumatra" gestartet hat: http://openjdk.java.net/projects/sumatra/ . Ziel dieses Projekts ist es, GPU-Unterstützung direkt in der JVM mit Unterstützung der JIT bereitzustellen . Der aktuelle Status und die ersten Ergebnisse sind in der Mailingliste unter http://mail.openjdk.java.net/mailman/listinfo/sumatra-dev aufgeführt
Vor einiger Zeit habe ich jedoch einige Ressourcen gesammelt, die sich auf "Java auf der GPU" im Allgemeinen beziehen. Ich werde diese hier in keiner bestimmten Reihenfolge noch einmal zusammenfassen.
( Haftungsausschluss : Ich bin der Autor von http://jcuda.org/ und http://jocl.org/ )
(Byte-) Code-Übersetzung und OpenCL-Code-Generierung:
https://github.com/aparapi/aparapi : Eine Open-Source-Bibliothek, die von AMD erstellt und aktiv verwaltet wird. In einer speziellen "Kernel" -Klasse kann eine bestimmte Methode überschrieben werden, die parallel ausgeführt werden soll. Der Bytecode dieser Methode wird zur Laufzeit mit einem eigenen Bytecodeleser geladen. Der Code wird in OpenCL-Code übersetzt, der dann mit dem OpenCL-Compiler kompiliert wird. Das Ergebnis kann dann auf dem OpenCL-Gerät ausgeführt werden, bei dem es sich möglicherweise um eine GPU oder eine CPU handelt. Wenn die Kompilierung in OpenCL nicht möglich ist (oder kein OpenCL verfügbar ist), wird der Code weiterhin parallel mithilfe eines Thread-Pools ausgeführt.
https://github.com/pcpratts/rootbeer1 : Eine Open-Source-Bibliothek zum Konvertieren von Teilen von Java in CUDA-Programme. Es bietet dedizierte Schnittstellen, die implementiert werden können, um anzugeben, dass eine bestimmte Klasse auf der GPU ausgeführt werden soll. Im Gegensatz zu Aparapi wird versucht, die "relevanten" Daten (dh den gesamten relevanten Teil des Objektgraphen!) Automatisch in eine für die GPU geeignete Darstellung zu serialisieren.
https://code.google.com/archive/p/java-gpu/ : Eine Bibliothek zum Übersetzen von kommentiertem Java-Code (mit einigen Einschränkungen) in CUDA-Code, der dann in eine Bibliothek kompiliert wird, die den Code auf der GPU ausführt. Die Bibliothek wurde im Rahmen einer Doktorarbeit entwickelt, die fundierte Hintergrundinformationen zum Übersetzungsprozess enthält.
https://github.com/ochafik/ScalaCL : Scala-Bindungen für OpenCL. Ermöglicht die parallele Verarbeitung spezieller Scala-Sammlungen parallel zu OpenCL. Die Funktionen, die für die Elemente der Sammlungen aufgerufen werden, können übliche Scala-Funktionen (mit einigen Einschränkungen) sein, die dann in OpenCL-Kernel übersetzt werden.
Spracherweiterungen
http://www.ateji.com/px/index.html : Eine Spracherweiterung für Java, die parallele Konstrukte (z. B. Parallel for Loops, OpenMP-Stil) ermöglicht, die dann mit OpenCL auf der GPU ausgeführt werden. Leider wird dieses vielversprechende Projekt nicht mehr gepflegt.
http://www.habanero.rice.edu/Publications.html (JCUDA): Eine Bibliothek, die speziellen Java-Code (JCUDA-Code genannt) in Java- und CUDA-C-Code übersetzen kann, der dann kompiliert und auf dem ausgeführt werden kann GPU. Die Bibliothek scheint jedoch nicht öffentlich zugänglich zu sein.
https://www2.informatik.uni-erlangen.de/EN/research/JavaOpenMP/index.html : Java-Spracherweiterung für OpenMP-Konstrukte mit CUDA-Backend
Java OpenCL / CUDA-Bindungsbibliotheken
https://github.com/ochafik/JavaCL : Java-Bindungen für OpenCL: Eine objektorientierte OpenCL-Bibliothek, die auf automatisch generierten Low-Level-Bindungen basiert
http://jogamp.org/jocl/www/ : Java-Bindungen für OpenCL: Eine objektorientierte OpenCL-Bibliothek, die auf automatisch generierten Low-Level-Bindungen basiert
http://www.lwjgl.org/ : Java-Bindungen für OpenCL: Automatisch generierte Low-Level-Bindungen und objektorientierte Convenience-Klassen
http://jocl.org/ : Java-Bindungen für OpenCL: Low-Level-Bindungen, die eine 1: 1-Zuordnung der ursprünglichen OpenCL-API darstellen
http://jcuda.org/ : Java-Bindungen für CUDA: Low-Level-Bindungen, die eine 1: 1-Zuordnung der ursprünglichen CUDA-API darstellen
Verschiedenes
http://sourceforge.net/projects/jopencl/ : Java-Bindungen für OpenCL. Scheint seit 2010 nicht mehr gepflegt zu sein
http://www.hoopoe-cloud.com/ : Java-Bindungen für CUDA. Scheint nicht mehr gewartet zu werden