We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Parallelisierung von Algorithmen für die Semantische Suche mit CUDA

00:00

Formale Metadaten

Titel
Parallelisierung von Algorithmen für die Semantische Suche mit CUDA
Serientitel
Anzahl der Teile
62
Autor
Lizenz
CC-Namensnennung 4.0 International:
Sie dürfen das Werk bzw. den Inhalt zu jedem legalen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen.
Identifikatoren
Herausgeber
Erscheinungsjahr
Sprache

Inhaltliche Metadaten

Fachgebiet
Genre
Abstract
Ein Anwendungsbeispiel für massive Parallelisierung mit CUDA für die semantische Suche in Multimediadatenbanken.
Schlagwörter
9
Vorschaubild
57:49
44
45
48
Vorschaubild
45:29
60
Vorschaubild
1:00:03
SoftwareHypermediaVimAlgorithmusAlgorithmusParallelisierungDatenverarbeitungssystemVorlesung/KonferenzComputeranimation
ParallelisierungDatenbankProzessorVorlesung/Konferenz
ProzessorBefehl <Informatik>SchnittstelleVorlesung/Konferenz
UMLQuantisierung <Physik>GraphikprozessorGraphAMD <Marke>BefehlsprozessorOpen SourceBewegungApple <Marke>CoprozessorSoftwareMultimediaMultimediaHardwareMittelungsverfahrenApple <Marke>DatenverarbeitungssystemSmartphoneDivergente ReiheBroadcastingverfahrenPhysikalische GrößeProzessorSoftwareentwicklungVorlesung/Konferenz
MetadatenMaschinelles LernenDetektionAlgorithmusComputeranimation
InformationRechenzentrumGraphMultimediaInformation RetrievalGerichteter GraphInformation RetrievalMultimediaTyp <Informatik>RichtungVorlesung/KonferenzComputeranimationTechnische ZeichnungFlussdiagramm
SoftwareCodeGraphDatenbankMatrizenringData DictionaryMathematikTyp <Informatik>KanteComputeranimation
SoftwareTaskGraphBerechnungREDUCE <Programm>GraphTaskRetrievalspracheMikroarchitekturMatrizenringElement <Mathematik>SchnittmengeBerechnungTyp <Informatik>UngleichungMetrisches SystemÄhnlichkeitssucheCodeCodierungDurchschnitt <Mengenlehre>Technische ZeichnungDiagramm
SoftwareEinfache GenauigkeitMehrkernprozessorAnwendungssoftwareParallelisierungSystems <München>BerechnungMikroarchitekturDatenverarbeitungssystemParalleler Algorithmus
GraphikprozessorWorld Wide WebAdditionMatrizenmultiplikationSoftwareProgrammierungEinfache GenauigkeitLineare AlgebraProzessfähigkeit <Qualitätsmanagement>HauptspeicherProgrammierungAnbindung <Informatik>GraphikprozessorShader <Informatik>ProgrammiergerätKerndarstellungSpeicherabzugDesktopProzessor
ComputeralgebraWorld Wide WebSoftwareProgrammierungKerndarstellungAlgorithmusSoftwareAlgebraisch abgeschlossener KörperC++HardwareCUDA <Informatik>Thread
GraphikprozessorCodeKernel <Informatik>BefehlsprozessorGroßrechnerSoftwareIOstreamCodeGroßrechnerKernel <Informatik>ProgrammierungGraphikkarte
CompilerCompilerKlasse <Mathematik>CodeCUDA <Informatik>C-CompilerCMakeSchreib-Lese-Kopf
IOstreamSoftwareGruppoidLängeInnerer PunktParametersystemVariablep-BlockRichtungCodeZahlenbereichAddition
Kernel <Informatik>GraphikprozessorSoftwareKonfigurationsraumThreadProzessorSchedulingp-BlockAdditionSpeicherabzugThreadComputeranimation
SoftwareARCHIVE <Programm>Physikalische GrößeMengeRAMHauptspeicherAlgorithmusNormalvektorp-BlockMatrizenringSynchronisierungProzess <Physik>Matrix <Mathematik>ZugriffParametersystemLängeArithmetikSpeicherverwaltungThreadGraph
LaufzeitBefehlsprozessorParallelisierungAlgorithmusTaskGraphikprozessorDatensatzSoftwareModenTOUR <Programm>KerndarstellungSpeicherabzugPunktInformationsmodellierungGrößenordnungDatenstrukturSpeicherverwaltungLaufzeitThreadAlgorithmusFunktion <Mathematik>ProzessorMengeParallelisierungElement <Mathematik>Physikalische GrößeComputeranimation
Drahtloses lokales NetzSoftwareWorld Wide WebZellularer AutomatC++MomentenproblemMatrizenringVorlesung/Konferenz
SoftwarePlatteAbfrageEditorData DictionaryMatrizenringComputeranimation
SoftwareMEGAWorld Wide WebCode variabler LängeAlgorithmusParallelisierungTaskBefehlsprozessorMengeInverser LimesCUDA <Informatik>RechenschieberMatrix <Mathematik>p-BlockRAMInformationSupremum <Mathematik>IntelThreadComputeranimation
JSONXMLUML
Transkript: Deutsch(automatisch erzeugt)
Ja, hallo zusammen. Schön, dass wir hier zusammenkommen können. Ich hatte eigentlich versucht mit dem Titel, versucht den Award für den längsten Talk-Titel hier zu gewinnen, aber ich habe im Programm gesehen, da habe ich schlechte Karten, da sind noch viel längere drin.
Was steckt so drin? Parallelisierung von Algorithmen für die semantische Suche mit CUDA. Also Parallelisierung, semantische Suche, der Use-Case und CUDA, die Technologie, die verwendet wird. Ich fand das schon immer sehr spannend, als ich mich so angefangen habe mit Computern zu beschäftigen,
diese Rechenpower zu haben und so weiter und viel berechnen zu können, parallel große Rechner, die stärksten Prozessoren und so weiter, was man so hatte. Als ich dann im letzten Jahr auf der Suche nach einem Thema für meine Masterarbeit war,
kam eben das Thema Parallelisierung zur Beschleunigung von großen Rechenaufgaben gerade richtig. Das passte einfach zusammen, das möchte ich euch heute vorstellen, was ich da im Rahmen der Arbeit erarbeitet habe. Ich entschuldige mich jetzt schon mal, es sind teilweise ein bisschen stark theoretische Teile drin,
aber das kriegen wir durch, auch wenn es heute schon ein bisschen spät ist. Ich möchte einmal kurz vorstellen, was eigentlich das Problem ist oder das Problemfeld, in dem ich mich dort bewegt habe. Wie ich dann halt eben die Parallelisierung angewendet habe und eben entsprechend halt CUDA bzw. GPUs für die Lösung genutzt habe.
Natürlich auch die Ergebnisse vorstellen. Vielleicht noch mal ein kleiner Exkurs vorweg. Bei uns in der Firma, in der Tarent arbeiten wir hauptsächlich mit Business-Applikationen. Da baut man ja heutzutage Microservices, man liest mal ein paar Daten aus der Datenbank oder so was und gibt die dann in irgendeiner Schnittstelle vorne raus.
Das sind ja noch relativ langweilige oder wenig rechenintensive Datenaufgaben. Aber es gibt natürlich immerhin und wieder den Fall, dass man halt doch mal ein Thema hat, wo man einfach schnell rechnen muss. Was halt irgendwie mit einer normalen Applikation einfach nicht so möglich ist. Und da haben sich jetzt einfach in der letzten Zeit ja sehr viele CPU- oder Prozessortechnologien halt auch aufgetan,
da es halt einfach mehrfach das Problem gibt, solche Probleme zu lösen. Das haben wir halt in der Vergangenheit, so CPU-Extensions. MMX kennt vielleicht noch der eine oder andere, aber AVX oder wie sie alle heißen, SGX, AIS und so weiter sind so Technologien, die ja als Co-Prozessoren oder als Teil des Befehls von modernen Prozessoren sind.
Man kennt heute Multicore-CPUs. Das ist ja halt das, was eigentlich in jedem System verbaut ist, auch in Smartphones oder in kleinen Computern wie in Raspberry Pi. Aber auch da muss man ja halt, um das wirklich auszunutzen, schon spezielle Sachen verwenden.
Dann das Thema für heute, Graphical Processing Units. Das ist gerade halt im Bereich 3D-Entwicklung ja groß geworden, damit jetzt durch Blockchain- und AI-Themen auch immer wichtiger geworden. Gerade jetzt aber auch im Bereich AI mit Tensor Cores, das ja nochmal wieder eine spezielle Gattung ist. Einfach halt was, was den immensen Rechenbedarf dort halt eben befriedigen soll.
Ein bisschen ein Sonderthema sind Quantencomputer, aber auch die sind ja halt mittlerweile verfügbar, also ein D-Wave-Computer. Das ist eine Variante davon, ist halt auch im Amazon-AWS-Bracket einfach direkt buchbar und nutzbar. Und auch das ist halt eigentlich allgemein verfügbar.
Und halt eben entsprechend für mathematisch harte Probleme auch das Mittel der Wahl. Dann gibt es auch viele Varianten, dass man diese FPGAs verwendet, field programmable gateway arrays, wo man halt eben quasi die Hardware auch so zusammenstückeln kann, um halt das optimal für das Problem zusammenzubauen. Viel im Audiobereich oder Encoding, Transcoding, solche Themen halt irgendwo verwendet.
Auch das gibt es auch in der AWS Cloud einfach zur Verfügung. Das heißt, man kann das halt auch verwenden. Und eben wenn man halt so ein Problem vor sich hat in der Softwareentwicklung auch dafür nutzen. Apple brauche ich jetzt auch nicht zu erwähnen, mit einem sehr speziellen extra für ihr systemdesignten Prozessoren M1
oder jetzt mittlerweile in M2 mit sehr viel für ProRes-Encoding, für Co-Prozessoren, für FaceID oder eben für auch die KI-Berechnung zur Beschleunigung derer.
Das ist halt alles so ein Thema. Ich glaube deswegen ist es schon wichtig, sich damit auch zu beschäftigen und halt auch bei anderen Problemen, die man so hat, halt mal sowas in Erwähnung zu ziehen, das dafür zu optimieren. Gut, aber eigentlich geht es jetzt darum, eine Herausforderung zu lösen im Bereich Multimedia Information Retrieval.
Und zwar eben halt die semantische Suche oder die Suche in großen Multimedia Bibliotheken. Man kann sich vorstellen, da gibt es natürlich halt viele große Collections an Multimedia Bibliotheken. Ich habe hier mal ein Beispiel von einem Random Broadcaster rausgesucht. Der hat natürlich halt sehr viele Serien und Filme und sonst irgendwas halt produziert und hat irgendwo einen gigantischen Archiv. Und wenn der jetzt mal nochmal wissen muss, die und die Person in der und der Szene, was ist denn da gewesen?
Was haben wir da an Material? Dann ist das natürlich halt schwierig, das alles mal kurz durchzugucken und dort wiederzufinden. Meistens gehen die dann nochmal hin und filmen das nochmal neu, weil das einfacher ist. Dann kann man natürlich mit KI wunderbar diese Sachen automatisch analysieren lassen. Das ist jetzt mal so ein Beispiel, wo so eine Prediction ist, was da gerade für eine Stimmung halt, so eine Mood Detection von den einzelnen Leuten.
Und da kann man halt natürlich verschiedenste Machine Learning Algorithmen auf sowas drauf loslassen. Dann habe ich halt einen großen Informationsbasis zu den Videos, für jede Szene dann halt eben entsprechende Metadaten.
Und um die halt sinnvollerweise zu organisieren, verwendet man halt im Multimedia Information Retrieval eben gerichtete Graphen. In dem ich dann halt eben sowas abbilde. Ich habe mir zwei Personen zum Beispiel, die Jane und den Jim. Die haben irgendwo halt eine Beziehung zueinander hier, die ist jetzt in beide Richtungen gerichtet, das ist Attached.
Von Jane weiß man halt irgendwo durch die Erkennung, das ist vom Typ Frau. Hört hier ein bisschen komisch an, aber ihr wisst, was ich meine. Das ist auch Typ Person. Man sieht hier vielleicht noch auf dem Kopf, dass da auf dem Kopf halt ein Hut ist und dass der Hut eben auf dem Kopf sitzt und sowas. Das sind halt Informationen, die man dann halt so zusammenbringen kann und in einen semantischen Graphen oder in einen Knowledge Graphen halt eben überführen kann.
Und so organisieren kann, dass man sie durchsuchbar machen kann. Das Beispiel hier ist dann halt eben der Multimedia Feature Vector Graph. Und solche Graphen kann man dann halt eben wunderbar in Grafdatenbanken abspeichern. Neo4j als Beispiel. Aber da gibt es auch morgen nochmal einen Vortrag dazu von dem Hersteller der
ArangoDB, der auch nochmal erzählt, wie man dort mit Graphen in der Datenbank arbeiten kann. Und das ist auch alles schön gut. Ich kann da halt solche Beispiele, also solche Suchen durchführen. Gib mir ähnliche Bilder wie dieses, wo ich dann aussuche, das ist auch ein Mann und eine Frau und irgendwie ein Hut involviert oder sowas.
Oder halt eine bestimmte Farbe oder ein bestimmter Ort. Ich kann halt auch Recommendations machen halt. Also wenn das halt gesucht ist, dann das und das ist halt so ähnlich. Oder da ist wiederum halt die Person drauf oder sowas. Das kann man halt wunderbar machen für so Use Cases, so wie auch für Thumbnail, für A-B-Testing. Dann halt was kommt beim Kunden besser an oder sowas.
Dann kann man halt sagen, gib mir mal Bilder wie die, dann kann ich die halt reinwerfen und dann sehen, was irgendwo mehr geklickt wird oder sowas. Und solche Grafdatenbanken, wie gesagt, die können das halt gut. Aber solche Grafoperationen, die sind dort auch sehr langsam. Langsam im Vergleich, dass ich, wenn ich jetzt halt so 100.000 Medienelemente habe und dann da einen entsprechenden Graf dazu habe,
dann dauert es halt auch schon mal irgendwie mal fünf, zehn Sekunden oder sowas, bis ich da bestimmte Ergebnisse habe. Das kann halt in bestimmten Situationen schon blöd sein. Gibt es den Punkt, das zu beschleunigen mit Grafcodes. Das ist halt im Rahmen der, im Lehrgebiet entwickelt worden. Und der Ansatz ist halt folgendes.
Ich habe eben hier meinen ganz großen gerichteten Grafen. Und für ein bestimmtes Szenario oder eine Szene oder ein Bild habe ich eben hier meinen Grafen zu dieser Szene. Und den kann ich halt eben mathematisch eine Matrix umwandeln über eine Operation, die heißt Stellvertreter Matrix sozusagen. Das heißt, ich habe hier einen gerichteten Grafen mit bestimmten Typen an den Kanten.
Für jeden Knoten kann ich dann sozusagen hier in der Matrix eben den Knoten halt zuordnen und kann halt die Beziehung untereinander halt eben mit einer 1 besetzen innerhalb der Matrix, um zu sagen, da gibt es halt eine Verknüpfung zwischen den beiden Knoten. Und wenn ich dann halt eben noch einen Wert dahinsetze und nicht nur eine 1 verwende,
dann habe ich halt eben auch den Typen dort abgebildet. Mathematisch sieht das dann halt eben eher so wie hier unten rechts aus, dass ich dann halt eben ja wirklich eine Matrix habe und programmatisch habe ich dann vielleicht noch ein Dictionary dazu, was mir halt eben die Items, eben die Knoten halt noch dazu eingibt.
Und dadurch, dass ich dann halt eine Matrix habe, kann ich halt eben mit einer Matrixoperation oder so etwas bestimmte Sachen halt eben wesentlich schneller lösen, als das eine Grafdatenbank machen kann. Und eine Operation dazu ist halt eben die Ähnlichkeitssuche oder Recommendation oder die ähnliche Typen innerhalb der Matrix.
Und das ist halt quasi der Algorithmus, den ich dann halt verwenden, den ich parallelisiert habe. Der funktioniert im Groben eigentlich so, dass ich halt drei Metrikwerte berechne. Wir sind hier mit Metrik Feature, Metrik Feature Relationship und Metrik Relationship Type betitelt. Jetzt brauchen wir jetzt gar nicht so genau wissen, was da im Einzelnen passiert.
Aber das ist im Prinzip, dass ich in der einen Matrix schaue, was ist dort, sind da dieselben Typen drin. Wenn ja, haben die denselben Wert und dann kann ich die halt eben zusammenzählen, kann da halt quasi einen Durchschnitt bilden. Und dann habe ich halt eben einen Wert, eine Metrik dazu. Und die kann ich dann halt sortieren und sagen halt, was dann halt ähnliches ist.
Und das funktioniert natürlich so, dass ich das halt für jede quasi zwei Elemente in der Collection halt mache. Das heißt, ich habe also meinen Query Graphen, dann schaue ich halt eben nach, welche Elemente habe ich dort in der Collection, berechne für jedes halt eben dann zueinander den Metrikwert, habe dann am Ende halt meine Ergebnisliste,
die kann ich dann halt nochmal nach der Wertigkeit sortieren und habe dann halt mein Result Set. Und wie hier schon angedeutet ist, kann ich das halt eben schon parallelisieren, dass ich natürlich diese Metrik Berechnungen für diese zwei Elemente parallel mache. Das kann man jetzt hier in meinem Task Graphen sehen. Ich habe halt meinen Query Graphen, ich habe meine Collection mit den ganzen Graph Codes
und dann nehme ich halt eben meinen Query für jede Berechnung, dann eben entsprechend ein Element aus der Collection, berechne die Metriken. Und das kann man halt eben hier parallel machen. Also diesen Schritt hier kann ich halt quasi, wenn ich genug Prozessor-Elemente habe, einfach parallel komplett durchführen.
Einen Schritt weiter gedacht kann ich natürlich auch sagen, ist halt hier dieser Algorithmus, der schaut halt für jeden Wert innerhalb der Matrix. Jetzt gibt es das halt auch in dem Vergleichs Graph Code. Und wenn ja, ist halt der Wert besetzt, also steht da eine 1 oder etwas höher wäre es als 0 drin. Und dann der dritte Metrikwert ist halt eben, ist da halt der Typ identisch.
Ist auch gar nicht so wichtig, eher relevant ist halt eben für den Algorithmus, dass ich natürlich sagen kann, wenn ich das für jedes Element innerhalb der Matrix machen kann, kann ich das natürlich halt auch wieder parallel durchführen. Das heißt, da habe ich den zweiten Ansatz, das parallel zu machen. Das heißt, in Task Graphen sich dann halt hier, ich habe immer die Metrikberechnung,
dann kann ich für jedes Feld in der Query Matrix halt eben nochmal diese Vergleichsoperation machen, bekomme mein Ergebnis, das kann ich mir irgendwo inzwischen speichern, kann das nochmal reduzieren. Das kann man halt parallelisieren, wenn man halt genug Elemente hat. Wenn ich jetzt eine Matrix mit 6 Elementen habe, dann lohnt sich das nicht zu parallelisieren.
Aber wenn ich jetzt so eine Matrix habe mit mehreren Millionen Elementen, dann wiederum schon. Und zu guter Letzt kann man halt auch die Sortierung auch parallel durchführen. So, das mal halt zu dem Thema, mit dem ich mich halt beschäftigt habe. Das war sozusagen die Ausgangslage bzw. der Ansatz, was man halt hier parallelisieren kann. Und man sieht halt eben, dass es sehr gut parallelisierbar.
Das heißt, ich kann halt sehr viele, wenn ich sehr viele Prozessorkerne habe, kann ich das halt auch in sehr vielen Schritten parallel machen. Und da bieten sich halt eben bestimmte Architekturen an. Schauen wir mal drauf, was es halt für parallele Compute Architekturen gibt. Da gibt es hier Flint's Taxonomie. Da teilt es eben ein in Single Instruction Single Data bzw. Single Instruction Multiple Data.
Und eben andersrum Multiple Instruction Single Data, Multiple Instruction Multiple Data. Das hier, SISD, ist quasi das, was man halt so als Standard-Einkernprozess so hat. So ein 486er oder so einer der ersten Pentium-Generationen. Das sind so die typischen, die haben einen Instruktionscounter,
laden Daten in den Speicher, führen eine Operation auf die Daten im Speicher aus und so weiter geht es dann halt durch. Die MISD, das ist im Prinzip dann, dass ich das halt parallel mache, trotzdem auf denselben Daten. Das wird häufig verwendet z.B. in der Raumfahrt oder irgendwo, wo ich halt sehr genaue Berechnungen haben will, wo ich nicht will, dass irgendwie der Kurs von so einer Raumfähre halt irgendwie durch einen Bitkipper oder so was
halt da mal so ein bisschen am Zielpunkt vorbeigeht. Da kann man halt eben sich selbst kontrollierende Systeme halt bauen. Das ist aber nicht interessant für die Parallelisierung. Da sind eigentlich hier die beiden halt spannend. Das heißt, ich führe eine Operation aus auf mehrere Dateneinheiten zur selben Zeit. Das ist halt typischerweise das, was GPUs machen.
Und wir haben hier die Multiple Instruction Multiple Data. Das heißt, ich werde halt mehrere Instruction-Counter haben oder Instruction-Listen. Das ist halt typischerweise beim Mehrkernprozessor in der Fall. Da habe ich zwei Applikationen, die haben ihre Instruktionslisten und zwei Prozessorkerne können die halt irgendwo abarbeiten. Das ist gut für Multipurpose Computing auch.
Also wenn ich da irgendwas habe, ich mehrere Anwendungen habe, kann ich das halt machen. Aber wenn ich jetzt halt besonders viel parallelisieren will, eignet sich dieses Prinzip hier eher, weil es sich besser organisieren lässt. Deswegen sind halt hier diese Mehrkernprozessoren. Hat man ja üblicherweise auch weniger Recheneinheiten oder so was. Also hat man im Desktop vielleicht acht Cores oder von mir aus zwölf oder sechzehn oder so was.
Im Serverbereich knapp unter 200. Aber halt eben hier im GPU-Bereich, da haben wir halt schon mehrere tausend bis hoch zu, ich glaube, das Top-Modell von NVIDIA hat gut 40.000 Kern in einer GPU.
Ich kann halt mehrere in einem System haben. Aber wie funktioniert das überhaupt auf einer GPU, auf einem Grafikprozessor zu programmieren? Es ist ein Grafikprozessor an sich, der ermöglicht das eigentlich gar nicht. Aber wenn ich jetzt halt eben eine GPU habe, also sprich eine, die mir ermöglicht dort halt auch eigene Programme zu platzieren,
typischerweise für die 3D-Bearbeitung. Wenn ich dort Shader halt eben programmieren kann, dann kann ich halt eben auch dort einfach, ja, Shader sind auch nur Programme, das ist auch nur Code, der ausgeführt werden kann, sondern eben auf der CPU, da habe ich spezielle Datentypen, habe ich nicht dasselbe Environment, wie ich das jetzt bei einer normalen Programmierung habe, aber das geht eben.
Es sind SIMD-Prozessoren und sie ermöglichen halt eben, dass man halt massiv parallel etwas ausführt, weil wenn ich da halt irgendwo 4.000 Recheneinheiten oder 15, 20, 40.000 habe, muss ich die natürlich auch auslasten, dafür sind sie gebaut, also gibt es auch Wege, das zu tun. Funktioniert super für lineare Algebra, Vektoradditionen und Matrix-Multiplikationen.
Deswegen, dass wir halt auch viel im KI-Bereich halt eben, dass Machine Learning-Modelle das halt eben verwenden. Limitiert ist man durch PCI Express durch die Größe des Memories auf der GPU, weil ich muss da mal die Daten erstmal halt vom Hauptspeicher oder sonst wo dahinschieben und PCI Express ist halt eben nicht so schnell wie jetzt die Anbindung halt direkt von dem Memory an die CPU.
Und oft ist halt auch der Speicher dort nicht wahnsinnig groß, also der Hauptspeicher ist typischerweise größer. Man hat dann, wie ist das aufgebaut, wir haben halt hier so eine GPU, das ist jetzt eine Nvidia-Architektur, diese G80G92 oder die GT200, da haben wir hier diese gelben Boxen,
das sind Shared Streaming-Multiprozessoren, das heißt das ist eine Prozessoreinheit, quasi wie ein Kern, der hat allerdings halt, also kann Instruktionen ausführen, der hat aber halt eben hier noch diese Stream-Prozessoren, die dann halt eben diese eine Operation auf mehrere Daten durchführen.
Das heißt also hier ein Prozessor ist dann in der Lage mit den acht Kern dieselbe Instruktion auf acht unterschiedliche Datenbereiche halt durchzuführen. Und davon kann man halt natürlich mehrere haben, das ist so die erste Generation, da gab es dann zwei davon, dann gab es mal drei und heute sind wir halt irgendwo bei so einer größeren Architektur, wo man einfach sehr, sehr viele Kerne hat.
Das Schöne ist halt eben, dass die halt alle halt auch separat sind, das heißt also man kann sie halt auch so schedulen, dass man sie halt nicht immer, dass man mal nur eine Operation macht, sondern man hat auch Threads und andere Varianten, um das halt noch ein bisschen effizienter zu machen. Dann kommen wir mal zu CUDA, was ist da noch das Besondere? CUDA ist halt eben eine proprietäre Technologie von Nvidia,
Hardware und Software-Architektur zusammen, das heißt also das funktioniert einfach von der Entwicklung in die Hardware zusammen, da hat man ein Hardware-Scheduler, den man halt eben aus der Software dann zwar ansteuern kann und sowas, aber das ist halt sehr eng miteinander verwoben. Das ist ja 2007 veröffentlicht worden und ja, es ermöglicht einfach
viele Varianten, dass man halt eben auf der GPU Datenverarbeitung durchführen kann. Was kann man, wie kann man das programmieren? Das war natürlich für mich dann die Frage, ja ich muss jetzt halt meinen Algorithmus mal halt entwickeln und da bin ich dann halt eben da hingekommen und hab gedacht, geil, was wir mit Java machen oder Golang oder Python oder irgendeinen anderen coolen Programmiersprache, nein.
Es ist leider nur mit C und C++ Extensions möglich oder das ist das, was CUDA halt anbietet. Natürlich gibt es halt irgendwo Wrapper, dass man mit Java, JNI, diese Sachen halt trotzdem anschließen kann, aber ja, man muss durch die Hardschule, wenn man da gerade am Anfang halt rein will, das mit C und C++ zu machen.
Witzig ist ja, ich hab ja gerade gesagt, Fun Fact sozusagen, das ist 2007 veröffentlicht worden, da hab ich meine Diplomarbeit geschrieben und in dem Zeitpunkt, da hab ich auch schon mit C gearbeitet und da hab ich eigentlich gesagt, so Gott sei Dank, nach dem Abschluss der Arbeit, da bin ich fertig mit,
da brauch ich nicht mehr wieder hin, jetzt kann mir das in meiner Masterarbeit dann doch nochmal wieder rauf, aber gut, hat irgendwie dann doch geklappt. So schwer ist es mir nämlich gar nicht. Wie macht man nämlich so eine Programmierung einer GPU? Prinzipiell hab ich so etwas wie einen Kernel und das ist halt einfach eine Codefunktion, die auf der GPU ausgeführt wird. Das ist halt isolierter Stück Code, das wird halt eben dort ausgeführt.
Man hat dann im Code immer zwei Sachen, sozusagen das Device und den Host. Das Device ist dann halt eben das, quasi die GPU, auf der der Code dann ausgeführt wird und eben der Host, das ist dann halt eben das, was auf der CPU ausgeführt wird. Zwischen den Geräten muss ich dann natürlich halt eben Daten austauschen, hat ich ja gesagt.
Also ich muss halt die Daten, damit sie halt bearbeitet werden können, in die GPU, in die Grafikkarte laden. Und das mach ich dann natürlich mit klassischer Memoryverwaltung. Memory allocation, memory copy, Speicher wieder frei geben. Also eben halt, wenn ich eine Funktion ausführen möchte, Daten in den Speicher laden, also erstmal allokieren, dann transferieren,
also die Funktion durchführen, das Ergebnis zurück transferieren, den Speicher wieder frei geben. Kann zu lustigen Seiteneffekten führen, kennt man vielleicht. Die wichtigen Statements sind hier einmal nochmal global und device, die sozusagen halt angeben, wo welche Funktion ausgeführt wird. Ich hab ja gesagt, das sind halt eben quasi C extensions und so hab ich halt eben hier so eine Kernelfunktion.
Und wenn ich dann halt hier diese extension, dieses underscore underscore global underscore underscore reinschreibe, dann signalisier ich damit diese Funktion ist dafür gedacht, dass sie halt eben auf der GPU ausgeführt wird. Ansonsten hab ich halt hier so typisch meine Mainfunktion, dann kann ich halt hier diese Funktion aufrufen.
Und ja, kann ich noch ein Hello World ausgeben, muss ich aber nicht, aber das ist halt schon ein erstes Programm, was sozusagen halt eben für eine GPU kompiliert werden kann. Und diese Werte, die sind hier natürlich noch ganz spannend, die geben nämlich nachher an, wie oft sozusagen, wie viel parallel das halt eben ausgeführt wird.
Aber so tief will ich eigentlich gar nicht einsteigen. Schauen wir erstmal dran, ich hab jetzt diesen Code, da haben wir ja schon gesehen, das sind halt extensions, die der C-Compiler erstmal nicht umsetzen kann. Deswegen hat CUDA halt einen eigenen Compiler, der halt eben NVCC heißt und der setzt eben auf GCC auf. Kann halt auch in dem entsprechenden Tooling verwendet werden.
Dann gibt's nochmal noch spezielle Extensions, also File Extensions, die dann halt eben signalisieren, dass da halt CUDA Code drin ist. So hab ich, verwende ich dann halt eben den Compiler halt für die CUDA Klassen oder für die CUDA Header. Und das Ganze wird aber auch schön von CMake unterstützt, das heißt also, man kann das halt auch in seine bestehenden C Tooling halt auch irgendwo einbinden.
Das ist aber wie immer bei CU mit diesen ganzen Sachen, mit Makefiles und so weiter und so fort nicht so einfach. Gut, das ist dann das Hello World, das kann ich konquilieren und ausführen, aber es tut ja noch nichts. Und da wird's jetzt halt eben erst spannend, wenn wir halt mal schauen, wie das eben funktioniert, wenn ich das mache.
Ich hab hier mal eine Funktion, um halt zwei Zahlen zu addieren. Da muss ich dann hier entsprechend halt drei Parameter hinzugeben. Und zwar muss ich halt eben sagen, halt einmal A, das sind halt die beiden Operanten für die Addition. Und halt eben das Ergebnis, wo es halt reingeschrieben wird. Und ja, diese Sachen muss ich dann halt eben bei mir lokal einmal vorhalten.
Das heißt, ich muss einmal sagen, es gibt ein entsprechendes Wert für C und device C. Das ist dann nachher das, wo ich das Ergebnis dann reinlade. Dann muss ich das eben auf der GPU allokieren, diesen Speicherbereich. Muss angeben, welche Größe dieser Speicherbereich hat.
Und dann kann ich diese Addition halt durchführen. Ich übergebe jetzt zwei und sieben mit und lasse das Ergebnis halt in der FC reinschreiben. Und dann verwende ich Memory Copy, um das Ganze halt eben aus dem Device Pointer in den lokalen Pointer zu schreiben. Das ist hier die Richtung Copy Device to Host.
Und dann wird halt quasi das Ergebnis, was im Speicher der GPU ist, zurückgeschrieben. Ja, und dann kann ich das Ergebnis halt wieder lokal in meinem Code verwenden und ausgeben oder was auch immer. Richtiger Punkt natürlich. Ich muss den Speicher wieder freigeben, ansonsten steht er mir nicht mehr zur Verfügung.
Jetzt haben wir immer noch nichts parallel gemacht. Das ist jetzt einfach nur eine Addition, die halt auf der GPU funktioniert. Und da kommt jetzt sozusagen die Magic rein, dass ich hier Parameter angeben kann. Ich hatte vorher mal jetzt eins eins drin stehen, jetzt habe ich halt fünf eins. Und das kann ich halt eben verwenden, um zu sagen, das soll jetzt quasi in einem Block von fünf ausgeführt werden.
Und da kommen halt solche wieder Magic Variablen da rein, die hier irgendwie vorher nicht deklariert wurden. Das sind quasi Variablen, die der CUDA Compiler halt dort mit eingibt. Je nachdem, wie dies gescheduled ist im Programm auf der GPU, haben diese Parameter halt Werte.
Also da kann ich dann halt sehen, zur Laufzeit, wenn diese Funktion ausgeführt wird, kann ich halt eben hier ein X reinschreiben. Das wird dann halt fünfmal passieren. Und dann steht halt in dem einen X eine Eins, in dem anderen zwei, drei, vier und fünf und so weiter. Und so sehe ich dann, was ich dann halt hier mache.
Und so habe ich dann halt mehrere Ergebnisse. Die schreibe ich dann natürlich nicht mehr in eine einzelne Variable, sondern halt in einen Array. Und dort steht dann halt eben drin, je nachdem, was habe ich hier, zwei plus sieben plus null. Ich fange natürlich mit null anzuzählen. Also haben wir entsprechend einen neun.
In der nächsten Funktionsaufruf steht eine eins drin. Dann komme ich halt bei einer zehn raus und so weiter und so fort. Und dementsprechend, wenn ich dann hier noch anpasse, dass ich halt eben fünfmal die Größe von Int habe. Dann kann ich halt eben durch das Ergebnis in C iterieren. Das ist jetzt hier ein Array von der Länge fünf und kann mir halt die Ergebnisse ausgeben.
Ja, und so kann ich dann halt anfangen, halt eben zu sagen, was ich wie oft ausgeführt haben möchte. Und wenn ich jetzt eine Matrix-Multiplikation habe, dann kann ich das eben entsprechend anpassen. Diese Block-Indexe sind dreidimensional. Das heißt, ich habe ein X, Y und ein Z-Wert.
Das führt jetzt vielleicht ein bisschen zu weit für den Vortrag. Aber da kann ich dann natürlich halt unterschiedlichste Operationen mit ausfinden. Das sind unterschiedlichste Varianten nutzen. Wie funktioniert sowas dann? Also ich habe auch auf der GPU sowas wie Threads auf der CPU.
Da kann ich halt eben, die sind organisiert in Blöcken. Ein Block kann halt maximal 1024 Threads enthalten. Das ist so eine Vorgabe, die halt Nvidia an der Stelle gemacht hat. Und da kann ich dann halt eben jetzt so meine Addition halt durchführen. Das heißt, so wie ich das gerade gemacht habe, kann ich das maximal 1024 Mal machen. Danach muss ich das halt irgendwie anders sortieren.
Und zwar halt eben in einem mehrdimensionalen Threadblock, wo ich dann halt eben 1024 mal 1024 mal 64 Threads irgendwie organisieren kann. Ob ich die dann alle eindimensional mache oder zweidimensional oder dreidimensional, das hängt halt vom Problem ab. Das hilft aber halt eben dem Stream-Prozessor, das auf seine einzelnen Cores zu verteilen.
Das heißt, das ist notwendig, damit er das intern halt verteilen kann, um halt das maximale Ergebnis rauszuholen. Denn wenn ich jetzt die Addition habe, habe ich jetzt nur eine Operation. Das ist ja alles fein, die dauert für alle gleich. Aber wenn ich da zum Beispiel eine Schleife reinmache und die laufen unterschiedlich lange, dann läuft immer,
die Instruktionen werden immer durchlaufen, aber in einigen Blöcken warten die dann halt, bis der andere halt seine Schleife durchlaufen hat. Und so habe ich dann sowas, sogenannte Thread-Diversion. Und das hilft eben hier, das zu vermeiden und zu minimieren. Das Ganze kann man noch größer treiben, wenn ich dann halt eben diese Dimension nicht ausreichte
und ich habe noch mehr Sachen, die ich berechnen will, kann ich halt einen Grid machen und dann kann ich das halt noch größer machen. Das ist jetzt halt viel Thread-Organisation, das ist dann viel Pain, wenn man das dann halt mal entwickelt, das halt alles noch zu organisieren. Das ist alles ganz gut, wenn das so lange es noch gut läuft. Aber wenn da irgendwann mal ein Ergebnis rauskommt, was halt nicht so ist, wie man sich das vorstellt,
dann macht es sehr viel Spaß, das Ganze rauszufriemeln, wo der Fehler ist. Hier sieht man das nochmal aufgeteilt. Also der Scheduler teilt eben diese Blöcke, die man vorher definiert hat, auf. Und das hilft dabei, wenn ich eine GPU mit zwei Prozessoren habe, dann kann ich das halt eben so aufteilen. Wenn ich eine mit vier oder mehr habe oder sowas, dann kann ich es halt so aufteilen.
Dann ist das Programm auch unabhängig von der jeweiligen GPU, die im System ist. Puh, eine Menge Theorie, denke ich. Klar, Dinge werden dann kompliziert. Die Datenmenge, die ich bearbeiten musste, ist halt eben für 430.000 Artikel des Washington Post Text Archives.
Das ist schon auf der Festplatte, die sie realisiert, in JSON-Dingern 7 GB groß. Wenn ich das halt in den Speicher lade, dauert das schon mal eine Weile. Und wenn man das dann halt noch dann irgendwo in die CPU reinschiebt, ist das halt auch schwierig. Und außerdem, wie man gerade gesehen hat, ich kann halt eben nicht meine fancy Bibliotheken verwenden,
also mit irgendwelchen Arrays oder Dictionaries oder sowas arbeiten, sondern ich habe dann halt eben Pointer und ich habe viel Pointer Arithmetik. Und da habe ich keine Möglichkeit, einfach mal zu sagen, immer die Länge von einem Array gerade heraus, sondern das muss ich halt immer irgendwie wissen oder als Parameter halt mitgeben oder sowas irgendwo reinschreiben,
damit ich weiß, wie viel muss ich da durchinterieren. Und wer das halt eben mal in C gemacht hat, der weiß, dass das halt alles schön ist, wenn es funktioniert, aber bis dahin ist das halt doch ein schmerzhafter Weg unter Umständen. Klar, es muss alles für CUDA kompiliert werden. Das Witzige ist halt, dass diese Graph Codes, wie man ja vorhin gesehen hat,
je nachdem wie viele Features ich dort erkannt habe, sind diese Graphen ja halt auch unterschiedlich groß. Und daher sind halt auch die Matrizen halt unterschiedlich groß. Das heißt also, ich muss immer gucken, in dem Zyklus vergleiche ich einen Matrix 9x9 mit einer 16x16. Da muss ich halt so und so lange durchgehen und so weiter und so fort. Das war sehr spannend, das alles rauszulösen.
Gut, ich kann nicht weiter darauf eingehen. Als Recap ist halt eben, man muss Memory Management machen. Das heißt, die Verbindung ist halt auch lahm, halt über PCI Express. Vergleichsweise ist das glaube ich in meinem Problemfall gar nicht mal so groß. Aber Memory ist kleiner als der Hauptspeicher. Wenn ich die 7 GB in meinen internen RAM lade, kann ich die nicht in meiner 3 GB großen GPU verwenden.
Das heißt, ich muss das schon immer halt vorsortieren. Ich brauche auch immer halt meine eigenen passenden Datenstrukturen, um das zu machen. Vor allen Dingen, wenn es halt komplexere Daten sind als jetzt eine einfache Matrix. Und mit den Dictionaries dazu wird es halt immer schwieriger. Diese Sachen müssen halt alle irgendwo organisiert werden innerhalb dieser Thread Blocks und in den Grids.
Und der Zugriff auf den Speicher, wie gesagt, mit welchem Index, wie lange darf der laufen und so weiter und so fort. Darüber hinaus gibt es noch einige Themen, die man jetzt mit CUDA halt machen kann. Ich kann natürlich auch Shared Memory machen.
Mittlerweile gibt es ganz fancy Methoden, wie ich halt eben den Speicher innerhalb der GPU mit dem RAM verknüpfen kann. So dass ich halt eben beim, wenn ich halt einen Speicherbereich allokiere, kann ich dem CUDA sagen, verwende halt beides mit irgendwo. Und wo du speicherst, ist mir egal. Und wie du es transferierst, brauche ich auch nicht wissen.
Ich will halt nur irgendwie halt mehr nutzen, als eigentlich da ist. Und CUDA kann das relativ gut verstecken. Ansonsten, wenn man halt auch innerhalb, also ich habe in meinen Algorithmen zu Glück keinen Punkt gehabt, dass ich halt irgendwo Zwischenergebnisse in anderen Stufen halt brauchte.
Aber wenn man das natürlich halt hat, dass man halt eben zwei Prozesse hat, die ein Zwischenergebnis produzieren, was von einem Dritten gebraucht wird. Dann habe ich halt irgendwo halt einen Shared Memory, wo ich das irgendwo ablegen muss. Da kommt da halt Synchronisierung noch mit rein, dass ich halt sage, okay, du musst so lange warten, bis das und das da ist oder alle Blocks fertig sind. Das hatte ich zum Glück nicht mit zu tun, aber das sind natürlich auch nochmal Themen, die dazu kommen.
Dann gibt es noch die Organisation für Concurrency Management. Dynamic Parallelism ist auch sehr interessant, dass ich halt eben aus einer CUDA-Funktion eine andere CUDA-Funktion aufrufen kann. Das heißt also, ich habe nicht mehr nur den Punkt, dass ich eine Funktion aufrufen kann, sondern ich kann dann halt auch wirklich halt sagen,
wenn ich das berechnet habe, dann würde ich die Sortierfunktion nochmal aufrufen. Die ist dann auch wieder parallel. Das ist auch eine ziemlich coole Variante, die es eigentlich sehr nah an dem normalen entwickeln macht, wie man ein normales Programm strukturieren würde. Gut, aber dann kommen wir mal zu den Ergebnissen, die ich jetzt hier mit meiner Problemstellung dort produziert habe.
Wie zu erwarten ist eben CUDA, dadurch, dass der Algorithmus halt sehr gut parallelisieren kann, performt er sehr gut. Ich habe jetzt hier mal die sequentielle Variante gemacht. Hier sieht man das so ungefähr bei 100.000 Elementen. Da braucht eine sequentielle CPU um die Größenordnung 5 Sekunden.
Wenn ich das mit Threads mache, ich kann natürlich auch den Algorithmus mit Threads parallelisieren. Da hatte ich hier auf meiner Maschine, glaube ich, 5 parallele Threads. Dann komme ich da halt schon deutlich weniger, bin ich irgendwo bei so einer Sekunde. Wenn ich das halt mit CUDA mache, dann bin ich halt irgendwo hier im 0,1 Bereich.
Das ist natürlich alles ein bisschen Äpfel mit Birnen vergleichen, weil der CPU und die GPU laufen mit ganz anderen Taktraten. Das kann man jetzt nicht so nehmen, aber man sieht halt eben, dass es um Größenordnung schneller geht. Das ist auch noch besonders schön, wenn man das Ganze mit unterschiedlichen Modellen anschaut. Ich habe hier immer so einen Low-End-Computer, so einen Jetson Nano, verwendet mit 128 Cores.
Der braucht dann halt eben hier für so eine Größe 100.000, eben sind wir hier 100.000, irgendwas im Sekundenbereich. Wenn ich halt die Anzahl der Kerne verdoppel, dann reduziert sich auch die Laufzeit um die Hälfte bzw. halt eben noch weiter.
Das heißt also, das skaliert mit der Anzahl der Kerne. Das lässt sich halt einfach sehr schön parallelisieren. Und wenn ich halt größeres Problem habe, kann ich halt mit mehr Kernen das Problem schneller lösen, fast linear. Und das ist natürlich sehr cool. Das hat jetzt nichts mit der GPU zu tun.
Das selbe sieht man halt auch auf Multicore-CPUs. Also auch hier, wenn ich halt eben quasi halt eine Vielfaches der Menge an Kernen in der CPU da drauf werfe, dann lässt sich das Problem auch sehr schön lösen. Im besten Fall mit der Hardware, die wir getestet haben, im Vergleich zu einem sequenziellen Algorithmus,
habe ich halt eine 225-fache Beschleunigung erreicht. Juhu, was haben wir als Ergebnis, das kann man mitnehmen. Also spezialisierte Prozessoren sind durchaus ein Trend. Und die Probleme, die man da hat, sind glaube ich gar nicht so fern. Also man trifft das halt häufiger an, als man denkt.
Aber dadurch, dass man halt gar nicht diese Varianten halt so kennt, würde man gar nicht erst damit anfangen, das Problem zu lösen. GPUs können für general purpose computing eingesetzt werden. Wie man gesehen hat, das sind halt auch relativ normale Funktionen. Ja, ich habe halt das Speichermanagement und so weiter, aber es ist halt auch eine relativ normale Funktion, wie man sonst auch Sachen programmieren würde.
Es ist nicht so kompliziert als C++-Entwickler, weil man das Ganze mit diesem Memory-Management halt eh kennt und sich da mit den ganzen Datenstrukturen und Pointern eben mit befasst. Dann ist es, glaube ich, nicht so easy. Aber wenn man als Java-Entwickler da hinkommt und sagt, der Garbage-Collector wird es schon irgendwie lösen, dann muss man sich da einfach nochmal wieder reinfinden.
Und klar, große Datensätze, viel Datenmanagement, große Datenstrukturen. Und ja, ihr habt wahrscheinlich einen Use-Case von massive Parallelisierungen. Maybe. That's it. Dann gibt es Fragen.
Danke. Ja, hier vorne. Es gibt ein Mikrofon, einen kleinen Moment.
Maximale Wegstrecke ist auch immer ein gutes mathematisches Problem, das zu lösen. Du hattest jetzt gerade gesagt, das Leben wäre einfach, wenn man so mit C und C++ schon was gemacht hätte. Aber eben hat es erzählt, das war, glaube ich, bei den unterschiedlich großen Feature-Matrizen, dass du die Orga der Verarbeitung selber machen musst.
Das macht ja der C, C++-Mensch normalerweise nicht, wie viel zusätzlicher Aufwand dadurch kommt. Ja, man muss schon einiges an Gehirnschmalz dann reinstecken. Zumindest ging es mir so, um zu sagen, ich muss für jede Matrix wissen, wie groß sie ist.
Wie groß ist das Dictionary? Dadurch, dass ich alle in einem Schwung reingeladen habe, oder alle, die ich berechnen wollte, reingeladen habe, das waren dann mehrere Pointer-Arrays.
Da muss ich mir dann natürlich merken, wo die Offsets sind. Bis wohin geht die Matrix, wie groß ist sie, wo fängt die nächste an usw. Um das dann immer wieder über die Blog-Dinger zu verorten. Wenn ich dort diese einen Berechnungsschritt mache, wo fange ich an, in dem Array zu suchen, oder wo beginnt die Matrix oder das Dictionary und wie lang geht es?
Und da hat die iteration, also die Schleifen, auch entsprechend lang laufen zu lassen. Das war jetzt in dem Fall schon ein großer Teil, sich damit zu beschäftigen. So wie du das gerade beschreibst, klingt das für mich, als ob du sozusagen vor dem Editor sitzt und da drauf guckst. Aber wenn ich mir, was hast du gesagt, 430.000 Artikel aus dem Washington Post Text Archive,
da weißt du ja erstmal gar nicht, wie groß die Feature-Matrizen sind, je nachdem, was du für eine Abfrage hast, wechselt das. Wie muss ich mir das praktisch vorstellen? Also ich hab das so gehabt, ich hab dir diese Feature oder die Graph-Codes halt in Jason-Arrays als File auf der Platte liegen und hab sie dann halt natürlich erstmal eingelesen.
Beim Einlesen von Jason konnte ich mir natürlich dann halt immer gucken, wie groß sind die einzelnen Elemente, hab mir das halt eben in entsprechende Integer-Arrays oder Short-Integer-Arrays dann reingeschrieben, um mir das halt für alle 430.000 zu merken. Und auf der Basis von der Information von diesen 430.000 hast du dann die Blöcke für die Verarbeitung organisieren müssen?
Genau. Oh, okay. Also das ist dann, man hat ja natürlich, also man kann halt auch in einem Cuda-Code halt eine Schleife verwenden, also das, weil man ja halt immer schaut, wenn ich weiß, wie lang ist das, wie lange muss der laufen, dann, das geht schon ganz gut.
Da ist halt eben die Gefahr, dass man diese Thread-Diversion hat, weil der eine Thread läuft dann halt wesentlich kürzer als der andere, weil die Matrizen größer sind. Das war auch ein Punkt, den ich dann vermutet hab, dass der halt die Performance drastisch beeinflusst. Zum Glück tut das nicht, also es war kein großer Effekt festzustellen.
Dann ist mir noch eine Kleinigkeit aufgefallen, auf der Slide 27, wenden wir mal ein paar Blütenblätter. 27. Da, genau die. Ja. Da ist, wenn ich das richtig sehe, das grüne Cuda bei dem letzten 728.618, nicht bei dem davor.
Weiß ich jetzt, kann ich jetzt nicht richtig sehen. Genau, die sind nicht dabei, die Werte, weil wir die nicht gemessen haben, weil in dem Experiment, in dem die GPU, die ich da verwendet hab, hat nicht genug Speicher gehabt, um die Menge an Daten halt komplett zu laden. Also skaliert mit oberer Grenze.
Also der Speicherbereich ist natürlich irgendwo halt das Limit. Es hat so eine NVIDIA A100 GPU, die hat 80 GB Speicher, da kann man natürlich schon mal eine Menge reinpacken, aber das ist auf jeden Fall ein Limit. Das war halt für mich in der Arbeit nicht mehr relevant, das kann man natürlich auch in Shards aufteilen,
oder wenn man eine Multi-GPU hat, dann kann man natürlich die eine Hälfte dahin schicken, die andere Hälfte dahin, da gibt es natürlich auch Varianten. Aber die PC Variante, die hat diese 728.000, werden auch in elender Zeit geschafft. Ja. Was war das für eine Maschine? Das war ein Intel i5 5400 mit 16 GB RAM.
Ok, also ich will das verfehlen. Keine weiteren Fragen? Alle erschlagen, alle fertig für das Social Event?
Wunderbar. Dann nochmal vielen Dank und viel Spaß auf dem Event.