CUDA, Verwendung von 2D- und 3D-Arrays

Lesezeit: 6 Minuten

Es gibt online viele Fragen zum Zuweisen, Kopieren, Indizieren usw. von 2D- und 3D-Arrays in CUDA. Ich bekomme viele widersprüchliche Antworten, also versuche ich, frühere Fragen zusammenzustellen, um zu sehen, ob ich die richtigen stellen kann.

Erster Link: https://devtalk.nvidia.com/default/topic/392370/how-to-cudamalloc-two-dimensional-array-/

Problem: Zuordnung eines 2D-Arrays von Zeigern

Benutzerlösung: Verwenden Sie mallocPitch

“Richtige” ineffiziente Lösung: Verwenden Sie malloc und memcpy in einer for-Schleife für jede Zeile (absurder Overhead)

“Korrektere” Lösung: Squash in ein 1D-Array “professionelle Meinung”, ein Kommentar, der besagt, dass niemand mit einem Auge auf die Leistung 2D-Zeigerstrukturen auf der GPU verwendet

Zweiter Link: https://devtalk.nvidia.com/default/topic/413905/passing-a-multidimensional-array-to-kernel-how-to-allocate-space-in-host-and-pass-to-device-/

Problem: Speicherplatz auf dem Host zuweisen und an das Gerät weitergeben

Sublink: https://devtalk.nvidia.com/default/topic/398305/cuda-programming-and-performance/dynamically-allocate-array-of-structs/

Sub-Link-Lösung: Das Codieren von zeigerbasierten Strukturen auf der GPU ist eine schlechte Erfahrung und äußerst ineffizient, quetschen Sie sie in ein 1D-Array.

Dritter Link: Weisen Sie 2D-Array im Gerätespeicher in CUDA zu

Problem: Zuweisen und Übertragen von 2D-Arrays

Benutzerlösung: Verwenden Sie mallocPitch

Andere Lösung: glätten

Vierter Link: Wie verwende ich 2D-Arrays in CUDA?

Problem: Weisen Sie 2D-Arrays zu und durchlaufen Sie sie

Eingereichte Lösung: Zeigt keine Zuordnung an

Andere Lösung: Squash it

Es gibt viele andere Quellen, die meistens dasselbe sagen, aber in mehreren Fällen sehe ich Warnungen zu Zeigerstrukturen auf der GPU.

Viele Leute behaupten, dass der richtige Weg, ein Array von Zeigern zuzuweisen, ein Aufruf von malloc und memcpy für jede Zeile ist, obwohl die Funktionen mallocPitch und memcpy2D existieren. Sind diese Funktionen irgendwie weniger effizient? Warum sollte dies nicht die Standardantwort sein?

Die andere “richtige” Antwort für 2D-Arrays besteht darin, sie in einem Array zusammenzudrücken. Soll ich mich einfach daran gewöhnen als eine Tatsache des Lebens? Ich bin sehr penibel in Bezug auf meinen Code und er fühlt sich für mich unelegant an.

Eine andere Lösung, die ich in Betracht gezogen habe, war, eine Matrixklasse zu maximieren, die ein 1d-Zeiger-Array verwendet, aber ich kann keine Möglichkeit finden, den Operator mit doppelten Klammern zu implementieren.

Auch nach diesem Link: Objekt auf Gerät kopieren?

und die Sublink-Antwort: cudaMemcpy-Segmentierungsfehler

Das wird ein wenig zweifelhaft.

Die Klassen, mit denen ich CUDA verwenden möchte, haben alle 2/3D-Arrays, und würde es nicht viel Overhead geben, diese in 1D-Arrays für CUDA zu konvertieren?

Ich weiß, dass ich viel gefragt habe, aber zusammenfassend sollte ich mich an gequetschte Arrays als Tatsache des Lebens gewöhnen oder kann ich die 2D-Zuweisungs- und Kopierfunktionen verwenden, ohne einen schlechten Overhead zu bekommen, wie in der Lösung, in der alloc und cpy in a for aufgerufen werden Schleife?

CUDA Verwendung von 2D und 3D Arrays
Robert Crovella

Da Ihre Frage eine Liste anderer Fragen zusammenstellt, werde ich antworten, indem ich eine Liste anderer Antworten zusammenstelle.

cudaMallocPitch/cudaMemcpy2D:

Erstens funktioniert die cuda-Laufzeit-API wie folgt cudaMallocPitch und cudaMemcpy2D beinhalten weder Doppelzeiger-Zuweisungen noch 2D-Arrays (doppelt subskribiert). Dies ist einfach durch einfaches Ansehen zu bestätigen die Dokumentation, und Notieren der Typen von Parametern in den Funktionsprototypen. Die src und dst Parameter sind Single-Pointer-Parameter. Sie konnten nicht doppelt subskribiert oder doppelt dereferenziert werden. Für eine zusätzliche Beispielverwendung finden Sie hier eine von vielen Fragen dazu. Hier ist ein vollständig ausgearbeitetes Anwendungsbeispiel. Ein weiteres Beispiel für verschiedene Konzepte im Zusammenhang mit cudaMallocPitch/cudaMemcpy2d Verwendung ist hier. Stattdessen ist die richtige Art, darüber nachzudenken, dass sie damit arbeiten aufgeschlagen Zuweisungen. Außerdem können Sie nicht verwenden cudaMemcpy2D um Daten zu übertragen, wenn die zugrunde liegende Zuordnung mit einem Satz von erstellt wurde malloc (oder new, oder ähnliche) Operationen in einer Schleife. Diese Art von Host-Datenzuweisungskonstruktion ist besonders schlecht geeignet, um mit den Daten auf dem Gerät zu arbeiten.

Allgemeiner, dynamisch zugewiesener 2D-Fall:

Wenn Sie lernen möchten, wie man ein dynamisch zugewiesenes 2D-Array in einem CUDA-Kernel verwendet (d.h. Sie können doppelt subskriptierten Zugriff verwenden, z data[x][y]), dann ist die cuda Die Tag-Info-Seite enthält die “kanonische” Frage dafür, sie ist hier. Die Antwort, die die Talonmies dort geben, enthält die richtige Mechanik sowie entsprechende Vorbehalte:

  • es gibt zusätzliche, nicht triviale Komplexität
  • Der Zugriff ist im Allgemeinen weniger effizient als der 1D-Zugriff, da der Datenzugriff die Dereferenzierung von 2 Zeigern anstelle von 1 erfordert.

(Beachten Sie, dass das Zuweisen eines Arrays von Objekten, bei dem die Objekte einen eingebetteten Zeiger auf eine dynamische Zuweisung haben, im Wesentlichen dasselbe ist wie das 2D-Array-Konzept, und das Beispiel, das Sie in Ihrer Frage verlinkt haben, ist eine vernünftige Demonstration dafür.)

Außerdem ist hier ein Schubverfahren zum Aufbauen eines allgemeinen dynamisch zugewiesenen 2D-Arrays.

Abflachung:

Wenn Sie der Meinung sind, dass Sie die allgemeine 2D-Methode verwenden müssen, dann fahren Sie fort, es ist nicht unmöglich (obwohl die Leute manchmal mit dem Prozess kämpfen!). Aufgrund der zusätzlichen Komplexität und der verringerten Effizienz lautet der kanonische “Rat” hier jedoch, “abzuflachen”. Ihre Speichermethode und verwenden Sie “simulierten” 2D-Zugriff. Hier ist eines von vielen Beispielen für Fragen/Antworten zum Thema “Abflachen”.

Allgemeiner, dynamisch zugewiesener 3D-Fall:

Wenn wir dies auf 3 (oder höher!) Dimensionen erweitern, wird der allgemeine Fall übermäßig komplex zu handhaben, IMO. Die zusätzliche Komplexität sollte uns stark motivieren, nach Alternativen zu suchen. Der dreifach subskriptierte allgemeine Fall beinhaltet 3 Zeigerzugriffe, bevor die Daten tatsächlich abgerufen werden, also noch weniger effizient. Hier ist ein voll funktionsfähiges Beispiel (2. Codebeispiel).

Sonderfall: Array-Breite zur Kompilierzeit bekannt:

Beachten Sie, dass es als a betrachtet werden sollte besonderer Fall wenn die Array-Dimension(en) (die Breite, im Fall eines 2D-Arrays oder 2 der 3 Dimensionen für ein 3D-Array) ist zur Kompilierzeit bekannt. In diesem Fall können wir mit einer geeigneten Hilfstypdefinition den Compiler “anweisen”, wie die Indizierung berechnet werden soll, und in diesem Fall können wir den doppelt subskriptierten Zugriff mit erheblich weniger Komplexität als im allgemeinen Fall verwenden. und Es gibt keinen Effizienzverlust durch Pointer-Jagd. Es muss nur ein Zeiger dereferenziert werden, um die Daten abzurufen (unabhängig von der Array-Dimensionalität, wenn n-1 Dimensionen zur Kompilierzeit für ein n-dimensionales Array bekannt sind). Das erste Codebeispiel in der bereits erwähnten Antwort hier (erstes Codebeispiel) gibt ein vollständig funktionierendes Beispiel dafür im 3D-Fall, und die Antwort hier gibt ein 2D-Beispiel für diesen Sonderfall.

Hostcode mit doppelter Subskription, Gerätecode mit einfacher Subskription:

Schließlich ermöglicht uns eine weitere Methodenoption, den 2D-Zugriff (doppelt abonniert) einfach einzumischen Host-Code bei Verwendung von nur 1D (einzeln abonniert, vielleicht mit “simuliertem 2D”-Zugriff) in Gerätecode. Ein ausgearbeitetes Beispiel dafür ist hier. Indem wir die zugrunde liegende Zuweisung als zusammenhängende Zuweisung organisieren und dann den Zeiger-“Baum” erstellen, können wir den doppelt abonnierten Zugriff auf dem Host aktivieren und trotzdem die flache Zuweisung problemlos an das Gerät übergeben. Obwohl das Beispiel dies nicht zeigt, wäre es möglich, diese Methode zu erweitern, um ein doppelt abonniertes Zugriffssystem auf dem Gerät basierend auf einer flachen Zuweisung und einem manuell erstellten Zeiger-„Baum“ zu erstellen, dies hätte jedoch ungefähr die gleichen Probleme wie die oben angegebene 2D-allgemeine dynamisch zugewiesene Methode: Es würde einen Doppelzeigerzugriff (doppelte Dereferenzierung) beinhalten, also weniger effizient, und es ist eine gewisse Komplexität mit dem Aufbau des Zeiger-“Baums” zur Verwendung im Gerätecode verbunden (z erfordern eine zusätzliche cudaMemcpy Operation, wahrscheinlich).

Aus den oben genannten Methoden müssen Sie eine auswählen, die Ihrem Appetit und Ihren Bedürfnissen entspricht. Es gibt keine einzige Empfehlung, die für jeden möglichen Fall geeignet ist.

914680cookie-checkCUDA, Verwendung von 2D- und 3D-Arrays

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy