13.09.2024
Heim / Büro / Mining auf einer GPU-Grafikkarte – eine vollständige Anleitung. Effektive Nutzung der GPU Warum ich im Task-Manager keine GPU habe

Mining auf einer GPU-Grafikkarte – eine vollständige Anleitung. Effektive Nutzung der GPU Warum ich im Task-Manager keine GPU habe

Ein Entwickler sollte lernen, die Grafikverarbeitungseinheit (GPU) des Geräts effektiv zu nutzen, damit die Anwendung nicht langsamer wird oder unnötige Arbeit ausführt.

Konfigurieren Sie die GPU-Rendering-Einstellungen

Wenn Ihre App träge ist, bedeutet das, dass die Aktualisierung einiger oder aller Aktualisierungsrahmen des Bildschirms länger als 16 Millisekunden dauert. Um Frame-Updates visuell auf dem Bildschirm anzuzeigen, können Sie eine spezielle Option auf dem Gerät aktivieren (Profile GPU Rendering).

Sie können schnell erkennen, wie lange das Rendern von Frames dauert. Ich möchte Sie daran erinnern, dass Sie es innerhalb von 16 Millisekunden halten müssen.

Die Option ist auf Geräten ab Android 4.1 verfügbar. Der Entwicklermodus muss auf dem Gerät aktiviert sein. Auf Geräten mit Version 4.2 und höher ist der Modus standardmäßig ausgeblendet. Zum Aktivieren gehen Sie zu Einstellungen | Über das Telefon und klicken Sie sieben Mal auf die Zeile Build-Nummer.

Gehen Sie nach der Aktivierung zu Entwickleroptionen und den Punkt finden Konfigurieren Sie die GPU-Rendering-Einstellungen(Profil-GPU-Rendering), das aktiviert sein sollte. Wählen Sie im Popup-Fenster die Option aus Auf dem Bildschirm in Form von Spalten(Auf dem Bildschirm als Balken). In diesem Fall wird das Diagramm über der laufenden Anwendung angezeigt.

Sie können nicht nur Ihre Anwendung testen, sondern auch andere. Starten Sie eine beliebige Anwendung und beginnen Sie damit zu arbeiten. Während Sie arbeiten, wird am unteren Bildschirmrand eine aktualisierte Grafik angezeigt. Die horizontale Achse stellt die verstrichene Zeit dar. Die vertikale Achse zeigt die Zeit für jeden Frame in Millisekunden. Bei der Interaktion mit der Anwendung werden von links nach rechts vertikale Balken auf dem Bildschirm gezeichnet, die die Frame-Leistung im Zeitverlauf anzeigen. Jede dieser Spalten stellt einen Rahmen zum Zeichnen des Bildschirms dar. Je höher die Spaltenhöhe, desto länger dauert das Zeichnen. Die dünne grüne Linie dient als Orientierung und entspricht 16 Millisekunden pro Frame. Daher müssen Sie beim Studium Ihrer Bewerbung darauf achten, dass die Grafik nicht über diese Linie hinausgeht.

Schauen wir uns eine größere Version des Diagramms an.

Die grüne Linie ist für 16 Millisekunden verantwortlich. Um innerhalb von 60 Bildern pro Sekunde zu bleiben, muss jeder Diagrammbalken unterhalb dieser Linie gezeichnet werden. Irgendwann wird die Säule zu groß und viel höher als die grüne Linie sein. Das bedeutet, dass das Programm langsamer wird. Jede Spalte enthält Cyan, Lila (Lollipop und höher), Rot und Orange.

Die blaue Farbe ist für die Zeit verantwortlich, die zum Erstellen und Aktualisieren benötigt wird Sicht.

Der violette Teil stellt die Zeit dar, die für die Übertragung der Rendering-Ressourcen des Threads aufgewendet wurde.

Die rote Farbe stellt die Zeit zum Zeichnen dar.

Die orange Farbe zeigt an, wie lange die CPU gewartet hat, bis die GPU ihre Arbeit abgeschlossen hat. Dies ist die Ursache für Probleme bei großen Werten.

Um die Belastung der GPU zu reduzieren, gibt es spezielle Techniken.

Debuggen Sie die GPU-Überlastungsanzeige

Mit einer anderen Einstellung erfahren Sie, wie oft derselbe Bereich des Bildschirms neu gezeichnet wird (d. h. zusätzliche Arbeit geleistet wird). Lass uns noch einmal gehen Entwickleroptionen und den Punkt finden Debuggen Sie die GPU-Überlastungsanzeige(Debug GPU Overdraw), das aktiviert sein sollte. Wählen Sie im Popup-Fenster die Option aus Overlay-Zonen anzeigen(Überzeichnungsbereiche anzeigen). Hab keine Angst! Einige Elemente auf dem Bildschirm ändern ihre Farbe.

Gehen Sie zu einer beliebigen Anwendung zurück und beobachten Sie, wie sie funktioniert. Die Farbe weist auf Problembereiche in Ihrer Anwendung hin.

Wenn sich die Farbe in der Anwendung nicht verändert hat, ist alles in Ordnung. Es gibt keine Überlagerung einer Farbe über einer anderen.

Die blaue Farbe zeigt an, dass eine Ebene über der darunter liegenden Ebene gezeichnet wird. Bußgeld.

Grüne Farbe – zweimal neu gezeichnet. Sie müssen über eine Optimierung nachdenken.

Rosa Farbe – dreimal neu gezeichnet. Alles ist sehr schlecht.

Rote Farbe – viele Male neu gezeichnet. Etwas ist schief gelaufen.

Sie können Ihre Bewerbung selbst überprüfen, um Problembereiche zu finden. Erstellen Sie eine Aktivität und platzieren Sie eine Komponente darauf Textansicht. Geben Sie dem Stammelement und der Textbeschriftung einen Hintergrund im Attribut Android:Hintergrund. Sie erhalten Folgendes: Zuerst haben Sie die unterste Aktivitätsebene mit einer Farbe bemalt. Dann wird eine neue Ebene darüber gezeichnet Textansicht. Eigentlich übrigens Textansicht Text wird ebenfalls gezeichnet.

An manchen Stellen sind überlappende Farben nicht zu vermeiden. Aber stellen Sie sich vor, dass Sie den Hintergrund für die Liste auf die gleiche Weise festlegen ListView, das den gesamten Aktivitätsbereich einnimmt. Das System wird eine doppelte Aufgabe erfüllen, obwohl der Benutzer nie die unterste Aktivitätsebene sehen wird. Und wenn Sie zusätzlich für jedes Element der Liste ein eigenes Markup mit eigenem Hintergrund erstellen, werden Sie in der Regel überfordert.

Ein kleiner Rat. Nach Methode platzieren setContentView() Aufrufen einer Methode, die verhindert, dass der Bildschirm mit der Designfarbe bemalt wird. Dies hilft dabei, eine zusätzliche Farbüberlagerung zu entfernen:

GetWindow().setBackgroundDrawable(null);

Verwendung von GPU-Computing mit C++ AMP

Bisher haben wir bei der Erörterung paralleler Programmiertechniken nur Prozessorkerne berücksichtigt. Wir haben einige Fähigkeiten im Parallelisieren von Programmen über mehrere Prozessoren hinweg, im Synchronisieren des Zugriffs auf gemeinsam genutzte Ressourcen und im Verwenden von Hochgeschwindigkeits-Synchronisationsprimitiven ohne den Einsatz von Sperren erworben.

Es gibt jedoch eine andere Möglichkeit, Programme zu parallelisieren – Grafikprozessoren (GPUs), mit mehr Kernen als selbst Hochleistungsprozessoren. GPU-Kerne eignen sich hervorragend für die Implementierung paralleler Datenverarbeitungsalgorithmen, und ihre große Anzahl macht die Unannehmlichkeiten, die mit der Ausführung von Programmen auf ihnen verbunden sind, mehr als wett. In diesem Artikel lernen wir eine der Möglichkeiten kennen, Programme auf einer GPU auszuführen, indem wir eine Reihe von C++-Spracherweiterungen namens „ C++AMP.

Die C++ AMP-Erweiterungen basieren auf der Sprache C++, weshalb in diesem Artikel Beispiele in C++ gezeigt werden. Allerdings bei mäßiger Nutzung des Interaktionsmechanismus in. NET können Sie C++ AMP-Algorithmen in Ihren .NET-Programmen verwenden. Aber darüber werden wir am Ende des Artikels sprechen.

Einführung in C++ AMP

Im Wesentlichen ist eine GPU derselbe Prozessor wie jeder andere, jedoch mit einem speziellen Befehlssatz, einer großen Anzahl von Kernen und einem eigenen Speicherzugriffsprotokoll. Es gibt jedoch große Unterschiede zwischen modernen GPUs und herkömmlichen Prozessoren, und deren Verständnis ist der Schlüssel zum Erstellen von Programmen, die die Rechenleistung effizient nutzen. GPU.

    Moderne GPUs verfügen über einen sehr kleinen Befehlssatz. Dies impliziert einige Einschränkungen: fehlende Möglichkeit zum Aufrufen von Funktionen, begrenzte Anzahl unterstützter Datentypen, fehlende Bibliotheksfunktionen und andere. Einige Vorgänge, beispielsweise bedingte Verzweigungen, können erheblich mehr kosten als ähnliche Vorgänge, die auf herkömmlichen Prozessoren ausgeführt werden. Offensichtlich erfordert das Verschieben großer Codemengen von der CPU auf die GPU unter solchen Bedingungen einen erheblichen Aufwand.

    Die Anzahl der Kerne ist bei einer durchschnittlichen GPU deutlich höher als bei einem durchschnittlichen herkömmlichen Prozessor. Einige Aufgaben sind jedoch zu klein oder können nicht in ausreichend große Teile zerlegt werden, um von der GPU zu profitieren.

    Die Synchronisierungsunterstützung zwischen GPU-Kernen, die dieselbe Aufgabe ausführen, ist sehr schlecht und zwischen GPU-Kernen, die unterschiedliche Aufgaben ausführen, fehlt sie völlig. Dieser Umstand erfordert eine Synchronisierung des Grafikprozessors mit einem herkömmlichen Prozessor.

Es stellt sich sofort die Frage: Welche Aufgaben eignen sich zur Lösung auf einer GPU? Bedenken Sie, dass nicht jeder Algorithmus für die Ausführung auf einer GPU geeignet ist. GPUs haben beispielsweise keinen Zugriff auf E/A-Geräte, sodass Sie die Leistung eines Programms, das RSS-Feeds aus dem Internet scrapt, nicht mithilfe einer GPU verbessern können. Viele Rechenalgorithmen lassen sich jedoch auf die GPU übertragen und massiv parallelisieren. Nachfolgend finden Sie einige Beispiele für solche Algorithmen (diese Liste ist keineswegs vollständig):

    zunehmende und abnehmende Bildschärfe und andere Transformationen;

    schnelle Fourier-Transformation;

    Matrixtransposition und -multiplikation;

    Zahlensortierung;

    direkte Hash-Inversion.

Eine ausgezeichnete Quelle für zusätzliche Beispiele ist der Microsoft Native Concurrency-Blog, der Codeausschnitte und Erklärungen für verschiedene in C++ AMP implementierte Algorithmen bereitstellt.

C++ AMP ist ein in Visual Studio 2012 enthaltenes Framework, das C++-Entwicklern eine einfache Möglichkeit bietet, Berechnungen auf der GPU durchzuführen, wobei nur ein DirectX 11-Treiber erforderlich ist. Microsoft hat C++ AMP als offene Spezifikation veröffentlicht, die von jedem Compiler-Anbieter implementiert werden kann.

Mit dem C++ AMP-Framework können Sie Code ausführen Grafikbeschleuniger, das sind Computergeräte. Mithilfe des DirectX 11-Treibers erkennt das C++ AMP-Framework alle Beschleuniger dynamisch. C++ AMP umfasst außerdem einen Software-Beschleuniger-Emulator und einen herkömmlichen prozessorbasierten Emulator, WARP, der als Fallback auf Systemen ohne GPU oder mit GPU dient, aber keinen DirectX 11-Treiber hat und mehrere Kerne und SIMD-Anweisungen verwendet.

Beginnen wir nun mit der Erforschung eines Algorithmus, der für die Ausführung auf einer GPU leicht parallelisiert werden kann. Die folgende Implementierung nimmt zwei Vektoren gleicher Länge und berechnet das punktweise Ergebnis. Etwas Einfacheres kann man sich kaum vorstellen:

Void VectorAddExpPointwise(float* first, float* second, float* result, int length) ( for (int i = 0; i< length; ++i) { result[i] = first[i] + exp(second[i]); } }

Um diesen Algorithmus auf einem regulären Prozessor zu parallelisieren, müssen Sie den Iterationsbereich in mehrere Unterbereiche aufteilen und für jeden einen Ausführungsthread ausführen. Wir haben in früheren Artikeln genau dieser Methode der Parallelisierung unseres ersten Suchbeispiels viel Zeit gewidmet Primzahlen– Wir haben gesehen, wie wir dies erreichen können, indem wir Threads manuell erstellen, Jobs an einen Thread-Pool übergeben und Parallel.For und PLINQ zur automatischen Parallelisierung verwenden. Denken Sie auch daran, dass wir bei der Parallelisierung ähnlicher Algorithmen auf einem herkömmlichen Prozessor besonders darauf geachtet haben, das Problem nicht in zu kleine Aufgaben aufzuteilen.

Für die GPU sind diese Warnungen nicht erforderlich. GPUs verfügen über mehrere Kerne, die Threads sehr schnell ausführen, und die Kosten für den Kontextwechsel sind deutlich geringer als bei herkömmlichen Prozessoren. Unten sehen Sie einen Ausschnitt, der versucht, die Funktion zu verwenden parallel_for_each aus dem C++ AMP-Framework:

#enthalten #enthalten Verwendung der Namespace-Parallelität; void VectorAddExpPointwise(float* first, float* second, float* result, int length) ( array_view avFirst(length, first); array_view avSecond(length, second);<1>i) strict(amp) ( avResult[i] = avFirst[i] + fast_math::exp(avSecond[i]); ));

avResult.synchronize(); )

Lassen Sie uns nun jeden Teil des Codes einzeln untersuchen. Beachten wir sofort, dass die allgemeine Form der Hauptschleife beibehalten wurde, die ursprünglich verwendete for-Schleife jedoch durch einen Aufruf der Funktion parallel_for_each ersetzt wurde. Tatsächlich ist das Prinzip der Umwandlung einer Schleife in einen Funktions- oder Methodenaufruf für uns nicht neu – eine solche Technik wurde bereits zuvor mit den Methoden Parallel.For() und Parallel.ForEach() aus der TPL-Bibliothek demonstriert. Als nächstes werden die Eingabedaten (Parameter erster, zweiter und Ergebnis) mit Instanzen umschlossen array_view

. Die Klasse array_view wird verwendet, um an die GPU (Beschleuniger) übergebene Daten zu verpacken. Sein Vorlagenparameter gibt den Datentyp und seine Dimension an. Um Anweisungen auf einer GPU auszuführen, die auf Daten zugreifen, die ursprünglich auf einer herkömmlichen CPU verarbeitet wurden, muss sich jemand oder etwas darum kümmern, die Daten auf die GPU zu kopieren, da die meisten modernen Grafikkarten separate Geräte mit eigenem Speicher sind. array_view-Instanzen lösen dieses Problem – sie ermöglichen das Kopieren von Daten bei Bedarf und nur dann, wenn es wirklich benötigt wird. Wenn die GPU die Aufgabe abschließt, werden die Daten zurückkopiert. Indem wir array_view mit einem const-Argument instanziieren, stellen wir sicher, dass „first“ und „second“ in den GPU-Speicher kopiert, aber nicht zurückkopiert werden. Ebenso anrufen verwerfen_data()

Wir schließen das Kopieren des Ergebnisses aus dem Speicher eines regulären Prozessors in den Beschleunigerspeicher aus, diese Daten werden jedoch in die entgegengesetzte Richtung kopiert.

Die Funktion parallel_for_each benötigt ein Extent-Objekt, das die Form der zu verarbeitenden Daten angibt, und eine Funktion, die auf jedes Element im Extent-Objekt angewendet werden soll. Im obigen Beispiel haben wir eine Lambda-Funktion verwendet, deren Unterstützung im ISO C++2011 (C++11)-Standard enthalten ist. Das Schlüsselwort „restrict“ (amp) weist den Compiler an, zu prüfen, ob der Funktionskörper auf der GPU ausgeführt werden kann, und deaktiviert die meisten C++-Syntaxen, die nicht in GPU-Anweisungen kompiliert werden können.<1>Lambda-Funktionsparameter, Index

Zum Schluss der Methodenaufruf synchronisieren() Am Ende der VectorAddExpPointwise-Methode wird sichergestellt, dass die von der GPU erzeugten Berechnungsergebnisse aus array_view avResult zurück in das Ergebnisarray kopiert werden.

Damit ist unsere erste Einführung in die Welt von C++ AMP abgeschlossen, und jetzt sind wir bereit für detailliertere Recherchen sowie weitere interessante Beispiele, die die Vorteile der Verwendung von parallelem Computing auf einer GPU demonstrieren. Die Vektoraddition ist kein guter Algorithmus und aufgrund des hohen Aufwands beim Kopieren von Daten nicht der beste Kandidat für die Demonstration der GPU-Nutzung. Im nächsten Unterabschnitt werden zwei weitere interessante Beispiele gezeigt.

Matrixmultiplikation

Das erste „echte“ Beispiel, das wir uns ansehen werden, ist die Matrixmultiplikation. Zur Implementierung verwenden wir einen einfachen kubischen Matrixmultiplikationsalgorithmus und nicht den Strassen-Algorithmus, der eine Ausführungszeit nahe der kubischen ~O(n 2,807) hat. Gegeben zwei Matrizen, eine m x w-Matrix A und eine w x n-Matrix B, multipliziert das folgende Programm sie und gibt das Ergebnis zurück, eine m x n-Matrix C:

Void MatrixMultiply(int* A, int m, int w, int* B, int n, int* C) ( for (int i = 0; i< m; ++i) { for (int j = 0; j < n; ++j) { int sum = 0; for (int k = 0; k < w; ++k) { sum += A * B; } C = sum; } } }

Es gibt mehrere Möglichkeiten, diese Implementierung zu parallelisieren. Wenn Sie diesen Code so parallelisieren möchten, dass er auf einem regulären Prozessor ausgeführt wird, wäre die Parallelisierung der äußeren Schleife die richtige Wahl. Allerdings verfügt die GPU über eine relativ große Anzahl an Kernen, und wenn wir nur die äußere Schleife parallelisieren, können wir nicht genügend Jobs erstellen, um alle Kerne mit Arbeit zu belasten. Daher ist es sinnvoll, die beiden äußeren Schleifen zu parallelisieren und die innere Schleife unberührt zu lassen:

Void MatrixMultiply (int* A, int m, int w, int* B, int n, int* C) ( array_view avA(m, w, A); array_view avB(w, n, B);<2>array_view< w; ++k) { sum + = avA(idx*w, k) * avB(k*w, idx); } avC = sum; }); }

Diese Implementierung ähnelt immer noch stark der sequentiellen Implementierung der Matrixmultiplikation und dem oben angegebenen Beispiel für die Vektoraddition, mit Ausnahme des Index, der jetzt zweidimensional ist und in der inneren Schleife mithilfe des Operators zugänglich ist. Wie viel schneller ist diese Version als die sequentielle Alternative, die auf einem normalen Prozessor läuft? Wenn man zwei Matrizen (Ganzzahlen) der Größe 1024 x 1024 multipliziert, benötigt die sequentielle Version auf einer normalen CPU durchschnittlich 7350 Millisekunden, während die GPU-Version – halten Sie sich fest – 50 Millisekunden benötigt, 147-mal schneller!

Partikelbewegungssimulation

Die oben vorgestellten Beispiele zur Lösung von Problemen auf der GPU verfügen über eine sehr einfache Implementierung der internen Schleife. Es ist klar, dass dies nicht immer der Fall sein wird. Der oben verlinkte Blog „Native Concurrency“ zeigt ein Beispiel für die Modellierung von Gravitationswechselwirkungen zwischen Partikeln. Die Simulation umfasst unendlich viele Schritte; Bei jedem Schritt werden für jedes Teilchen neue Werte der Elemente des Beschleunigungsvektors berechnet und anschließend deren neue Koordinaten bestimmt. Hier wird der Partikelvektor parallelisiert – mit einer ausreichend großen Anzahl von Partikeln (von mehreren Tausend und mehr) können Sie eine ausreichend große Anzahl von Aufgaben erstellen, um alle GPU-Kerne mit Arbeit zu belasten.

Grundlage des Algorithmus ist die Implementierung der Bestimmung des Ergebnisses von Wechselwirkungen zwischen zwei Partikeln, wie unten dargestellt, was sich leicht auf die GPU übertragen lässt:

// hier sind float4 Vektoren mit vier Elementen // die die an den Operationen beteiligten Partikel repräsentieren void bodybody_interaction (float4& Beschleunigung, const float4 p1, const float4 p2) strict(amp) ( float4 dist = p2 – p1; // wird hier nicht verwendet float absDist = dist.x*dist.x + dist.y*dist.z; float invDist = 1.0f / sqrt(absDist); float invDistCube = invDist*invDist; PARTICLE_MASS*invDistCube )

Die Ausgangsdaten bei jedem Modellierungsschritt sind ein Array mit den Koordinaten und Geschwindigkeiten der Partikel. Als Ergebnis der Berechnungen wird ein neues Array mit den Koordinaten und Geschwindigkeiten der Partikel erstellt:

Struct-Partikel ( float4 Position, Geschwindigkeit; // Implementierungen von Konstruktor, Kopierkonstruktor und // Operator =, wobei restriktiv(amp) weggelassen wird, um Platz zu sparen ); void simulation_step(array & vorheriges Array & weiter, int-Körper) ( Umfang<1>ext(körper);<1>idx) beschränken(amp) ( Partikel p = vorherige; float4 Beschleunigung(0, 0, 0, 0); for (int body = 0; body< bodies; ++body) { bodybody_interaction (acceleration, p.position, previous.position); } p.velocity + = acceleration*DELTA_TIME; p.position + = p.velocity*DELTA_TIME; next = p; }); }

Mit Hilfe einer entsprechenden grafischen Oberfläche kann die Modellierung sehr interessant sein. Das vollständige Beispiel des C++ AMP-Teams finden Sie im Native Concurrency-Blog. Auf meinem System mit einem Intel Core i7-Prozessor und einer Geforce GT 740M-Grafikkarte läuft die Simulation von 10.000 Partikeln mit ~2,5 fps (Schritten pro Sekunde) bei Verwendung der sequentiellen Version, die auf dem regulären Prozessor ausgeführt wird, und 160 fps, wenn die optimierte Version ausgeführt wird auf der GPU - eine enorme Leistungssteigerung.

Bevor wir diesen Abschnitt abschließen, gibt es noch eine weitere wichtige Funktion des C++ AMP-Frameworks, die die Leistung von Code, der auf der GPU ausgeführt wird, weiter verbessern kann. GPU-Unterstützung programmierbarer Datencache(oft genannt gemeinsames Gedächtnis). Die in diesem Cache gespeicherten Werte werden von allen Ausführungsthreads in einer einzelnen Kachel gemeinsam genutzt. Dank der Speicherkachelung können Programme, die auf dem C++ AMP-Framework basieren, Daten aus dem Grafikkartenspeicher in den gemeinsamen Speicher des Mosaiks lesen und dann von mehreren Ausführungsthreads darauf zugreifen, ohne die Daten erneut aus dem Grafikkartenspeicher abrufen zu müssen. Der Zugriff auf den gemeinsam genutzten Mosaikspeicher ist etwa zehnmal schneller als auf den Grafikkartenspeicher. Mit anderen Worten: Sie haben Gründe, weiterzulesen.

Um eine gekachelte Version der Parallelschleife bereitzustellen, wird die Methode parallel_for_each übergeben Domäne „tiled_extent“., der das mehrdimensionale Extent-Objekt in mehrdimensionale Kacheln unterteilt, und den lambda-Parameter „tiled_index“, der die globale und lokale ID des Threads innerhalb der Kachel angibt. Beispielsweise kann eine 16x16-Matrix in 2x2-Kacheln unterteilt werden (wie im Bild unten gezeigt) und dann an die Funktion parallel_for_each übergeben werden:

Ausmaß<2>Matrix(16,16); kachel_ausdehnung<2,2>TiledMatrix = Matrix.tile<2,2>(); parallel_for_each(tiledMatrix, [=](tiled_index<2,2>idx) strict(amp) ( // ... ));

Jeder der vier Ausführungsthreads, die zum selben Mosaik gehören, kann die im Block gespeicherten Daten gemeinsam nutzen.

Bei der Ausführung von Operationen mit Matrizen im GPU-Kern anstelle des Standardindex<2>, wie in den obigen Beispielen, können Sie verwenden idx.global. Die ordnungsgemäße Verwendung von lokalem Kachelspeicher und lokalen Indizes kann zu erheblichen Leistungssteigerungen führen. Um gekachelten Speicher zu deklarieren, der von allen Ausführungsthreads in einer einzelnen Kachel gemeinsam genutzt wird, können lokale Variablen mit dem Bezeichner „tile_static“ deklariert werden.

In der Praxis wird häufig die Technik verwendet, gemeinsam genutzten Speicher zu deklarieren und seine einzelnen Blöcke in verschiedenen Ausführungsthreads zu initialisieren:

Parallel_for_each(tiledMatrix, [=](tiled_index<2,2>idx) strict(amp) ( // 32 Bytes werden von allen Threads im Block gemeinsam genutzt Tile_static int local; // Weisen Sie dem Element für diesen Ausführungsthread einen Wert zu local = 42; ));

Offensichtlich können die Vorteile der Verwendung von Shared Memory nur dann erzielt werden, wenn der Zugriff auf diesen Speicher synchronisiert ist. Das heißt, Threads dürfen nicht auf den Speicher zugreifen, bis dieser von einem von ihnen initialisiert wurde. Die Synchronisierung von Threads in einem Mosaik erfolgt mithilfe von Objekten Tile_Barrier(erinnert an die Barrier-Klasse aus der TPL-Bibliothek) – Sie können die Ausführung erst nach dem Aufruf der Methode „tile_barrier.Wait()“ fortsetzen, die die Kontrolle erst dann zurückgibt, wenn alle Threads „tile_barrier.Wait“ aufgerufen haben. Zum Beispiel:

Parallel_for_each(tiledMatrix, (tiled_index<2,2>idx) strict(amp) ( // 32 Bytes werden von allen Threads im Block gemeinsam genutzt Tile_static int local; // Weisen Sie dem Element für diesen Ausführungsthread einen Wert zu local = 42; // idx.barrier ist eine Instanz von Tile_barrier idx.barrier.wait(); // Jetzt kann dieser Thread auf das „lokale“ Array zugreifen, // unter Verwendung der Indizes anderer Ausführungsthreads ));

Jetzt ist es an der Zeit, das Gelernte in ein konkretes Beispiel umzusetzen. Kehren wir zur Implementierung der Matrixmultiplikation zurück, die ohne Verwendung einer Kachelspeicherorganisation durchgeführt wird, und fügen wir die beschriebene Optimierung hinzu. Nehmen wir an, dass die Matrixgröße ein Vielfaches von 256 ist. Dadurch können wir mit 16 x 16 Blöcken arbeiten. Die Natur von Matrizen ermöglicht eine blockweise Multiplikation, und wir können diese Funktion nutzen (tatsächlich dividieren). Die Umwandlung von Matrizen in Blöcke ist eine typische Optimierung des Matrixmultiplikationsalgorithmus und sorgt für eine effizientere CPU-Cache-Nutzung.

Das Wesentliche dieser Technik ist Folgendes. Um C i,j (das Element in Zeile i und Spalte j in der Ergebnismatrix) zu finden, müssen Sie das Skalarprodukt zwischen A i,* (i-te Zeile der ersten Matrix) und B *,j (j) berechnen -te Spalte in der zweiten Matrix). Dies entspricht jedoch der Berechnung der Partialskalarprodukte der Zeile und Spalte und der anschließenden Summierung der Ergebnisse. Wir können diese Tatsache nutzen, um den Matrixmultiplikationsalgorithmus in eine Kachelversion umzuwandeln:

Void MatrixMultiply(int* A, int m, int w, int* B, int n, int* C) ( array_view avA(m, w, A); array_view avC(m, n, C);<16,16>avC.discard_data();<16,16>parallel_for_each(avC.extent.tile

(), [=](tiled_index

idx) strict(amp) ( int sum = 0; int localRow = idx.local, localCol = idx.local; for (int k = 0; k

Der Kern der beschriebenen Optimierung besteht darin, dass jeder Thread im Mosaik (256 Threads werden für einen 16 x 16-Block erstellt) sein Element in 16 x 16 lokalen Kopien von Fragmenten der Originalmatrizen A und B initialisiert. Jeder Thread im Mosaik erfordert nur eine Zeile und eine Spalte dieser Blöcke, aber alle Threads zusammen greifen 16 Mal auf jede Zeile und jede Spalte zu. Dieser Ansatz reduziert die Anzahl der Zugriffe auf den Hauptspeicher erheblich.

Bevor wir unsere Diskussion über das C++ AMP-Framework beenden, möchten wir die Tools (in Visual Studio) erwähnen, die Entwicklern zur Verfügung stehen. Visual Studio 2012 bietet einen GPU-Debugger (Graphics Processing Unit), mit dem Sie Haltepunkte festlegen, den Aufrufstapel untersuchen und lokale Variablenwerte lesen und ändern können (einige Beschleuniger unterstützen das GPU-Debugging direkt; für andere verwendet Visual Studio einen Softwaresimulator). und einen Profiler, mit dem Sie die Vorteile bewerten können, die eine Anwendung durch die Parallelisierung von Vorgängen mithilfe einer GPU erhält. Weitere Informationen zu Debugfunktionen in Visual Studio finden Sie im Walkthrough-Artikel. Debuggen einer C++ AMP-Anwendung“ auf MSDN.

GPU-Computing-Alternativen in .NET

Bisher wurden in diesem Artikel nur Beispiele in C++ gezeigt, es gibt jedoch mehrere Möglichkeiten, die Leistung der GPU in verwalteten Anwendungen zu nutzen. Eine Möglichkeit besteht darin, Interop-Tools zu verwenden, mit denen Sie die Arbeit mit GPU-Kernen auf C++-Komponenten auf niedriger Ebene verlagern können. Diese Lösung eignet sich hervorragend für diejenigen, die das C++ AMP-Framework verwenden möchten oder die Möglichkeit haben, vorgefertigte C++ AMP-Komponenten in verwalteten Anwendungen zu verwenden.

Eine andere Möglichkeit besteht darin, eine Bibliothek zu verwenden, die über verwalteten Code direkt mit der GPU zusammenarbeitet. Derzeit gibt es mehrere solcher Bibliotheken. Zum Beispiel GPU.NET und CUDAfy.NET (beides kommerzielle Angebote). Unten finden Sie ein Beispiel aus dem GPU.NET-GitHub-Repository, das die Implementierung des Skalarprodukts zweier Vektoren demonstriert:

Öffentliche statische Leere MultiplyAddGpu(double a, double b, double c) ( int ThreadId = BlockDimension.X * BlockIndex.X + ThreadIndex.X; int TotalThreads = BlockDimension.X * GridDimension.X; for (int ElementIdx = ThreadId; ElementIdx

Ich bin der Meinung, dass es viel einfacher und effizienter ist, eine Spracherweiterung (basierend auf C++ AMP) zu erlernen, als zu versuchen, Interaktionen auf Bibliotheksebene zu orchestrieren oder wesentliche Änderungen an der IL-Sprache vorzunehmen.

Nachdem wir uns also die Möglichkeiten der parallelen Programmierung in .NET und der Verwendung der GPU angesehen haben, zweifelt niemand daran, dass die Organisation paralleler Datenverarbeitung eine wichtige Möglichkeit zur Steigerung der Produktivität ist. Auf vielen Servern und Workstations auf der ganzen Welt bleibt die unschätzbare Rechenleistung von CPUs und GPUs ungenutzt, weil Anwendungen sie einfach nicht nutzen.

Die Task Parallel Library bietet uns die einzigartige Möglichkeit, alle verfügbaren CPU-Kerne einzubeziehen, obwohl dies die Lösung einiger interessanter Probleme der Synchronisierung, übermäßiger Aufgabenfragmentierung und ungleicher Arbeitsverteilung zwischen Ausführungsthreads erfordern wird.

Das C++ AMP-Framework und andere Mehrzweck-GPU-Parallel-Computing-Bibliotheken können erfolgreich verwendet werden, um Berechnungen über Hunderte von GPU-Kernen hinweg zu parallelisieren. Schließlich gibt es eine bisher unerschlossene Möglichkeit, Produktivitätssteigerungen durch den Einsatz von Cloud-Distributed-Computing-Technologien zu erzielen, die in letzter Zeit zu einer der Hauptrichtungen in der Entwicklung der Informationstechnologie geworden sind.

GPU-Computing

Die CUDA-Technologie (Compute Unified Device Architecture) ist eine Hardware- und Softwarearchitektur, die das Rechnen mit NVIDIA-Grafikprozessoren ermöglicht, die die GPGPU-Technologie (Random Computing auf Grafikkarten) unterstützen. Die CUDA-Architektur erschien erstmals mit der Veröffentlichung des NVIDIA-Chips der achten Generation – G80 – auf dem Markt und ist in allen nachfolgenden Serien von Grafikchips enthalten, die in den Beschleunigerfamilien GeForce, ION, Quadro und Tesla zum Einsatz kommen.

Mit dem CUDA SDK können Programmierer Algorithmen, die auf NVIDIA-GPUs ausgeführt werden können, in einem speziellen vereinfachten Dialekt der Programmiersprache C implementieren und spezielle Funktionen in den Text eines C-Programms einbinden. CUDA gibt dem Entwickler die Möglichkeit, nach eigenem Ermessen den Zugriff auf den Befehlssatz des Grafikbeschleunigers zu organisieren, seinen Speicher zu verwalten und darauf komplexe parallele Berechnungen zu organisieren.

Geschichte

Im Jahr 2003 lieferten sich Intel und AMD einen gemeinsamen Wettlauf um den leistungsstärksten Prozessor. Als Ergebnis dieses Rennens stiegen die Taktraten über mehrere Jahre hinweg deutlich an, insbesondere nach der Veröffentlichung des Intel Pentium 4.

Nach der Erhöhung der Taktfrequenzen (zwischen 2001 und 2003 verdoppelte sich die Taktfrequenz des Pentium 4 von 1,5 auf 3 GHz) mussten sich Nutzer mit Zehntel-Gigahertz begnügen, die von den Herstellern auf den Markt gebracht wurden (von 2003 bis). 2005, Taktfrequenzen um 3 auf 3,8 GHz erhöht).

Architekturen, die für hohe Taktfrequenzen optimiert waren, wie beispielsweise Prescott, begannen ebenfalls auf Schwierigkeiten zu stoßen, und zwar nicht nur in der Produktion. Chiphersteller stehen vor der Herausforderung, die Gesetze der Physik zu überwinden. Einige Analysten sagten sogar voraus, dass das Mooresche Gesetz nicht mehr gelten würde. Dies geschah jedoch nicht. Die ursprüngliche Bedeutung des Gesetzes wird oft verfälscht, betrifft aber die Anzahl der Transistoren auf der Oberfläche des Siliziumkerns. Lange Zeit ging mit einer Erhöhung der Transistoranzahl einer CPU eine entsprechende Leistungssteigerung einher – was zu einer Bedeutungsverzerrung führte. Doch dann wurde die Situation komplizierter. Die Entwickler der CPU-Architektur näherten sich dem Gesetz der Wachstumsreduzierung: Die Anzahl der Transistoren, die für die erforderliche Leistungssteigerung hinzugefügt werden mussten, wurde immer größer und führte in eine Sackgasse.

Der Grund, warum GPU-Hersteller nicht auf dieses Problem gestoßen sind, ist ganz einfach: CPUs sind darauf ausgelegt, maximale Leistung bei einem Befehlsstrom zu erzielen, der unterschiedliche Daten (sowohl Ganzzahl- als auch Gleitkommazahlen) verarbeitet, wahlfreien Speicherzugriff durchführt usw. d. Bisher versuchen Entwickler, eine größere Parallelität der Anweisungen zu erreichen, also möglichst viele Anweisungen parallel auszuführen. Beim Pentium erschien beispielsweise die superskalare Ausführung, bei der es unter bestimmten Bedingungen möglich war, zwei Befehle pro Taktzyklus auszuführen. Pentium Pro erhielt Anweisungen außerhalb der Reihenfolge, wodurch der Betrieb von Recheneinheiten optimiert werden konnte. Das Problem besteht darin, dass es offensichtliche Einschränkungen bei der parallelen Ausführung eines sequentiellen Befehlsstroms gibt, so dass eine blinde Erhöhung der Anzahl der Recheneinheiten keinen Vorteil bringt, da diese die meiste Zeit noch im Leerlauf sind.

Die Bedienung der GPU ist relativ einfach. Dabei wird auf der einen Seite eine Gruppe von Polygonen genommen und auf der anderen Seite eine Gruppe von Pixeln erzeugt. Polygone und Pixel sind unabhängig voneinander und können daher parallel verarbeitet werden. Somit ist es in einer GPU möglich, einen großen Teil des Kristalls in Recheneinheiten zu unterteilen, die im Gegensatz zur CPU tatsächlich genutzt werden.

Die GPU unterscheidet sich nicht nur in dieser Hinsicht von der CPU. Der Speicherzugriff in der GPU ist sehr gekoppelt – wenn ein Texel gelesen wird, wird nach einigen Taktzyklen das benachbarte Texel gelesen; Wenn ein Pixel aufgezeichnet wird, wird nach einigen Taktzyklen das benachbarte Pixel aufgezeichnet. Durch die intelligente Organisation des Speichers können Sie eine Leistung erreichen, die dem theoretischen Durchsatz nahe kommt. Dies bedeutet, dass die GPU im Gegensatz zur CPU keinen großen Cache benötigt, da ihre Aufgabe darin besteht, Texturierungsvorgänge zu beschleunigen. Alles, was benötigt wird, sind ein paar Kilobyte mit ein paar Texeln, die in bilinearen und trilinearen Filtern verwendet werden.

Erste Berechnungen auf GPU

Die ersten Versuche solcher Anwendungen beschränkten sich auf die Verwendung bestimmter Hardwarefunktionen wie Rasterung und Z-Pufferung. Doch im laufenden Jahrhundert begann man mit dem Aufkommen von Shadern, Matrixberechnungen zu beschleunigen. Im Jahr 2003 wurde bei SIGGRAPH ein eigener Bereich für GPU-Computing eingerichtet, der den Namen GPGPU (General-Purpose Computation on GPU) erhielt.

Am bekanntesten ist BrookGPU, ein Compiler für die Streaming-Programmiersprache Brook, der für die Durchführung nichtgrafischer Berechnungen auf der GPU entwickelt wurde. Vor seinem Erscheinen wählten Entwickler, die die Fähigkeiten von Videochips für Berechnungen nutzten, eine von zwei gängigen APIs: Direct3D oder OpenGL. Dies schränkte die Verwendung von GPUs erheblich ein, da 3D-Grafiken Shader und Texturen verwenden, die Spezialisten für parallele Programmierung nicht kennen müssen, sondern Threads und Kerne. Brook konnte ihnen die Aufgabe erleichtern. Diese an der Stanford University entwickelten Streaming-Erweiterungen der C-Sprache verbargen die 3D-API vor Programmierern und präsentierten den Videochip als parallelen Coprozessor. Der Compiler verarbeitete die .br-Datei mit C++-Code und Erweiterungen und erzeugte Code, der mit einer DirectX-, OpenGL- oder x86-fähigen Bibliothek verknüpft war.

Das Erscheinen von Brook weckte das Interesse von NVIDIA und ATI und eröffnete anschließend einen ganz neuen Sektor – Parallelcomputer auf Basis von Videochips.

Anschließend schlossen sich einige Forscher des Brook-Projekts dem NVIDIA-Entwicklungsteam an, um eine Hardware-Software-Parallel-Computing-Strategie einzuführen und so neue Marktanteile zu erschließen. Und der Hauptvorteil dieser NVIDIA-Initiative besteht darin, dass Entwickler alle Fähigkeiten ihrer GPUs bis ins letzte Detail kennen, die Verwendung der Grafik-API nicht erforderlich ist und direkt über den Treiber mit der Hardware arbeiten kann. Das Ergebnis der Bemühungen dieses Teams war NVIDIA CUDA.

Anwendungsgebiete paralleler Berechnungen auf GPU

Bei der Übertragung von Berechnungen auf die GPU wird bei vielen Aufgaben eine 5- bis 30-fache Beschleunigung im Vergleich zu schnellen Universalprozessoren erreicht. Die größten Zahlen (in der Größenordnung von 100-facher Beschleunigung und sogar mehr!) werden mit Code erreicht, der für Berechnungen mit SSE-Blöcken nicht sehr gut geeignet ist, für GPUs jedoch recht praktisch ist.

Dies sind nur einige Beispiele für Beschleunigungen für synthetischen Code auf der GPU im Vergleich zu SSE-vektorisiertem Code auf der CPU (laut NVIDIA):

Fluoreszenzmikroskopie: 12x.

Molekulardynamik (Berechnung nicht gebundener Kräfte): 8–16x;

Elektrostatik (direkte und mehrstufige Coulomb-Summation): 40-120x und 7x.

Eine Tabelle, die NVIDIA in allen Präsentationen anzeigt, zeigt die Geschwindigkeit von GPUs im Verhältnis zu CPUs.

Liste der Hauptanwendungen, in denen GPU-Computing eingesetzt wird: Analyse und Verarbeitung von Bildern und Signalen, physikalische Simulation, Computermathematik, Computerbiologie, Finanzberechnungen, Datenbanken, Dynamik von Gasen und Flüssigkeiten, Kryptographie, adaptive Strahlentherapie, Astronomie, Audioverarbeitung , Bioinformatik, biologische Simulationen, Computer Vision, Data Mining, digitales Kino und Fernsehen, elektromagnetische Simulationen, geografische Informationssysteme, militärische Anwendungen, Minenplanung, Molekulardynamik, Magnetresonanztomographie (MRT), neuronale Netze, ozeanografische Forschung, Teilchenphysik, Protein Faltsimulation, Quantenchemie, Raytracing, Visualisierung, Radar, Reservoirsimulation, künstliche Intelligenz, Satellitendatenanalyse, seismische Erkundung, Chirurgie, Ultraschall, Videokonferenzen.

Vorteile und Einschränkungen von CUDA

Aus der Sicht eines Programmierers ist eine Grafikpipeline eine Sammlung von Verarbeitungsstufen. Der Geometrieblock generiert die Dreiecke und der Rasterisierungsblock generiert die auf dem Monitor angezeigten Pixel. Das traditionelle GPGPU-Programmiermodell sieht folgendermaßen aus:

Um Berechnungen innerhalb dieses Modells auf die GPU zu übertragen, ist ein besonderer Ansatz erforderlich. Selbst die elementweise Addition zweier Vektoren erfordert das Zeichnen der Figur auf dem Bildschirm oder in einem Puffer außerhalb des Bildschirms. Die Figur wird gerastert, die Farbe jedes Pixels wird mit einem vorgegebenen Programm (Pixel-Shader) berechnet. Das Programm liest für jedes Pixel die Eingabedaten aus den Texturen, fügt sie hinzu und schreibt sie in den Ausgabepuffer. Und all diese zahlreichen Operationen werden für etwas benötigt, das in einem einzigen Operator in einer regulären Programmiersprache geschrieben ist!

Daher weist die Verwendung von GPGPU für Allzweck-Computing die Einschränkung auf, dass es für die Schulung von Entwicklern zu schwierig ist. Und es gibt noch genügend andere Einschränkungen, denn ein Pixel-Shader ist nur eine Formel für die Abhängigkeit der endgültigen Farbe eines Pixels von seiner Koordinate, und die Sprache der Pixel-Shader ist eine Sprache zum Schreiben dieser Formeln mit einer C-ähnlichen Syntax. Frühe GPGPU-Methoden sind ein netter Trick, der es Ihnen ermöglicht, die Leistung der GPU zu nutzen, jedoch ohne deren Komfort. Die dortigen Daten werden durch Bilder (Texturen) dargestellt und der Algorithmus wird durch den Rasterungsprozess dargestellt. Besonders hervorzuheben ist das sehr spezifische Gedächtnis- und Ausführungsmodell.

Die Software- und Hardware-Architektur von NVIDIA für GPU-Computing unterscheidet sich von früheren GPGPU-Modellen dadurch, dass sie es Ihnen ermöglicht, Programme für die GPU in echter C-Sprache mit Standardsyntax, Zeigern und der Notwendigkeit eines Minimums an Erweiterungen zu schreiben, um auf die Rechenressourcen von Videochips zuzugreifen. CUDA ist unabhängig von Grafik-APIs und verfügt über einige Funktionen, die speziell für allgemeine Computeranwendungen entwickelt wurden.

Vorteile von CUDA gegenüber dem herkömmlichen Ansatz für GPGPU-Computing

CUDA bietet Zugriff auf 16 KB Thread-Shared-Speicher pro Multiprozessor, der zum Organisieren eines Caches mit höherer Bandbreite als Texturabrufen verwendet werden kann;

Effizientere Datenübertragung zwischen System und Videospeicher;

Keine Grafik-APIs mit Redundanz und Overhead erforderlich;

Lineare Speicheradressierung, Sammeln und Streuen, Fähigkeit, an beliebige Adressen zu schreiben;

Hardwareunterstützung für Ganzzahl- und Bitoperationen.

Haupteinschränkungen von CUDA:

Fehlende Rekursionsunterstützung für ausführbare Funktionen;

Die minimale Blockbreite beträgt 32 Threads;

Geschlossene CUDA-Architektur von NVIDIA.

Die Schwächen der Programmierung mit früheren GPGPU-Methoden bestehen darin, dass diese Methoden keine Vertex-Shader-Ausführungseinheiten in früheren nicht einheitlichen Architekturen verwenden, Daten in Texturen gespeichert und in einen Off-Screen-Puffer ausgegeben werden und Multi-Pass-Algorithmen Pixel-Shader-Einheiten verwenden. Zu den GPGPU-Einschränkungen können gehören: unzureichende Nutzung der Hardwarefunktionen, Einschränkungen der Speicherbandbreite, fehlender Scatter-Vorgang (nur Gather), obligatorische Verwendung der Grafik-API.

Die Hauptvorteile von CUDA gegenüber früheren GPGPU-Methoden ergeben sich aus der Tatsache, dass die Architektur darauf ausgelegt ist, nichtgrafisches Computing auf der GPU effizient zu nutzen und die Programmiersprache C verwendet, ohne dass Algorithmen in eine konzeptfreundliche Form für die Grafikpipeline portiert werden müssen . CUDA bietet einen neuen Weg zum GPU-Computing, das keine Grafik-APIs verwendet und einen wahlfreien Speicherzugriff (Scatter oder Gather) bietet. Diese Architektur weist nicht die Nachteile von GPGPU auf, nutzt alle Ausführungseinheiten und erweitert außerdem die Fähigkeiten durch Ganzzahlmathematik und Bitverschiebungsoperationen.

CUDA eröffnet einige Hardwarefunktionen, die über Grafik-APIs nicht verfügbar sind, wie z. B. gemeinsam genutzter Speicher. Dabei handelt es sich um einen kleinen Speicher (16 Kilobyte pro Multiprozessor), auf den Thread-Blöcke Zugriff haben. Es ermöglicht Ihnen, die am häufigsten aufgerufenen Daten zwischenzuspeichern und bietet schnellere Geschwindigkeiten als die Verwendung von Texturabrufen für diese Aufgabe. Dies wiederum verringert die Durchsatzempfindlichkeit paralleler Algorithmen in vielen Anwendungen. Es ist beispielsweise nützlich für lineare Algebra, schnelle Fourier-Transformation und Bildverarbeitungsfilter.

Auch der Speicherzugriff ist in CUDA bequemer. Der Grafik-API-Code gibt Daten als 32 Gleitkommawerte mit einfacher Genauigkeit (RGBA-Werte gleichzeitig in acht Renderziele) in vordefinierte Bereiche aus, und CUDA unterstützt Scatter Writing – eine unbegrenzte Anzahl von Datensätzen an jeder Adresse. Solche Vorteile ermöglichen die Ausführung einiger Algorithmen auf der GPU, die mit GPGPU-Methoden auf Basis von Grafik-APIs nicht effizient implementiert werden können.

Außerdem speichern Grafik-APIs Daten notwendigerweise in Texturen, was eine vorherige Verpackung großer Arrays in Texturen erfordert, was den Algorithmus verkompliziert und die Verwendung einer speziellen Adressierung erzwingt. Und mit CUDA können Sie Daten an jeder Adresse lesen. Ein weiterer Vorteil von CUDA ist der optimierte Datenaustausch zwischen CPU und GPU. Und für Entwickler, die Low-Level-Zugriff wünschen (z. B. beim Schreiben einer anderen Programmiersprache), bietet CUDA Low-Level-Programmierfunktionen in Assemblersprache.

Nachteile von CUDA

Einer der wenigen Nachteile von CUDA ist seine schlechte Portabilität. Diese Architektur funktioniert nur auf Videochips dieser Firma und nicht auf allen, sondern beginnend mit den Serien GeForce 8 und 9 und den entsprechenden Quadro, ION und Tesla. NVIDIA gibt eine Zahl von 90 Millionen CUDA-kompatiblen Videochips an.

CUDA-Alternativen

Ein Framework zum Schreiben von Computerprogrammen für paralleles Rechnen auf verschiedenen Grafik- und Zentralprozessoren. Das OpenCL-Framework umfasst eine Programmiersprache, die auf dem C99-Standard basiert, und eine An(API). OpenCL bietet Parallelität auf Befehls- und Datenebene und ist eine Implementierung der GPGPU-Technik. OpenCL ist ein vollständig offener Standard und kann lizenzgebührenfrei genutzt werden.

Das Ziel von OpenCL besteht darin, OpenGL und OpenAL, offene Industriestandards für 3D-Computergrafik und -Audio, zu ergänzen, indem die Leistung der GPU genutzt wird. OpenCL wird vom gemeinnützigen Konsortium Khronos Group entwickelt und gepflegt, zu dem viele große Unternehmen gehören, darunter Apple, AMD, Intel, nVidia, Sun Microsystems, Sony Computer Entertainment und andere.

CAL/IL (Compute Abstraction Layer/Intermediate Language)

Bei der ATI Stream-Technologie handelt es sich um eine Reihe von Hardware- und Softwaretechnologien, die es ermöglichen, AMD-GPUs in Verbindung mit einer CPU zu verwenden, um viele Anwendungen (nicht nur Grafiken) zu beschleunigen.

Zu den Anwendungen für ATI Stream gehören rechenintensive Anwendungen wie Finanzanalysen oder seismische Datenverarbeitung. Durch den Einsatz eines Stream-Prozessors konnte die Geschwindigkeit einiger Finanzberechnungen im Vergleich zur Lösung desselben Problems nur mit dem Zentralprozessor um das 55-fache gesteigert werden.

NVIDIA betrachtet die ATI Stream-Technologie nicht als sehr starken Konkurrenten. CUDA und Stream sind zwei verschiedene Technologien, die sich auf unterschiedlichen Entwicklungsstufen befinden. Die Programmierung für ATI-Produkte ist viel komplexer – ihre Sprache ähnelt eher der Assemblersprache. CUDA C wiederum ist eine viel höhere Sprache. Das Schreiben darauf ist bequemer und einfacher. Dies ist für große Entwicklungsunternehmen sehr wichtig. Wenn wir über Leistung sprechen, können wir sehen, dass der Spitzenwert bei ATI-Produkten höher ist als bei NVIDIA-Lösungen. Aber auch hier kommt es darauf an, wie man diese Kraft erlangt.

DirectX11 (DirectCompute)

Eine Anwendungsprogrammierschnittstelle, die Teil von DirectX ist, einer Reihe von APIs von Microsoft, die für die Ausführung auf IBM PC-kompatiblen Computern mit Microsoft Windows-Betriebssystemen konzipiert sind. DirectCompute ist für die Durchführung allgemeiner Datenverarbeitung auf GPUs konzipiert, einer Implementierung des GPGPU-Konzepts. DirectCompute wurde ursprünglich als Teil von DirectX 11 veröffentlicht, wurde aber später für DirectX 10 und DirectX 10.1 verfügbar.

NVDIA CUDA in der russischen Wissenschaftsgemeinschaft.

Seit Dezember 2009 wird das CUDA-Softwaremodell an 269 Universitäten weltweit gelehrt. In Russland werden Schulungen zu CUDA an den staatlichen Universitäten Moskau, St. Petersburg, Kasan, Nowosibirsk und Perm, der Internationalen Universität für die Natur der Gesellschaft und des Menschen „Dubna“, dem Gemeinsamen Institut für Kernforschung und dem Moskauer Institut für Elektronik angeboten Technologie, Staatliche Energieuniversität Iwanowo, BSTU. V. G. Shukhov, MSTU im. Bauman, nach ihm benannte Russische Chemisch-Technische Universität. Mendeleev, Russisches Wissenschaftszentrum „Kurchatov-Institut“, Interregionales Supercomputerzentrum der Russischen Akademie der Wissenschaften, Taganrog Technological Institute (TTI SFU).

Ich hatte einmal die Gelegenheit, auf dem Computermarkt mit dem technischen Direktor eines der vielen Unternehmen zu sprechen, die Laptops verkaufen. Dieser „Spezialist“ versuchte mit Schaum vor dem Mund genau zu erklären, welche Laptop-Konfiguration ich brauchte. Die Hauptbotschaft seines Monologs war, dass die Zeit der Zentraleinheiten (CPUs) vorbei ist und alle Anwendungen jetzt aktiv Berechnungen auf dem Grafikprozessor (GPU) nutzen und die Leistung eines Laptops daher vollständig von der GPU und Ihnen abhängt Ich muss nicht auf die CPU achten. Als mir klar wurde, dass es absolut sinnlos war, mit diesem technischen Direktor zu streiten und zu argumentieren, verschwendete ich keine Zeit und kaufte den Laptop, den ich brauchte, in einem anderen Pavillon. Allerdings hat mich schon die Tatsache der offensichtlichen Inkompetenz des Verkäufers beeindruckt. Es wäre verständlich, wenn er mich als Käufer täuschen wollte. Gar nicht. Er glaubte aufrichtig an das, was er sagte. Ja, anscheinend essen die Vermarkter von NVIDIA und AMD ihr Brot aus einem bestimmten Grund, und sie haben es geschafft, einigen Benutzern die Vorstellung von der dominanten Rolle des Grafikprozessors in einem modernen Computer zu vermitteln.

Die Tatsache, dass GPU-Computing (Grafikprozessor) heutzutage immer beliebter wird, steht außer Zweifel. Dies schmälert jedoch keineswegs die Rolle des Zentralprozessors. Wenn wir über die überwiegende Mehrheit der Benutzeranwendungen sprechen, hängt ihre Leistung heute außerdem vollständig von der CPU-Leistung ab. Das heißt, die überwiegende Mehrheit der Benutzeranwendungen nutzt kein GPU-Computing.

Im Allgemeinen wird GPU-Computing hauptsächlich auf speziellen HPC-Systemen für wissenschaftliches Rechnen durchgeführt. Aber Benutzeranwendungen, die GPU-Computing verwenden, können einerseits abgezählt werden. Es sei gleich darauf hingewiesen, dass der Begriff „GPU-Computing“ in diesem Fall nicht ganz korrekt ist und irreführend sein kann. Tatsache ist: Wenn eine Anwendung GPU-Computing verwendet, bedeutet dies nicht, dass der Zentralprozessor im Leerlauf ist. Beim GPU-Computing wird die Last nicht vom Zentralprozessor auf den Grafikprozessor übertragen. In der Regel bleibt der Zentralprozessor ausgelastet, und der Einsatz eines Grafikprozessors zusammen mit dem Zentralprozessor kann die Leistung verbessern, also die Zeit verkürzen, die zum Erledigen einer Aufgabe benötigt wird. Darüber hinaus fungiert die GPU selbst hier als eine Art Coprozessor für die CPU, ersetzt diese jedoch keinesfalls vollständig.

Um zu verstehen, warum GPU-Computing kein Allheilmittel ist und warum es falsch ist zu sagen, dass seine Rechenleistung die der CPU übersteigt, muss man den Unterschied zwischen einem Zentralprozessor und einem Grafikprozessor verstehen.

Unterschiede in GPU- und CPU-Architekturen

CPU-Kerne sind darauf ausgelegt, einen einzelnen Strom sequenzieller Befehle mit maximaler Leistung auszuführen, während GPU-Kerne darauf ausgelegt sind, schnell eine sehr große Anzahl paralleler Befehlsströme auszuführen. Dies ist der grundlegende Unterschied zwischen GPUs und Zentralprozessoren. Die CPU ist ein Allzweck- oder Allzweckprozessor, der für hohe Leistung aus einem einzigen Befehlsstrom optimiert ist und sowohl Ganzzahlen als auch Gleitkommazahlen verarbeitet. In diesem Fall erfolgt der Zugriff auf den Speicher mit Daten und Anweisungen überwiegend zufällig.

Um die CPU-Leistung zu verbessern, sind sie darauf ausgelegt, möglichst viele Anweisungen parallel auszuführen. Zu diesem Zweck verwenden die Prozessorkerne beispielsweise eine Out-of-Order-Befehlsausführungseinheit, die es ermöglicht, Befehle in der Reihenfolge, in der sie empfangen wurden, neu anzuordnen, was es ermöglicht, den Grad der Parallelität zu erhöhen Implementierung von Anweisungen auf der Ebene eines Threads. Dies ermöglicht jedoch immer noch nicht die parallele Ausführung einer großen Anzahl von Anweisungen, und der Overhead der Parallelisierung von Anweisungen innerhalb des Prozessorkerns erweist sich als sehr erheblich. Aus diesem Grund verfügen Allzweckprozessoren nicht über eine sehr große Anzahl von Ausführungseinheiten.

Der Grafikprozessor ist grundlegend anders konzipiert. Es wurde ursprünglich für die Ausführung einer großen Anzahl paralleler Befehlsströme entwickelt. Darüber hinaus sind diese Befehlsströme von Anfang an parallelisiert, und es fallen einfach keine Mehrkosten für die Parallelisierung von Anweisungen in der GPU an. Die GPU ist für das Rendern von Bildern ausgelegt. Vereinfacht ausgedrückt nimmt es eine Gruppe von Polygonen als Eingabe, führt alle erforderlichen Operationen aus und gibt Pixel aus. Die Verarbeitung von Polygonen und Pixeln erfolgt unabhängig voneinander; sie können parallel und getrennt voneinander verarbeitet werden. Daher verwendet die GPU aufgrund der inhärent parallelen Arbeitsorganisation eine große Anzahl von Ausführungseinheiten, die im Gegensatz zum sequentiellen Befehlsfluss für die CPU leicht zu laden sind.

Grafik- und Zentralprozessoren unterscheiden sich auch in den Prinzipien des Speicherzugriffs. In einer GPU ist der Speicherzugriff leicht vorhersehbar: Wenn ein Texturtexel aus dem Speicher gelesen wird, kommt nach einiger Zeit die Frist für benachbarte Texel. Bei der Aufnahme passiert das Gleiche: Wird ein Pixel in den Framebuffer geschrieben, wird nach einigen Taktzyklen das daneben liegende Pixel geschrieben. Daher benötigt die GPU im Gegensatz zur CPU einfach keinen großen Cache-Speicher und Texturen benötigen nur wenige Kilobyte. Auch das Prinzip der Arbeit mit Speicher unterscheidet sich bei GPUs und CPUs. Alle modernen GPUs verfügen also über mehrere Speichercontroller und der Grafikspeicher selbst ist schneller, sodass GPUs viel mehr haben O größere Speicherbandbreite im Vergleich zu Universalprozessoren, was auch für parallele Berechnungen mit großen Datenströmen sehr wichtig ist.

In Universalprozessoren O Der größte Teil der Chipfläche wird von verschiedenen Befehls- und Datenpuffern, Dekodiereinheiten, Hardware-Verzweigungsvorhersageeinheiten, Befehlsneuordnungseinheiten und Cache-Speichern der ersten, zweiten und dritten Ebene eingenommen. Alle diese Hardwareeinheiten werden benötigt, um die Ausführung einiger Befehlsthreads zu beschleunigen, indem sie auf Prozessorkernebene parallelisiert werden.

Die Ausführungseinheiten selbst nehmen in einem Universalprozessor relativ wenig Platz ein.

Bei einem Grafikprozessor hingegen wird der Hauptbereich von zahlreichen Ausführungseinheiten eingenommen, was die gleichzeitige Verarbeitung mehrerer tausend Befehlsthreads ermöglicht.

Wir können sagen, dass GPUs im Gegensatz zu modernen CPUs für parallele Berechnungen mit einer Vielzahl von Rechenoperationen ausgelegt sind.

Es ist möglich, die Rechenleistung von GPUs für nichtgrafische Aufgaben zu nutzen, allerdings nur, wenn das zu lösende Problem die Möglichkeit bietet, Algorithmen über Hunderte von in der GPU verfügbaren Ausführungseinheiten hinweg zu parallelisieren. Insbesondere GPU-Berechnungen zeigen hervorragende Ergebnisse, wenn dieselbe Abfolge mathematischer Operationen auf eine große Datenmenge angewendet wird. In diesem Fall werden die besten Ergebnisse erzielt, wenn das Verhältnis der Anzahl der Rechenbefehle zur Anzahl der Speicherzugriffe ausreichend groß ist. Dieser Vorgang erfordert weniger Ausführungskontrolle und keinen großen Cache-Speicher.

Es gibt viele Beispiele für wissenschaftliche Berechnungen, bei denen der Vorteil der GPU gegenüber der CPU hinsichtlich der Recheneffizienz unbestreitbar ist. Somit sind viele wissenschaftliche Anwendungen in der molekularen Modellierung, Gasdynamik, Fluiddynamik und anderen perfekt für Berechnungen auf der GPU geeignet.

Wenn also der Algorithmus zur Lösung eines Problems in Tausende einzelner Threads parallelisiert werden kann, kann die Effizienz der Lösung eines solchen Problems mit einer GPU höher sein als die Lösung mit nur einem Allzweckprozessor. Allerdings kann man die Lösung eines Problems nicht so einfach von der CPU auf die GPU übertragen, schon allein deshalb, weil CPU und GPU unterschiedliche Befehle verwenden. Das heißt, wenn ein Programm für eine Lösung auf einer CPU geschrieben wird, wird der x86-Befehlssatz verwendet (oder ein Befehlssatz, der mit einer bestimmten Prozessorarchitektur kompatibel ist), aber für die GPU werden völlig andere Befehlssätze verwendet, die wiederum berücksichtigt werden Berücksichtigen Sie dessen Architektur und Fähigkeiten. Bei der Entwicklung moderner 3D-Spiele kommen die DirectX- und OpenGL-APIs zum Einsatz, die es Programmierern ermöglichen, mit Shadern und Texturen zu arbeiten. Allerdings ist die Verwendung der DirectX- und OpenGL-APIs für nichtgrafisches Computing auf der GPU nicht die beste Option.

NVIDIA CUDA und AMD APP

Aus diesem Grund entstand der BrookGPU-Compiler, als die ersten Versuche begannen, nichtgrafisches Computing auf der GPU (General Purpose GPU, GPGPU) zu implementieren. Vor seiner Erstellung mussten Entwickler über die OpenGL- oder Direct3D-Grafik-API auf Grafikkartenressourcen zugreifen, was den Programmierprozess erheblich erschwerte, da spezifische Kenntnisse erforderlich waren – sie mussten die Prinzipien der Arbeit mit 3D-Objekten (Shader, Texturen usw.) erlernen. ). Dies war der Grund für den sehr begrenzten Einsatz von GPGPU in Softwareprodukten. BrookGPU ist zu einer Art „Übersetzer“ geworden. Diese Streaming-Erweiterungen der C-Sprache verbargen die 3D-API vor Programmierern, und bei deren Verwendung waren Kenntnisse in der 3D-Programmierung praktisch nicht mehr erforderlich. Die Rechenleistung von Grafikkarten steht Programmierern in Form eines zusätzlichen Coprozessors für parallele Berechnungen zur Verfügung. Der BrookGPU-Compiler verarbeitete die Datei mit C-Code und Erweiterungen und erstellte Code, der an eine Bibliothek mit DirectX- oder OpenGL-Unterstützung gebunden ist.

Vor allem dank BrookGPU haben NVIDIA und ATI (jetzt AMD) auf die neue Technologie des Allzweck-Computing auf GPUs aufmerksam gemacht und mit der Entwicklung eigener Implementierungen begonnen, die einen direkten und transparenteren Zugriff auf die Recheneinheiten von 3D-Beschleunigern ermöglichen.

Aus diesem Grund hat NVIDIA eine Hardware- und Softwarearchitektur für paralleles Computing entwickelt, CUDA (Compute Unified Device Architecture). Die CUDA-Architektur ermöglicht Nicht-Grafik-Computing auf NVIDIA-GPUs.

Die Veröffentlichung der öffentlichen Betaversion des CUDA SDK erfolgte im Februar 2007. Die CUDA-API basiert auf einem vereinfachten Dialekt der C-Sprache. Die CUDA SDK-Architektur ermöglicht es Programmierern, Algorithmen zu implementieren, die auf NVIDIA-GPUs laufen und spezielle Funktionen in den C-Programmtext einbinden. Um Code erfolgreich in diese Sprache zu übersetzen, enthält das CUDA SDK NVIDIAs eigenen nvcc-Befehlszeilen-Compiler.

CUDA ist eine plattformübergreifende Software für Betriebssysteme wie Linux, Mac OS X und Windows.

AMD (ATI) hat auch eine eigene Version der GPGPU-Technologie entwickelt, die früher ATI Stream und jetzt AMD Accelerated Parallel Processing (APP) hieß. Die AMD APP basiert auf dem offenen Industriestandard OpenCL (Open Computing Language). Der OpenCL-Standard bietet Parallelität auf Befehls- und Datenebene und ist eine Implementierung der GPGPU-Technik. Es handelt sich um einen völlig offenen Standard und die Nutzung ist lizenzgebührenfrei. Beachten Sie, dass AMD APP und NVIDIA CUDA nicht miteinander kompatibel sind. Die neueste Version von NVIDIA CUDA unterstützt jedoch auch OpenCL.

Testen von GPGPU in Videokonvertern

Wir haben also herausgefunden, dass die CUDA-Technologie zur Implementierung von GPGPU auf NVIDIA-GPUs verwendet wird und die APP-API auf AMD-GPUs verwendet wird. Wie bereits erwähnt, ist der Einsatz von nicht-grafischem Computing auf der GPU nur dann sinnvoll, wenn das zu lösende Problem in viele Threads parallelisiert werden kann. Die meisten Benutzeranwendungen erfüllen dieses Kriterium jedoch nicht. Es gibt jedoch einige Ausnahmen. Beispielsweise unterstützen die meisten modernen Videokonverter die Möglichkeit, Berechnungen auf NVIDIA- und AMD-GPUs zu verwenden.

Um herauszufinden, wie effizient GPU-Computing in benutzerdefinierten Videokonvertern eingesetzt wird, haben wir drei beliebte Lösungen ausgewählt: Xilisoft Video Converter Ultimate 7.7.2, Wondershare Video Converter Ultimate 6.0.3.2 und Movavi Video Converter 10.2.1. Diese Konverter unterstützen die Möglichkeit, NVIDIA- und AMD-GPUs zu verwenden. Sie können diese Funktion in den Videokonvertereinstellungen deaktivieren, um die Effektivität der Verwendung der GPU zu bewerten.

Für die Videokonvertierung haben wir drei verschiedene Videos verwendet.

Das erste Video war 3 Minuten 35 Sekunden lang und 1,05 GB groß. Es wurde im MKV-Datenspeicherformat (Container) aufgezeichnet und hatte die folgenden Eigenschaften:

  • Video:
    • Format - MPEG4-Video (H264),
    • Auflösung - 1920*um*1080,
    • Bitratenmodus - Variabel,
    • durchschnittliche Videobitrate - 42,1 Mbit/s,
    • maximale Videobitrate - 59,1 Mbit/s,
    • Bildrate - 25 fps;
  • Audio:
    • Format - MPEG-1 Audio,
    • Audio-Bitrate - 128 Kbit/s,
    • Anzahl der Kanäle - 2,

Das zweite Video hatte eine Dauer von 4 Minuten 25 Sekunden und eine Größe von 1,98 GB. Es wurde im MPG-Datenspeicherformat (Container) aufgezeichnet und wies folgende Eigenschaften auf:

  • Video:
    • Format - MPEG-PS (MPEG2-Video),
    • Auflösung - 1920*um*1080,
    • Bitratenmodus – Variabel.
    • durchschnittliche Videobitrate - 62,5 Mbit/s,
    • maximale Videobitrate - 100 Mbit/s,
    • Bildrate - 25 fps;
  • Audio:
    • Format - MPEG-1 Audio,
    • Audio-Bitrate - 384 Kbit/s,
    • Anzahl der Kanäle - 2,

Das dritte Video hatte eine Dauer von 3 Minuten 47 Sekunden und eine Größe von 197 MB. Es wurde im MOV-Datenspeicherformat (Container) geschrieben und hatte die folgenden Eigenschaften:

  • Video:
    • Format - MPEG4-Video (H264),
    • Auflösung - 1920*um*1080,
    • Bitratenmodus - Variabel,
    • Videobitrate - 7024 Kbit/s,
    • Bildrate - 25 fps;
  • Audio:
    • Format - AAC,
    • Audio-Bitrate - 256 Kbit/s,
    • Anzahl der Kanäle - 2,
    • Abtastfrequenz - 48 kHz.

Alle drei Testvideos wurden mithilfe von Videokonvertern in das MP4-Datenspeicherformat (H.264-Codec) zur Anzeige auf dem iPad 2-Tablet konvertiert. Die Auflösung der ausgegebenen Videodatei betrug 1280*um*720.

Bitte beachten Sie, dass wir nicht in allen drei Konvertern exakt die gleichen Konvertierungseinstellungen verwendet haben. Aus diesem Grund ist es falsch, die Effizienz von Videokonvertern selbst anhand der Konvertierungszeit zu vergleichen. Daher wurde im Videokonverter Xilisoft Video Converter Ultimate 7.7.2 die iPad 2-Voreinstellung - H.264 HD Video für die Konvertierung verwendet. Diese Voreinstellung verwendet die folgenden Kodierungseinstellungen:

  • Codec - MPEG4 (H.264);
  • Auflösung - 1280*um*720;
  • Bildrate - 29,97 fps;
  • Videobitrate - 5210 Kbit/s;
  • Audio-Codec – AAC;
  • Audio-Bitrate – 128 Kbit/s;
  • Anzahl der Kanäle - 2;
  • Abtastfrequenz - 48 kHz.

Wondershare Video Converter Ultimate 6.0.3.2 verwendete die iPad 2-Voreinstellung mit den folgenden zusätzlichen Einstellungen:

  • Codec - MPEG4 (H.264);
  • Auflösung - 1280*um*720;
  • Bildrate - 30 fps;
  • Videobitrate - 5000 Kbit/s;
  • Audio-Codec – AAC;
  • Audio-Bitrate – 128 Kbit/s;
  • Anzahl der Kanäle - 2;
  • Abtastfrequenz - 48 kHz.

Movavi Video Converter 10.2.1 verwendete die iPad-Voreinstellung (1280*um*720, H.264) (*.mp4) mit den folgenden zusätzlichen Einstellungen:

  • Videoformat - H.264;
  • Auflösung - 1280*um*720;
  • Bildrate - 30 fps;
  • Videobitrate - 2500 Kbit/s;
  • Audio-Codec – AAC;
  • Audio-Bitrate – 128 Kbit/s;
  • Anzahl der Kanäle - 2;
  • Abtastfrequenz - 44,1 kHz.

Jedes Quellvideo wurde auf jedem der Videokonverter fünfmal konvertiert, wobei sowohl die GPU als auch nur die CPU verwendet wurden. Nach jeder Konvertierung wurde der Computer neu gestartet.

Infolgedessen wurde jedes Video in jedem Videokonverter zehnmal konvertiert. Um diese Routinearbeit zu automatisieren, wurde ein spezielles Dienstprogramm mit grafischer Oberfläche geschrieben, mit dem Sie den Testprozess vollständig automatisieren können.

Prüfstandkonfiguration

Der Prüfstand hatte folgenden Aufbau:

  • Prozessor - Intel Core i7-3770K;
  • Hauptplatine - Gigabyte GA-Z77X-UD5H;
  • Motherboard-Chipsatz - Intel Z77 Express;
  • Speicher - DDR3-1600;
  • Speicherkapazität - 8 GB (zwei GEIL-Module mit je 4 GB);
  • Speicherbetriebsmodus - Dual-Channel;
  • Grafikkarte - NVIDIA GeForce GTX 660Ti (Grafiktreiber 314.07);
  • Laufwerk - Intel SSD 520 (240 GB).

Auf dem Stand wurde das Betriebssystem Windows 7 Ultimate (64-Bit) installiert.

Zunächst haben wir den Prozessor und alle anderen Systemkomponenten im Normalmodus getestet. Gleichzeitig lief der Intel Core i7-3770K-Prozessor bei aktiviertem Turbo-Boost-Modus mit einer Standardfrequenz von 3,5 GHz (die maximale Prozessorfrequenz im Turbo-Boost-Modus beträgt 3,9 GHz).

Anschließend wiederholten wir den Testvorgang, wobei der Prozessor jedoch auf eine feste Frequenz von 4,5 GHz übertaktet wurde (ohne den Turbo-Boost-Modus zu verwenden). Dadurch konnte die Abhängigkeit der Konvertierungsgeschwindigkeit von der Prozessorfrequenz (CPU) ermittelt werden.

In der nächsten Testphase kehrten wir zu den Standardprozessoreinstellungen zurück und wiederholten Tests mit anderen Grafikkarten:

  • NVIDIA GeForce GTX 280 (Treiber 314.07);
  • NVIDIA GeForce GTX 460 (Treiber 314.07);
  • AMD Radeon HD6850 (Treiber 13.1).

Somit wurde die Videokonvertierung auf vier Grafikkarten unterschiedlicher Architektur durchgeführt.

Die ältere NVIDIA GeForce 660Ti-Grafikkarte basiert auf einem gleichnamigen Grafikprozessor mit der Codierung GK104 (Kepler-Architektur), der mit einer 28-nm-Prozesstechnologie hergestellt wird. Diese GPU enthält 3,54 Milliarden Transistoren und hat eine Die-Fläche von 294 mm2.

Denken Sie daran, dass der GK104-Grafikprozessor vier Grafikverarbeitungscluster (Graphics Processing Clusters, GPC) umfasst. GPC-Cluster sind unabhängige Geräte innerhalb des Prozessors und können als separate Geräte betrieben werden, da sie über alle erforderlichen Ressourcen verfügen: Rasterer, Geometrie-Engines und Texturmodule.

Jeder dieser Cluster verfügt über zwei SMX-Multiprozessoren (Streaming Multiprocessor), aber im GK104-Prozessor ist ein Multiprozessor in einem der Cluster blockiert, sodass es insgesamt sieben SMX-Multiprozessoren gibt.

Jeder SMX-Streaming-Multiprozessor enthält 192 Streaming-Rechenkerne (CUDA-Kerne), sodass der GK104-Prozessor insgesamt über 1344 CUDA-Kerne verfügt. Darüber hinaus enthält jeder SMX-Multiprozessor 16 Textureinheiten (TMU), 32 Spezialfunktionseinheiten (SFU), 32 Load-Store-Einheiten (LSU), eine PolyMorph-Engine und vieles mehr.

Die GeForce GTX 460 basiert auf einem GPU-codierten GF104 basierend auf der Fermi-Architektur. Dieser Prozessor wird mit einer 40-nm-Prozesstechnologie hergestellt und enthält etwa 1,95 Milliarden Transistoren.

Die GF104-GPU umfasst zwei GPC-Grafikverarbeitungscluster. Jeder von ihnen verfügt über vier SM-Streaming-Multiprozessoren, aber im GF104-Prozessor in einem der Cluster ist ein Multiprozessor gesperrt, sodass es nur sieben SM-Multiprozessoren gibt.

Jeder SM-Streaming-Multiprozessor enthält 48 Streaming-Rechenkerne (CUDA-Kerne), sodass der GK104-Prozessor insgesamt über 336 CUDA-Kerne verfügt. Darüber hinaus enthält jeder SM-Multiprozessor acht Textureinheiten (TMU), acht Spezialfunktionseinheiten (SFU), 16 Load-Store-Einheiten (LSU), eine PolyMorph-Engine und vieles mehr.

Die GeForce GTX 280 GPU ist Teil der zweiten Generation der Unified GPU Architecture von NVIDIA und unterscheidet sich in ihrer Architektur stark von Fermi und Kepler.

Die GeForce GTX 280 GPU besteht aus Texture Processing Clusters (TPCs), die zwar ähnlich sind, sich aber stark von den GPC-Grafikverarbeitungsclustern in den Fermi- und Kepler-Architekturen unterscheiden. Insgesamt gibt es zehn solcher Cluster im GeForce-GTX-280-Prozessor. Jeder TPC-Cluster umfasst drei SM-Streaming-Multiprozessoren und acht Textur-Sampling- und Filtereinheiten (TMU). Jeder Multiprozessor besteht aus acht Stream-Prozessoren (SP). Multiprozessoren enthalten auch Blöcke zum Abtasten und Filtern von Texturdaten, die sowohl für Grafiken als auch für einige Rechenaufgaben verwendet werden.

So gibt es in einem TPC-Cluster 24 Stream-Prozessoren, in der GeForce GTX 280 GPU sind es bereits 240 davon.

Die zusammenfassenden Eigenschaften der im Test verwendeten Grafikkarten auf NVIDIA-GPUs sind in der Tabelle aufgeführt.

In der folgenden Tabelle ist die Grafikkarte AMD Radeon HD6850 nicht enthalten, was ganz natürlich ist, da ihre technischen Eigenschaften schwer mit denen von NVIDIA-Grafikkarten zu vergleichen sind. Daher werden wir es gesondert betrachten.

Die AMD Radeon HD6850 GPU mit dem Codenamen Barts wird mit einer 40-nm-Prozesstechnologie hergestellt und enthält 1,7 Milliarden Transistoren.

Die AMD Radeon HD6850-Prozessorarchitektur ist eine einheitliche Architektur mit einer Reihe gemeinsamer Prozessoren für die Streaming-Verarbeitung mehrerer Datentypen.

Der AMD Radeon HD6850 Prozessor besteht aus 12 SIMD-Kernen, von denen jeder 16 superskalare Stream-Prozessoreinheiten und vier Textureinheiten enthält. Jeder superskalare Stream-Prozessor enthält fünf Allzweck-Stream-Prozessoren. Insgesamt verfügt die AMD Radeon HD6850 GPU also über 12*um*16*um*5=960 Universal-Stream-Prozessoren.

Die GPU-Frequenz der AMD Radeon HD6850-Grafikkarte beträgt 775 MHz und die effektive GDDR5-Speicherfrequenz beträgt 4000 MHz. Die Speicherkapazität beträgt 1024 MB.

Testergebnisse

Schauen wir uns also die Testergebnisse an. Beginnen wir mit dem ersten Test, bei dem wir die NVIDIA GeForce GTX 660Ti-Grafikkarte und den Standardbetriebsmodus des Intel Core i7-3770K-Prozessors verwenden.

In Abb. 1-3 zeigen die Ergebnisse der Konvertierung von drei Testvideos mit drei Konvertern im Modus mit und ohne GPU.

Wie aus den Testergebnissen hervorgeht, ist der Effekt der Verwendung der GPU offensichtlich. Für Xilisoft Video Converter Ultimate 7.7.2 reduziert sich die Konvertierungszeit bei Verwendung einer GPU um 14, 9 bzw. 19 % für das erste, zweite und dritte Video.

Beim Wondershare Video Converter Ultimate 6.0.32 reduziert die Verwendung einer GPU die Konvertierungszeit um 10 %, 13 % bzw. 23 % für das erste, zweite und dritte Video.

Der Konverter, der am meisten von der Verwendung eines Grafikprozessors profitiert, ist jedoch Movavi Video Converter 10.2.1. Für das erste, zweite und dritte Video beträgt die Reduzierung der Konvertierungszeit 64, 81 bzw. 41 %.

Es ist klar, dass der Nutzen der Verwendung einer GPU sowohl vom Quellvideo als auch von den Videokonvertierungseinstellungen abhängt, was unsere Ergebnisse tatsächlich belegen.

Sehen wir uns nun an, wie hoch der Zeitgewinn bei der Umwandlung ist, wenn der Intel Core i7-3770K-Prozessor auf 4,5 GHz übertaktet wird. Geht man davon aus, dass im Normalmodus alle Prozessorkerne bei der Konvertierung belastet werden und im Turbo-Boost-Modus mit einer Frequenz von 3,7 GHz arbeiten, dann entspricht eine Erhöhung der Frequenz auf 4,5 GHz einer Frequenzübertaktung von 22 %.

In Abb. In Abb. 4-6 zeigen die Ergebnisse der Konvertierung von drei Testvideos beim Übertakten des Prozessors in den Modi mit und ohne Grafikprozessor. In diesem Fall ermöglicht der Einsatz eines Grafikprozessors einen Zeitgewinn bei der Konvertierung.

Beim Videokonverter Xilisoft Video Converter Ultimate 7.7.2 wird bei Verwendung einer GPU die Konvertierungszeit für das erste, zweite und dritte Video um 15, 9 bzw. 20 % reduziert.

Beim Wondershare Video Converter Ultimate 6.0.32 kann die Verwendung einer GPU die Konvertierungszeit für das erste, zweite und dritte Video um 10, 10 bzw. 20 % verkürzen.

Für Movavi Video Converter 10.2.1 kann die Verwendung eines Grafikprozessors die Konvertierungszeit um 59, 81 bzw. 40 % verkürzen.

Natürlich ist es interessant zu sehen, wie CPU-Übertaktung die Konvertierungszeiten mit und ohne GPU verkürzen kann.

In Abb. Die Abbildungen 7-9 zeigen die Ergebnisse des Vergleichs der Zeit für die Konvertierung von Videos ohne Verwendung eines Grafikprozessors im normalen Prozessormodus und im übertakteten Modus. Da in diesem Fall die Konvertierung nur von der CPU ohne Berechnungen auf der GPU durchgeführt wird, liegt es auf der Hand, dass eine Erhöhung der Prozessortaktfrequenz zu einer Verkürzung der Konvertierungszeit (Erhöhung der Konvertierungsgeschwindigkeit) führt. Ebenso offensichtlich ist, dass die Reduzierung der Konvertierungsgeschwindigkeit bei allen Testvideos ungefähr gleich sein sollte. So wird beim Videokonverter Xilisoft Video Converter Ultimate 7.7.2 beim Übertakten des Prozessors die Konvertierungszeit für das erste, zweite und dritte Video um 9, 11 bzw. 9 % reduziert. Für Wondershare Video Converter Ultimate 6.0.32 wird die Konvertierungszeit für das erste, zweite und dritte Video um 9, 9 bzw. 10 % reduziert. Nun, für den Videokonverter Movavi Video Converter 10.2.1 wird die Konvertierungszeit um 13, 12 bzw. 12 % reduziert.

Bei einer Übertaktung der Prozessorfrequenz um 20 % reduziert sich die Konvertierungszeit also um ca. 10 %.

Vergleichen wir die Zeit zum Konvertieren von Videos mit einem Grafikprozessor im normalen Prozessormodus und im Übertaktungsmodus (Abb. 10-12).

Beim Videokonverter Xilisoft Video Converter Ultimate 7.7.2 wird beim Übertakten des Prozessors die Konvertierungszeit für das erste, zweite und dritte Video um 10, 10 bzw. 9 % reduziert. Für Wondershare Video Converter Ultimate 6.0.32 wird die Konvertierungszeit für das erste, zweite und dritte Video um 9, 6 bzw. 5 % reduziert. Nun, für den Videokonverter Movavi Video Converter 10.2.1 wird die Konvertierungszeit um 0,2, 10 bzw. 10 % reduziert.

Wie Sie sehen können, ist bei den Konvertern Xilisoft Video Converter Ultimate 7.7.2 und Wondershare Video Converter Ultimate 6.0.32 die Verkürzung der Konvertierungszeit beim Übertakten des Prozessors sowohl bei Verwendung eines Grafikprozessors als auch ohne Verwendung ungefähr gleich logisch, da diese Konverter das GPU-Computing nicht sehr effizient nutzen. Beim Movavi Video Converter 10.2.1, der GPU-Computing effektiv nutzt, hat die Übertaktung des Prozessors im GPU-Computing-Modus jedoch kaum Auswirkungen auf die Verkürzung der Konvertierungszeit, was auch verständlich ist, da in diesem Fall die Hauptlast auf dem Grafikprozessor liegt .

Schauen wir uns nun die Testergebnisse mit verschiedenen Grafikkarten an.

Es scheint, dass die Videokonvertierung bei Verwendung eines Grafikprozessors umso effektiver sein sollte, je leistungsfähiger die Grafikkarte und je mehr CUDA-Kerne (oder Universal-Stream-Prozessoren für AMD-Grafikkarten) im Grafikprozessor sind. Aber in der Praxis klappt das nicht ganz so.

Bei Grafikkarten, die auf NVIDIA-GPUs basieren, ist die Situation wie folgt. Bei Verwendung der Konverter Xilisoft Video Converter Ultimate 7.7.2 und Wondershare Video Converter Ultimate 6.0.32 hängt die Konvertierungszeit praktisch in keiner Weise vom Typ der verwendeten Grafikkarte ab. Das heißt, für die Grafikkarten NVIDIA GeForce GTX 660Ti, NVIDIA GeForce GTX 460 und NVIDIA GeForce GTX 280 im GPU-Rechenmodus ist die Konvertierungszeit gleich (Abb. 13-15).

Reis. 1. Ergebnisse der ersten Konvertierung
Testvideo im Normalmodus
Prozessorbetrieb

Prozessor auf Grafikkarten im GPU-Modus

Reis. 14. Ergebnisse des Vergleichs der Konvertierungszeit des zweiten Videos

Reis. 15. Ergebnisse des Vergleichs der Konvertierungszeit des dritten Videos
auf verschiedenen Grafikkarten im GPU-Modus

Dies kann nur durch die Tatsache erklärt werden, dass der in den Konvertern Xilisoft Video Converter Ultimate 7.7.2 und Wondershare Video Converter Ultimate 6.0.32 implementierte GPU-Berechnungsalgorithmus einfach ineffektiv ist und nicht die aktive Nutzung aller Grafikkerne ermöglicht. Dies erklärt übrigens genau die Tatsache, dass bei diesen Konvertern der Unterschied in der Konvertierungszeit zwischen den Modi mit und ohne GPU-Nutzung gering ist.

In Movavi Video Converter 10.2.1 ist die Situation etwas anders. Wie wir uns erinnern, ist dieser Konverter in der Lage, GPU-Berechnungen sehr effizient zu nutzen, und daher hängt die Konvertierungszeit im GPU-Modus vom Typ der verwendeten Grafikkarte ab.

Aber mit der AMD Radeon HD 6850 Grafikkarte ist alles wie gewohnt. Entweder ist der Grafikkartentreiber „schief“ oder die in den Konvertern implementierten Algorithmen müssen erheblich verbessert werden, aber wenn GPU-Computing verwendet wird, verbessern sich die Ergebnisse entweder nicht oder werden schlechter.

Genauer gesagt stellt sich die Situation wie folgt dar. Wenn für Xilisoft Video Converter Ultimate 7.7.2 eine GPU zum Konvertieren des ersten Testvideos verwendet wird, erhöht sich die Konvertierungszeit um 43 % und bei der Konvertierung des zweiten Videos um 66 %.

Darüber hinaus zeichnet sich Xilisoft Video Converter Ultimate 7.7.2 auch durch instabile Ergebnisse aus. Die Schwankung der Konvertierungszeit kann bis zu 40 % betragen! Deshalb haben wir alle Tests zehnmal wiederholt und das Durchschnittsergebnis berechnet.

Aber für Wondershare Video Converter Ultimate 6.0.32 und Movavi Video Converter 10.2.1 ändert sich die Konvertierungszeit überhaupt nicht, wenn eine GPU zum Konvertieren aller drei Videos verwendet wird! Es ist wahrscheinlich, dass Wondershare Video Converter Ultimate 6.0.32 und Movavi Video Converter 10.2.1 bei der Konvertierung entweder keine AMD APP-Technologie verwenden oder der AMD-Videotreiber einfach „schief“ ist, wodurch die AMD APP-Technologie nicht funktioniert .

Schlussfolgerungen

Basierend auf den Tests können die folgenden wichtigen Schlussfolgerungen gezogen werden. Moderne Videokonverter können tatsächlich GPU-Rechentechnologie nutzen, was eine höhere Konvertierungsgeschwindigkeit ermöglicht. Dies bedeutet jedoch nicht, dass alle Berechnungen vollständig auf die GPU übertragen werden und die CPU ungenutzt bleibt. Wie Tests zeigen, bleibt der Zentralprozessor beim Einsatz der GPGPU-Technologie ausgelastet, sodass der Einsatz leistungsstarker Multicore-Zentralprozessoren in Systemen zur Videokonvertierung weiterhin relevant bleibt. Die Ausnahme von dieser Regel ist die AMD APP-Technologie auf AMD-GPUs. Beispielsweise wird bei der Verwendung von Xilisoft Video Converter Ultimate 7.7.2 mit aktivierter AMD APP-Technologie zwar die Belastung der CPU reduziert, dies führt jedoch dazu, dass die Konvertierungszeit nicht verkürzt, sondern im Gegenteil erhöht wird.

Wenn wir im Allgemeinen über die Konvertierung von Videos unter zusätzlicher Verwendung eines Grafikprozessors sprechen, ist es zur Lösung dieses Problems ratsam, Grafikkarten mit NVIDIA-GPUs zu verwenden. Wie die Praxis zeigt, kann nur in diesem Fall eine Steigerung der Conversion-Geschwindigkeit erreicht werden. Darüber hinaus müssen Sie bedenken, dass die tatsächliche Steigerung der Conversion-Geschwindigkeit von vielen Faktoren abhängt. Dies sind die Eingabe- und Ausgabevideoformate und natürlich der Videokonverter selbst. Die Konverter Xilisoft Video Converter Ultimate 7.7.2 und Wondershare Video Converter Ultimate 6.0.32 sind für diese Aufgabe nicht geeignet, aber der Konverter und Movavi Video Converter 10.2.1 können die Fähigkeiten der NVIDIA-GPU sehr effektiv nutzen.

Grafikkarten auf AMD-GPUs sollten überhaupt nicht für Videokonvertierungsaufgaben verwendet werden. Im besten Fall führt dies zu keiner Erhöhung der Konvertierungsgeschwindigkeit, im schlimmsten Fall kann es zu einer Verringerung kommen.

Heutzutage sind an jeder Ecke Neuigkeiten über den Einsatz von GPUs für allgemeine Computer zu hören. Wörter wie CUDA, Stream und OpenCL sind in nur zwei Jahren fast zu den am häufigsten zitierten Wörtern im IT-Internet geworden. Allerdings weiß nicht jeder, was diese Worte bedeuten und was die Technologien dahinter bedeuten. Und für Linux-Benutzer, die es gewohnt sind, „on the fly“ zu sein, scheint das alles wie ein dunkler Wald.

Geburt von GPGPU

Wir sind es alle gewohnt zu glauben, dass die einzige Komponente eines Computers, die jeden Code ausführen kann, der ihr zugewiesen wird, der Zentralprozessor ist. Lange Zeit waren fast alle in Massenproduktion hergestellten PCs mit einem einzigen Prozessor ausgestattet, der alle erdenklichen Berechnungen erledigte, einschließlich des Betriebssystemcodes, aller unserer Software und Viren.

Später erschienen Mehrkernprozessoren und Mehrprozessorsysteme, in denen es mehrere solcher Komponenten gab. Dadurch konnten Maschinen mehrere Aufgaben gleichzeitig ausführen und die gesamte (theoretische) Systemleistung stieg genau so stark wie die Anzahl der in der Maschine installierten Kerne. Es stellte sich jedoch heraus, dass es zu schwierig und zu teuer war, Mehrkernprozessoren herzustellen und zu entwerfen.

Jeder Kern musste einen vollwertigen Prozessor einer komplexen und komplizierten x86-Architektur mit eigenem (ziemlich großem) Cache, eigener Befehlspipeline, SSE-Blöcken, vielen Blöcken zur Durchführung von Optimierungen usw. beherbergen. usw. Daher verlangsamte sich der Prozess der Erhöhung der Anzahl der Kerne erheblich, und weiße Universitätsmäntel, denen zwei oder vier Kerne eindeutig nicht ausreichten, fanden einen Weg, andere Rechenleistung für ihre wissenschaftlichen Berechnungen zu nutzen, die auf der Grafikkarte reichlich vorhanden war (Infolgedessen erschien sogar das BrookGPU-Tool, das einen zusätzlichen Prozessor mithilfe von DirectX- und OpenGL-Funktionsaufrufen emulierte.)

Grafikprozessoren, denen viele Nachteile des Zentralprozessors fehlten, erwiesen sich als hervorragende und sehr schnelle Rechenmaschinen, und sehr bald begannen die GPU-Hersteller selbst, die Entwicklungen wissenschaftlicher Köpfe (und nVidia tatsächlich angeheuert) genauer unter die Lupe zu nehmen die meisten Forscher). Als Ergebnis erschien die nVidia CUDA-Technologie, die eine Schnittstelle definiert, mit der es möglich wurde, die Berechnung komplexer Algorithmen ohne Krücken auf die Schultern der GPU zu übertragen. Später folgte ATi (AMD) mit einer eigenen Version der Technologie namens Close to Metal (jetzt Stream) und sehr bald erschien eine Standardversion von Apple namens OpenCL.

Ist die GPU alles?

Trotz aller Vorteile weist die GPGPU-Technik mehrere Probleme auf. Das erste davon ist der sehr enge Anwendungsbereich. GPUs sind dem Zentralprozessor hinsichtlich der Steigerung der Rechenleistung und der Gesamtzahl der Kerne weit voraus (Grafikkarten verfügen über eine Recheneinheit mit mehr als hundert Kernen), eine solch hohe Dichte wird jedoch durch die Maximierung der Vereinfachung des Designs erreicht des Chips selbst.

Im Wesentlichen besteht die Hauptaufgabe der GPU darin, mathematische Berechnungen mit einfachen Algorithmen durchzuführen, die nicht sehr große Mengen vorhersehbarer Daten als Eingabe erhalten. Aus diesem Grund haben GPU-Kerne ein sehr einfaches Design, geringe Cache-Größen und einen bescheidenen Befehlssatz, was letztendlich zu niedrigen Produktionskosten und der Möglichkeit einer sehr dichten Platzierung auf dem Chip führt. GPUs sind wie eine chinesische Fabrik mit Tausenden von Arbeitern. Einige einfache Dinge erledigen sie recht gut (und vor allem schnell und kostengünstig), aber wenn man ihnen den Zusammenbau eines Flugzeugs anvertraut, wird das Ergebnis höchstens ein Drachenflieger sein.

Daher besteht die erste Einschränkung von GPUs in ihrem Fokus auf schnelle mathematische Berechnungen, was den Anwendungsbereich von GPUs auf die Unterstützung beim Betrieb von Multimedia-Anwendungen sowie allen Programmen beschränkt, die an der komplexen Datenverarbeitung beteiligt sind (z. B. Archivierer oder Verschlüsselungssysteme). sowie Software für Fluoreszenzmikroskopie, Molekulardynamik, Elektrostatik und andere Dinge, die für Linux-Benutzer von geringem Interesse sind).

Das zweite Problem bei GPGPU besteht darin, dass nicht jeder Algorithmus für die Ausführung auf der GPU angepasst werden kann. Einzelne GPU-Kerne sind recht langsam und ihre Leistung entfaltet sich erst im Zusammenspiel. Das bedeutet, dass der Algorithmus so effektiv ist, wie der Programmierer ihn effektiv parallelisieren kann. In den meisten Fällen kann nur ein guter Mathematiker solche Arbeiten bewältigen, von denen es nur sehr wenige Softwareentwickler gibt.

Und drittens arbeiten GPUs mit Speicher, der auf der Grafikkarte selbst installiert ist, sodass bei jeder Verwendung der GPU zwei zusätzliche Kopiervorgänge stattfinden: Eingabedaten aus dem RAM der Anwendung selbst und Ausgabedaten aus dem GRAM zurück in den Anwendungsspeicher. Wie Sie sich vorstellen können, kann dies jegliche Vorteile in der Anwendungslaufzeit zunichte machen (wie es beim FlacCL-Tool der Fall ist, das wir später betrachten werden).

Aber das ist noch nicht alles. Trotz der Existenz eines allgemein akzeptierten Standards in Form von OpenCL bevorzugen viele Programmierer immer noch die Verwendung herstellerspezifischer Implementierungen der GPGPU-Technik. Als besonders beliebt erwies sich CUDA, das zwar eine flexiblere Programmierschnittstelle bietet (OpenCL in nVidia-Treibern ist übrigens auf CUDA implementiert), die Anwendung aber eng an Grafikkarten eines Herstellers bindet.

KGPU oder Linux-Kernel, beschleunigt durch GPU

Forscher der University of Utah haben ein KGPU-System entwickelt, das die Ausführung einiger Linux-Kernelfunktionen auf einer GPU mithilfe des CUDA-Frameworks ermöglicht. Um diese Aufgabe auszuführen, werden ein modifizierter Linux-Kernel und ein spezieller Daemon verwendet, der im Userspace läuft, Kernel-Anfragen abhört und diese mithilfe der CUDA-Bibliothek an den Grafikkartentreiber weiterleitet. Interessanterweise ist es den Autoren von KGPU trotz des erheblichen Mehraufwands, den eine solche Architektur verursacht, gelungen, eine Implementierung des AES-Algorithmus zu erstellen, die die Verschlüsselungsgeschwindigkeit des eCryptfs-Dateisystems um das Sechsfache erhöht.

Was gibt es jetzt?

Aufgrund seiner Jugend und auch aufgrund der oben beschriebenen Probleme hat sich GPGPU nie zu einer wirklich weit verbreiteten Technologie entwickelt, es gibt jedoch nützliche Software, die seine Fähigkeiten nutzt (wenn auch in winzigen Mengen). Zu den ersten, die auftauchten, gehörten Cracker verschiedener Hashes, deren Algorithmen sehr einfach zu parallelisieren sind.

Es entstanden auch Multimedia-Anwendungen wie der FlacCL-Encoder, mit dem Sie eine Audiospur in das FLAC-Format transkodieren können. Einige bereits vorhandene Anwendungen haben auch GPGPU-Unterstützung erhalten, die bemerkenswerteste davon ist ImageMagick, das jetzt mithilfe von OpenCL einen Teil seiner Arbeit auf die GPU verlagern kann. Es gibt auch Projekte, um Datenarchivierer und andere Iauf CUDA/OpenCL zu übertragen (ATi-Unixoide sind nicht beliebt). Wir werden uns die interessantesten dieser Projekte in den folgenden Abschnitten des Artikels ansehen, aber zunächst versuchen wir herauszufinden, was wir brauchen, damit alles in Gang kommt und stabil funktioniert.

GPUs haben die Leistung von x86-Prozessoren längst übertroffen

· Zweitens müssen die neuesten proprietären Treiber für die Grafikkarte im System installiert sein; sie bieten Unterstützung sowohl für die native GPGPU-Technologie der Karte als auch für OpenCL.

· Und drittens, da Distributionsentwickler noch nicht damit begonnen haben, Anwendungspakete mit GPGPU-Unterstützung zu verteilen, müssen wir Anwendungen selbst erstellen, und dafür benötigen wir offizielle SDKs von Herstellern: CUDA Toolkit oder ATI Stream SDK. Sie enthalten die Header-Dateien und Bibliotheken, die zum Erstellen von Anwendungen erforderlich sind.

Installieren Sie das CUDA Toolkit

Folgen Sie dem Link oben und laden Sie das CUDA Toolkit für Linux herunter (Sie können aus mehreren Versionen wählen, für die Distributionskits Fedora, RHEL, Ubuntu und SUSE gibt es Versionen sowohl für x86- als auch x86_64-Architekturen). Darüber hinaus müssen Sie dort auch Treiberkits für Entwickler herunterladen (Entwicklertreiber für Linux stehen an erster Stelle auf der Liste).

Starten Sie das SDK-Installationsprogramm:

$ sudo sh cudatoolkit_4.0.17_linux_64_ubuntu10.10.run

Wenn die Installation abgeschlossen ist, fahren wir mit der Installation der Treiber fort. Fahren Sie dazu den X-Server herunter:

# sudo /etc/init.d/gdm stop

Öffnen Sie die Konsole und führen Sie das Treiberinstallationsprogramm aus:

$ sudo sh devdriver_4.0_linux_64_270.41.19.run

Nachdem die Installation abgeschlossen ist, starten Sie X:

Damit Anwendungen mit CUDA/OpenCL arbeiten können, legen wir den Pfad zum Verzeichnis mit CUDA-Bibliotheken in der Variablen LD_LIBRARY_PATH fest:

$ export LD_LIBRARY_PATH=/usr/local/cuda/lib64

Oder, wenn Sie die 32-Bit-Version installiert haben:

$ export LD_LIBRARY_PATH=/usr/local/cuda/lib32

Sie müssen außerdem den Pfad zu den CUDA-Header-Dateien angeben, damit der Compiler sie in der Anwendungserstellungsphase findet:

$ export C_INCLUDE_PATH=/usr/local/cuda/include

Jetzt können Sie mit der Erstellung von CUDA/OpenCL-Software beginnen.

Installieren Sie das ATI Stream SDK

Das Stream SDK erfordert keine Installation, Sie können also einfach das von der Website heruntergeladene AMD-Archiv in ein beliebiges Verzeichnis entpacken (/opt ist die beste Wahl) und den Pfad dazu in dieselbe LD_LIBRARY_PATH-Variable schreiben:

$ wget http://goo.gl/CNCNo

$ sudo tar -xzf ~/AMD-APP-SDK-v2.4-lnx64.tgz -C /opt

$ export LD_LIBRARY_PATH=/opt/AMD-APP-SDK-v2.4-lnx64/lib/x86_64/

$ export C_INCLUDE_PATH=/opt/AMD-APP-SDK-v2.4-lnx64/include/

Wie beim CUDA Toolkit muss x86_64 auf 32-Bit-Systemen durch x86 ersetzt werden. Gehen Sie nun in das Stammverzeichnis und entpacken Sie das Archiv icd-registration.tgz (das ist eine Art kostenloser Lizenzschlüssel):

$ sudo tar -xzf /opt/AMD-APP-SDK-v2.4-lnx64/icd-registration.tgz - MIT /

Wir überprüfen die korrekte Installation/Funktionsweise des Pakets mit dem clinfo-Tool:

$ /opt/AMD-APP-SDK-v2.4-lnx64/bin/x86_64/clinfo

ImageMagick und OpenCL

OpenCL-Unterstützung ist in ImageMagick schon seit geraumer Zeit verfügbar, sie ist jedoch in keiner Distribution standardmäßig aktiviert. Daher müssen wir IM selbst aus dem Quellcode kompilieren. Daran ist nichts Kompliziertes, alles, was Sie brauchen, ist bereits im SDK enthalten, sodass für die Montage keine Installation zusätzlicher Bibliotheken von nVidia oder AMD erforderlich ist. Laden Sie also das Archiv mit den Quellen herunter/entpacken Sie es:

$ wget http://goo.gl/F6VYV

$ tar -xjf ImageMagick-6.7.0-0.tar.bz2

$ cd ImageMagick-6.7.0-0

$ sudo apt-get install build-essential

Wir starten den Konfigurator und holen uns seine Ausgabe für die OpenCL-Unterstützung:

$ LDFLAGS=-L$LD_LIBRARY_PATH ./configure | grep -e cl.h -e OpenCL

Die korrekte Ausgabe des Befehls sollte etwa so aussehen:

Überprüfung der CL/cl.h-Benutzerfreundlichkeit ... ja

Überprüfen der Anwesenheit von CL/cl.h... ja

Überprüfung auf CL/cl.h... ja

Überprüfung der Benutzerfreundlichkeit von OpenCL/cl.h... nein

Überprüfen der Anwesenheit von OpenCL/cl.h... nein

Suche nach OpenCL/cl.h... nein

Suche nach OpenCL-Bibliothek... -lOpenCL

Das Wort „Ja“ muss entweder in den ersten drei Zeilen oder in der zweiten (oder in beiden Optionen gleichzeitig) markiert werden. Ist dies nicht der Fall, wurde höchstwahrscheinlich die Variable C_INCLUDE_PATH nicht korrekt initialisiert. Wenn die letzte Zeile mit dem Wort „Nein“ gekennzeichnet ist, liegt das Problem in der Variablen LD_LIBRARY_PATH. Wenn alles in Ordnung ist, starten Sie den Build-/Installationsprozess:

$ sudo macht die Installation sauber

Überprüfen wir, ob ImageMagick tatsächlich mit OpenCL-Unterstützung kompiliert wurde:

$ /usr/local/bin/convert -version | grep-Funktionen

Funktionen: OpenMP OpenCL

Nun messen wir den resultierenden Geschwindigkeitsgewinn. Die ImageMagick-Entwickler empfehlen hierfür den Convolve-Filter:

$ time /usr/bin/convert image.jpg -convolve "-1, -1, -1, -1, 9, -1, -1, -1, -1" image2.jpg

$ time /usr/local/bin/convert image.jpg -convolve "-1, -1, -1, -1, 9, -1, -1, -1, -1" image2.jpg

Einige andere Vorgänge, wie zum Beispiel die Größenänderung, sollten jetzt ebenfalls viel schneller funktionieren, aber Sie sollten nicht erwarten, dass ImageMagick mit der Verarbeitung von Grafiken in rasender Geschwindigkeit beginnt. Bisher wurde ein sehr kleiner Teil des Pakets mithilfe von OpenCL optimiert.

FlacCL (Flacuda)

FlacCL ist ein Encoder von Audiodateien im FLAC-Format, der bei seiner Arbeit die Fähigkeiten von OpenCL nutzt. Es ist im CUETools-Paket für Windows enthalten, kann aber dank Mono auch unter Linux verwendet werden. Um ein Archiv mit einem Encoder zu erhalten, führen Sie den folgenden Befehl aus:

$ mkdir flaccl && cd flaccl

$ wget www.cuetools.net/install/flaccl03.rar

$ sudo apt-get install unrar mono

$ unrar x fl accl03.rar

Damit das Programm die OpenCL-Bibliothek finden kann, erstellen wir einen symbolischen Link:

$ ln -s $LD_LIBRARY_PATH/libOpenCL.so libopencl.so

Lassen Sie uns nun den Encoder ausführen:

$ mono CUETools.FLACCL.cmd.exe music.wav

Wenn auf dem Bildschirm die Fehlermeldung „Fehler: Die angeforderte Kompilierungsgröße ist größer als die erforderliche Arbeitsgruppengröße von 32“ angezeigt wird, ist die Grafikkarte in unserem System zu schwach und die Anzahl der beteiligten Kerne sollte auf die angegebene Anzahl reduziert werden Verwenden Sie das „-- Flag Gruppengröße XX“, wobei XX die erforderliche Anzahl von Kernen ist.

Ich sage gleich, dass aufgrund der langen Initialisierungszeit von OpenCL nur auf ausreichend langen Strecken spürbare Gewinne erzielt werden können. FlacCL verarbeitet kurze Audiodateien fast mit der gleichen Geschwindigkeit wie seine herkömmliche Version.

oclHashcat oder schnelle Brute-Force

Wie ich bereits sagte, gehörten die Entwickler verschiedener Cracker und Brute-Force-Passwortsysteme zu den ersten, die ihren Produkten GPGPU-Unterstützung hinzufügten. Für sie wurde die neue Technologie zu einem wahren Heiligen Gral, der es ermöglichte, natürlich leicht parallelisierten Code problemlos auf die Schultern schneller GPU-Prozessoren zu übertragen. Daher ist es nicht verwunderlich, dass es inzwischen Dutzende verschiedener Implementierungen solcher Programme gibt. Aber in diesem Artikel werde ich nur über einen davon sprechen – oclHashcat.

oclHashcat ist ein Hacker, der Passwörter mithilfe seines Hashs mit extrem hoher Geschwindigkeit erraten kann und dabei die Leistung der GPU mithilfe von OpenCL nutzt. Glaubt man den auf der Projektwebsite veröffentlichten Messungen, beträgt die Geschwindigkeit bei der Auswahl von MD5-Passwörtern auf der nVidia GTX580 bis zu 15.800 Millionen Kombinationen pro Sekunde, wodurch oclHashcat in nur 9 Minuten ein achtstelliges Passwort mittlerer Komplexität finden kann.

Das Programm unterstützt die Algorithmen OpenCL und CUDA, MD5, md5($pass.$salt), md5(md5($pass)) und vBulletin< v3.8.5, SHA1, sha1($pass.$salt), хэши MySQL, MD4, NTLM, Domain Cached Credentials, SHA256, поддерживает распределенный подбор паролей с задействованием мощности нескольких машин.

7 $ x oclHashcat-0,25,7 z

$cd oclHashcat-0.25

Und führen Sie das Programm aus (wir verwenden eine Beispielliste von Hashes und ein Beispielwörterbuch):

$ ./oclHashcat64.bin example.hash ?l?l?l?l example.dict

oclHashcat öffnet den Text der Benutzervereinbarung, der Sie durch Eingabe von „JA“ zustimmen müssen. Danach beginnt der Suchvorgang, dessen Fortschritt durch Drücken von angezeigt werden kann . Um den Vorgang anzuhalten, klicken Sie auf

Um fortzufahren - . Sie können auch eine direkte Aufzählung verwenden (z. B. von aaaaaaaa bis zzzzzzzz):

$ ./oclHashcat64.bin hash.txt ?l?l?l?l ?l?l?l?l

Und verschiedene Modifikationen des Wörterbuchs und der Direktsuchmethode sowie deren Kombinationen (Sie können dies in der Datei docs/examples.txt nachlesen). In meinem Fall betrug die Durchsuchungsgeschwindigkeit des gesamten Wörterbuchs 11 Minuten, während die direkte Suche (von aaaaaaaa bis zzzzzzzz) etwa 40 Minuten dauerte. Die durchschnittliche Geschwindigkeit der GPU (RV710-Chip) betrug 88,3 Millionen/s.

Schlussfolgerungen

Trotz vieler verschiedener Einschränkungen und der Komplexität der Softwareentwicklung ist GPGPU die Zukunft leistungsstarker Desktop-Computer. Aber das Wichtigste ist, dass Sie die Möglichkeiten dieser Technologie schon jetzt nutzen können, und das gilt nicht nur für Windows-Rechner, sondern auch für Linux.