Was ist der Unterschied zwischen __ldg() intrinsische und eine normale Ausführung?

Ich versuche zu erkunden", __ldg intrinsischen'. Ich bin in der NVIDIA Dokumentation für diese, aber bekam keine befriedigende Antwort über dessen Verwendung und Implementierungen. Außerdem mit Verweis auf DIESE ich versuchte Umsetzung __ldg in eine einfache 1024*1024-matrix-Multiplikation Beispiel.

#include<stdio.h>
#include<stdlib.h>

__global__ void matrix_mul(float * ad,float * bd,float * cd,int N)
{
        float pvalue=0;
        //find Row and Column corresponding to a data element for each thread
        int Row = blockIdx.y * blockDim.y + threadIdx.y;
        int Col = blockIdx.x * blockDim.x + threadIdx.x;
        //calculate dot product of Row of First Matrix and Column of Second Matrix
        for(int i=0;i< N;++i)
        {
//  I tried with executing this first:
            float m=__ldg(&ad[Row * N+i]);
            float n=__ldg(&bd[i * N + Col]);

//Then I executed this as a normal execution:
//         float m = ad[Row * N+i];
//         float n = bd[i * N + Col];

            pvalue += m * n;
         }
        //store dot product at corresponding position in resultant Matrix
        cd[Row * N + Col] = pvalue;
}

int main()
{
    int N = 1024,i,j;               //N == size of square matrix

    float *a,*b;
    float *ad,*bd,*cd,*c;

    //open a file for outputting the result
    FILE *f;
    f=fopen("Parallel Multiply_ldg.txt","w");

    size_t size=sizeof(float)* N * N;

    //allocate host side memory
    a=(float*)malloc(size);
    b=(float*)malloc(size);
    c=(float*)malloc(size);

    for(i=0;i<N;i++)
    {
        for(j=0;j<N;j++)
        {
            a[i*N+j]=2.0;   //(float)(i*N+j);       //initializing each value with its own index
            b[i*N+j]=1.0;   //(float)(i*N+j);       //random functions can be used alternatively
        }
    }

    //allocate device memory
    cudaMalloc(&ad,size);
    //printf("\nAfter cudaMalloc for ad\n%s\n",cudaGetErrorString(cudaGetLastError()));
    cudaMalloc(&bd,size);
    //printf("\nAfter cudaMalloc bd\n%s\n",cudaGetErrorString(cudaGetLastError()));
    cudaMalloc(&cd,size);
    //printf("\nAfter cudaMalloc cd\n%s\n",cudaGetErrorString(cudaGetLastError()));

    //copy value from host to device
    cudaMemcpy(ad,a,size,cudaMemcpyHostToDevice);
    cudaMemcpy(bd,b,size,cudaMemcpyHostToDevice);

    printf("\nAfter HostToDevice Memcpy\n%s\n",cudaGetErrorString(cudaGetLastError()));

    //calculate execution configuration
    dim3 blocksize(16,16);              //each block contains 16 * 16 (=256) threads
    dim3 gridsize(N/16,N/16);           //creating just sufficient no of blocks

    //GPU timer code
    float time;
    cudaEvent_t start,stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord(start,0);

    matrix_mul <<< gridsize, blocksize >>> (ad,bd,cd, N);
    cudaDeviceSynchronize();
    cudaEventRecord(stop,0);
    cudaEventSynchronize(stop);
    cudaEventElapsedTime(&time,start,stop);         //time taken in kernel call calculated
    cudaEventDestroy(start);
    cudaEventDestroy(stop);

    //copy back results
    cudaMemcpy(c,cd,sizeof(float)* N*N,cudaMemcpyDeviceToHost);

    printf("\nAfter DeviceToHost Memcpy\n%s\n",cudaGetErrorString(cudaGetLastError()));

    //output results in output_file
    fprintf(f,"Array A was---\n");
    for(i=0;i<N;i++)
    {
        for(j=0;j<N;j++)
            fprintf(f,"%f ",a[i*N+j]);
        fprintf(f,"\n");
    }
    fprintf(f,"\nArray B was---\n");
    for(i=0;i<N;i++)
    {
        for(j=0;j<N;j++)
            fprintf(f,"%f ",b[i*N+j]);
        fprintf(f,"\n");
    }
    fprintf(f,"\nMultiplication of A and B gives C----\n");
    for(i=0;i<N;i++)
    {
        for(j=0;j<N;j++)
            fprintf(f,"%f ",c[i*N+j]);              //if correctly computed, then all values must be N
        fprintf(f,"\n");
    }
    printf("\nYou can see output in Parallel Mutiply.txt file in project directory");
    printf("\n\nTime taken is %f (ms)\n",time);
    fprintf(f,"\n\nTime taken is %f (ms)\n",time);
    fclose(f);

    cudaThreadExit();
    //cudaFree(ad); cudaFree(bd); cudaFree (cd);
    free(a);free(b);free(c);
    //_getch();
    return 1;
}

Habe ich kommentiert, dass __ldg Teil in meinem kernel und ausgeführt von der normalen Ausführung, und Umgekehrt.
In beiden Fällen gibt es mir die korrekte Multiplikation Ergebnis. Ich bin verwirrt mit der Zeit Unterschied, ich bin immer zwischen diesen Ausführungen, weil seine riesigen, fast mehr als 100X!

Im Falle von __ldg es gibt mich: Time taken is 0.014432 (ms)

Werden, und im Fall der normalen Ausführung ohne __ldg es gibt mich : Time taken is 36.858398 (ms)

Ist dies die genaue Art und Weise der Benutzung __ldg intrisic? Was ist die Bedeutung von __ldg intrinsische und was ist der richtige Weg, es zu benutzen? Anscheinend was habe ich oben in meinem code ist falsch und naiv. Ich bin auf der Suche nach Erklärung und Beispiel. Vielen Dank im Voraus.

  • Nur so verstehe ich das - die Ergebnisse sind korrekt, Sie haben eine große Geschwindigkeit auf, und Ihre Frage "was habe ich falsch gemacht?"?....
  • Die Geschwindigkeit ist so groß, dass ich bezweifle, ob dies zu korrigieren. Und wenn dieser korrekt ist, dann was das für eine Zauberei __ldg bedeutet, dass ich immer dieses speedup? Und wenn das falsch ist, was ist dann die korrekte Art und Weise zu verwenden, __ldg? Insgesamt bin ich auf der Suche nach mehr Erklärung auf __ldg Konzept und seine Umsetzung.
  • Ich nehme an, Sie überprüft das Ergebnis mit einem CPU-Ergebnis. njuffa geliefert dieser Erklärung vis-a-vis des Mechanismus. Es ist wirklich darauf zu reduzieren, die Speicherzugriff-Latenz, die Beseitigung eines Engpasses.
  • Nein! Diese beiden sind die GPU-Ergebnisse. Man ist mit der Verwendung von __ldg und andere ohne mit __ldg
  • Sie schrieb: "In beiden Fällen gibt es mir die exakte Multiplikation Ergebnis." Ich nahm an, Sie verifizierten die Ergebnisse, die gegen einen Dritten zu führen, aber auch gegen jeden anderen, warum Sie immer noch Zweifel an der ldg Ergebnis? Die "Magie", auch auf die Gefahr hin, mich zu wiederholen, es ist Cache im Arbeitsspeicher zugreifen, was entfernt den Engpass.
  • Sie möchten möglicherweise versuchen, ohne __ldg folgende Signatur __global__ void matrix_mul(__restrict__ float const* ad, __restrict__ float const* bd,float * cd,int N)
  • das Ergebnis wäre ein Vorschlag an den compiler-cache ad und bd im Gegensatz zu den expliziten ldg.
  • Mit "Exakten" Ergebnisse, die ich wollte sagen, das richtige Ergebnis. Sehen Sie, ich war überrascht, nur wegen der Beschleunigung, die ich hier erreicht, das ist mehr als 100X, mit einem einzigen Schlagwort, das sehr schwer zu glauben! Wir setzen so viel Bemühungen im shared-memory-Programmierung zu erreichen speedup, und hier bekomme ich es mit __ldg nur. So verstehen wollte, Logik, die dahinter steckt. Vielen Dank für deine Antwort übrigens, das nützlich ist.
  • Ja, das ist das Ziel.
  • was GPU-sind Sie mit diesem Vergleich auf?
  • In der Tat ist der speedup du bist reporting ist auf die Bestellung von 2500x und nicht sinnvoll ist. Die 36 MS Zahl scheint vernünftig, wie bekomme ich ~17ms für Ihren code ist auf einem K40c GPU. Die 0.0144 ms Zahl ist nicht zumutbar. Ich vermute, dass der kernel starten versagt, vielleicht aufgrund der Art der GPU ausgeführt werden (sofern es nicht ein cc3.5 GPU, der Start fehlschlagen) und das Ergebnis ist dieser sehr kurze Messung. Natürlich, Ihre Forderung in diesem Fall, dass "die Ergebnisse korrekt sind" nicht der Fall. Vielleicht sind Sie auf der Suche nach einem alten output-Daten-Datei. (Und auch nicht zu bemerken, die Fehler vom code.)
  • Genau!Ich Stimme völlig mit Ihnen. Und dies ist der Hauptgrund, die ich gepostet diese Frage hier. Ich bin mit Tesla C2075 und Quadro 600 auf einer einzigen Maschine. Und wie Sie sehen können in meinen code Schreibe ich die Ergebnisse in separaten Dateien für jede Ausführung. Lustige daran ist, das Ergebnis ist richtig, und das macht mich mehr verwirrt!
  • Mein Fehler!! Keiner von Ihnen hat compute capability 3.5 🙁 . Obwohl ich gab Verweis auf eine Ihrer Antworten und veröffentlicht diese Frage, ich habe diesen wichtigen Punkt von cc. Ich werde führen Sie diese auf die gewünschte GPU(3.5 cc) und zurück hier in diese Frage.
  • Das Ergebnis ist nicht korrekt. Wenn Sie diesen code ausführen auf eine der GPUs die Sie genannt haben, wird es drucken Sie "ungültige Geräte-Funktion", aber Sie scheinen zu ignorieren, dass. Und Ihre Behauptung, dass die Ergebnisse korrekt sind ist auch nicht wahr. Wahrscheinlich sind Sie verwirrt und nicht den Dateinamen ändern, wenn Sie kompiliert den code. __ldg erfordert compute capability 3.5

Schreibe einen Kommentar