CUDA, wie man Gitter, Block, Fadengröße erhält und nicht quadratische Matrixberechnungen parallelisiert

Lesezeit: 4 Minuten

CUDA wie man Gitter Block Fadengrose erhalt und nicht quadratische
Benutzer656210

Ich bin neu bei CUDA und brauche Hilfe beim Verständnis einiger Dinge. Ich brauche Hilfe bei der Parallelisierung dieser beiden for-Schleifen. Insbesondere, wie man dimBlock und dimGrid einrichtet, damit dies schneller läuft. Ich weiß, dass dies wie das Beispiel zum Hinzufügen von Vektoren im SDK aussieht, aber dieses Beispiel gilt nur für quadratische Matrizen, und wenn ich versuche, diesen Code für meine 128 x 1024-Matrix zu ändern, funktioniert es nicht richtig.

__global__ void mAdd(float* A, float* B, float* C)
{
    for(int i = 0; i < 128; i++)
    {
        for(int j = 0; j < 1024; j++)
        {
            C[i * 1024 + j] = A[i * 1024 + j] + B[i * 1024 + j];
        }
    }
}

Dieser Code ist Teil einer größeren Schleife und der einfachste Teil des Codes, also habe ich beschlossen, zu versuchen, ihn zu parallelisieren und gleichzeitig CUDA zu lernen. Ich habe die Anleitungen gelesen, verstehe aber immer noch nicht, wie ich die richtige Nr. bekomme. von Grids/Block/Threads gehen und sie effektiv nutzen.

  • In Pikuda es ist nur C[i] = A[i] + B[i] demo.py

    – jfs

    13. April 11 um 2:06 Uhr


1643445611 267 CUDA wie man Gitter Block Fadengrose erhalt und nicht quadratische
Krallen

Wie Sie es geschrieben haben, ist dieser Kernel vollständig seriell. Jeder Thread, der zu seiner Ausführung gestartet wird, wird dieselbe Arbeit ausführen.

Die Hauptidee hinter CUDA (und OpenCL und anderen ähnlichen Programmiermodellen vom Typ „Einzelprogramm, mehrere Daten“) besteht darin, dass Sie eine „datenparallele“ Operation verwenden – also eine, bei der die gleiche, weitgehend unabhängige Operation viele Male ausgeführt werden muss – und Schreiben Sie einen Kernel, der diese Operation ausführt. Eine große Anzahl von (semi)autonomen Threads wird dann gestartet, um diese Operation über den Eingabedatensatz hinweg auszuführen.

In Ihrem Array-Additionsbeispiel ist die Datenparalleloperation

C[k] = A[k] + B[k];

für alle k zwischen 0 und 128 * 1024. Jede Additionsoperation ist völlig unabhängig und hat keine Ordnungsanforderungen und kann daher von einem anderen Thread ausgeführt werden. Um dies in CUDA auszudrücken, könnte man den Kernel so schreiben:

__global__ void mAdd(float* A, float* B, float* C, int n)
{
    int k = threadIdx.x + blockIdx.x * blockDim.x;

    if (k < n)
        C[k] = A[k] + B[k];
}

[disclaimer: code written in browser, not tested, use at own risk]

Hier werden die innere und äußere Schleife aus dem seriellen Code durch einen CUDA-Thread pro Operation ersetzt, und ich habe im Code eine Limitprüfung hinzugefügt, damit in Fällen, in denen mehr Threads als erforderliche Operationen gestartet werden, kein Pufferüberlauf auftreten kann. Wenn der Kernel dann so gestartet wird:

const int n = 128 * 1024;
int blocksize = 512; // value usually chosen by tuning and hardware constraints
int nblocks = n / blocksize; // value determine by block size and total work

madd<<<nblocks,blocksize>>>mAdd(A,B,C,n);

Dann werden 256 Blöcke mit jeweils 512 Threads auf der GPU-Hardware gestartet, um die Array-Hinzufügungsoperation parallel auszuführen. Beachten Sie, dass, wenn die Größe der Eingabedaten nicht als schönes rundes Vielfaches der Blockgröße ausgedrückt werden könnte, die Anzahl der Blöcke aufgerundet werden müsste, um den vollständigen Eingabedatensatz abzudecken.

All dies ist ein stark vereinfachter Überblick über das CUDA-Paradigma für eine sehr triviale Operation, aber vielleicht gibt es Ihnen genug Einblick, um selbst fortzufahren. CUDA ist heutzutage ziemlich ausgereift und es gibt eine Menge gutes, kostenloses Lehrmaterial im Internet, das Sie wahrscheinlich verwenden können, um viele der Aspekte des Programmiermodells, die ich in dieser Antwort beschönigt habe, weiter zu beleuchten.

  • int k = threadIdx.x + gridDim.x * blockDim.x; Das ist doch sicher falsch? gridDim.x * blockDim.x ist in Ihrem Beispiel immer 256*512. Sollte int sein k = threadIdx.x + blockIdx.x * blockDim.x; Ich habe versucht, es zu bearbeiten, wurde aber abgelehnt.

    – Ozon

    1. Mai 13 um 3:02 Uhr


  • Warnung an den Skim-Leser: nblocks = ceil(n / nthreads); // wenn Ihre Daten nicht perfekt aufgeteilt werden.

    – ofer.sheffer

    4. April 17 um 11:01 Uhr

  • @ofer.sheffer: Ich habe geschrieben: “Beachten Sie, dass, wenn die Größe der Eingabedaten nicht als schönes rundes Vielfaches der Blockgröße ausgedrückt werden könnte, die Anzahl der Blöcke aufgerundet werden müsste, um den gesamten Eingabedatensatz abzudecken.” Ist das nicht deutlich genug?

    – Krallen

    4. April 17 um 11:02 Uhr

  • @talonmies, deine Antwort ist sehr schön und ich habe sie positiv bewertet. Andererseits dachte ich, als ich es las, “er hat die +1 verpasst”, falls sich die Daten nicht gleichmäßig aufteilen … dann las ich noch ein paar andere Dinge und kam hierher zurück, um das Lesen zu beenden und mir ist aufgefallen, dass Sie es hineingeschrieben haben. Als oberflächlicher Leser, der normalerweise zuerst nur auf den Code schaut und später überlegt, jedes Wort zu lesen – ich denke, meine Warnung würde meinem zukünftigen Ich helfen.

    – ofer.sheffer

    4. April 17 um 12:50 Uhr


  • Wie soll ich wissen nthreads? Ist nicht blocksize die Anzahl der Fäden?

    – sms

    7. November 19 um 16:45 Uhr

.

685750cookie-checkCUDA, wie man Gitter, Block, Fadengröße erhält und nicht quadratische Matrixberechnungen parallelisiert

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

Privacy policy