errore di segmentazione cudaMemcpy

Sono stato ossessionato da questo errore per un po ‘di tempo quindi ho deciso di postarlo qui.

Questo errore di segmentazione si è verificato quando si chiama cudaMemcpy:

CurrentGrid->cdata[i] = new float[size]; cudaMemcpy(CurrentGrid->cdata[i], Grid_dev->cdata[i], size*sizeof(float),\ cudaMemcpyDeviceToHost); 

CurrentGrid e Grid_dev sono puntatori a un object di class grid su host e device rispettivamente e i = 0 in questo contesto. Il membro della class cdata è un array di puntatori di tipo float. Per il debug, proprio prima di questa chiamata cudaMemcpy ho stampato il valore di ogni elemento di Grid_Dev->cdata[i] , l’indirizzo di CurrentGrid->cdata[i] e Grid_dev->cdata[i] e il valore della size , che tutto sembra buono Ma finisce con “Errore di segmentazione (core dumped)”, che è l’unico messaggio di errore. cuda-memcheck ha dato solo “il processo non è terminato con successo”. Al momento non sono in grado di utilizzare cuda-gdb. Qualche suggerimento su dove andare?

AGGIORNAMENTO : Sembra che ora abbia risolto questo problema cudaMalloc un altro puntatore a virgola mobile A sul dispositivo e cudaMemcpy il valore di Grid_dev-> cdata [i] in A, e quindi cudaMemcpy A in host. Quindi il segmento di codice scritto sopra diventa:

 float * A; cudaMalloc((void**)&A, sizeof(float)); ... ... cudaMemcpy(&A, &(Grid_dev->cdata[i]), sizeof(float *), cudaMemcpyDeviceToHost); CurrentGrid->cdata[i] = new float[size]; cudaMemcpy(CurrentGrid->cdata[i], A, size*sizeof(float), cudaMemcpyDeviceToHost); 

L’ho fatto perché valgrind ha visualizzato “non letto della dimensione 8”, che ho pensato riferendosi a Grid_dev->cdata[i] . Ho controllato di nuovo con gdb, stampando il valore di Grid_dev->cdata[i] come NULL. Quindi immagino di non poter direttamente rimuovere il puntatore del dispositivo anche in questa chiamata cudaMemcpy. Ma perché ? Secondo il commento in fondo a questo thread , dovremmo essere in grado di dereferenziare il puntatore del dispositivo nella funzione cudaMemcpy.

Inoltre, non conosco il meccanismo sottostante di come funzionano cudaMalloc e cudaMemcpy, ma penso che da cudaMalloc un puntatore, ad esempio A, in realtà assegniamo questo puntatore a un determinato indirizzo sul dispositivo. E cudaMemcpy il Grid_dev->cdata[i] in A come nel codice modificato sopra, riassegniamo il puntatore A a puntare alla matrice. Quindi non perdiamo la traccia dell’indirizzo precedente che A ha indicato quando è cudaMalloced? Ciò potrebbe causare perdite di memoria o qualcosa del genere? Se sì, come dovrei aggirare questa situazione correttamente? Grazie!

Per riferimento ho messo il codice della funzione completa in cui questo errore è accaduto sotto.

Grazie molto!

 __global__ void Print(grid *, int); __global__ void Printcell(grid *, int); void CopyDataToHost(param_t p, grid * CurrentGrid, grid * Grid_dev){ cudaMemcpy(CurrentGrid, Grid_dev, sizeof(grid), cudaMemcpyDeviceToHost); #if DEBUG_DEV cudaCheckErrors("cudaMemcpy1 error"); #endif printf("\nBefore copy cell data\n"); Print<<>>(Grid_dev, 0); //Print out some Grid_dev information for cudaDeviceSynchronize(); //debug int NumberOfBaryonFields = CurrentGrid->ReturnNumberOfBaryonFields(); int size = CurrentGrid->ReturnSize(); int vsize = CurrentGrid->ReturnVSize(); CurrentGrid->FieldType = NULL; CurrentGrid->FieldType = new int[NumberOfBaryonFields]; printf("CurrentGrid size is %d\n", size); for( int i = 0; i 

cdata[i] = NULL; CurrentGrid->vdata[i] = NULL; CurrentGrid->cdata[i] = new float[size]; CurrentGrid->vdata[i] = new float[vsize]; Printcell<<>>(Grid_dev, i);//Print out element value of Grid_dev->cdata[i] cudaDeviceSynchronize(); cudaMemcpy(CurrentGrid->cdata[i], Grid_dev->cdata[i], size*sizeof(float),\ cudaMemcpyDeviceToHost); //where error occurs #if DEBUG_DEV cudaCheckErrors("cudaMemcpy2 error"); #endif printf("\nAfter copy cell data\n"); Print<<>>(Grid_dev, i); cudaDeviceSynchronize(); cudaMemcpy(CurrentGrid->vdata[i], Grid_dev->vdata[i], vsize*sizeof(float),\ cudaMemcpyDeviceToHost); #if DEBUG_DEV cudaCheckErrors("cudaMemcpy3 error"); #endif } cudaMemcpy(CurrentGrid->FieldType, Grid_dev->FieldType,\ NumberOfBaryonFields*sizeof(int), cudaMemcpyDeviceToHost); #if DEBUG_DEV cudaCheckErrors("cudaMemcpy4 error"); #endif }

EDIT: ecco le informazioni da Valgrind, da cui sto cercando di rintracciare dove è avvenuta la perdita di memoria.

 ==19340== Warning: set address range perms: large range [0x800000000, 0xd00000000) (noaccess) ==19340== Warning: set address range perms: large range [0x200000000, 0x400000000) (noaccess) ==19340== Invalid read of size 8 ==19340== at 0x402C79: CopyDataToHost(param_t, grid*, grid*) (CheckDevice.cu:48) ==19340== by 0x403646: CheckDevice(param_t, grid*, grid*) (CheckDevice.cu:186) ==19340== by 0x40A6CD: main (Transport.cu:81) ==19340== Address 0x2003000c0 is not stack'd, malloc'd or (recently) free'd ==19340== ==19340== ==19340== Process terminating with default action of signal 11 (SIGSEGV) ==19340== Bad permissions for mapped region at address 0x2003000C0 ==19340== at 0x402C79: CopyDataToHost(param_t, grid*, grid*) (CheckDevice.cu:48) ==19340== by 0x403646: CheckDevice(param_t, grid*, grid*) (CheckDevice.cu:186) ==19340== by 0x40A6CD: main (Transport.cu:81) ==19340== ==19340== HEAP SUMMARY: ==19340== in use at exit: 2,611,365 bytes in 5,017 blocks ==19340== total heap usage: 5,879 allocs, 862 frees, 4,332,278 bytes allocated ==19340== ==19340== LEAK SUMMARY: ==19340== definitely lost: 0 bytes in 0 blocks ==19340== indirectly lost: 0 bytes in 0 blocks ==19340== possibly lost: 37,416 bytes in 274 blocks ==19340== still reachable: 2,573,949 bytes in 4,743 blocks ==19340== suppressed: 0 bytes in 0 blocks ==19340== Rerun with --leak-check=full to see details of leaked memory ==19340== ==19340== For counts of detected and suppressed errors, rerun with: -v ==19340== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2) 

    Credo di sapere qual è il problema, ma per confermarlo, sarebbe utile vedere il codice che si sta utilizzando per impostare le classi Grid_dev sul dispositivo.

    Quando una class o un’altra struttura di dati deve essere utilizzata sul dispositivo, e quella class ha dei puntatori in essa che si riferiscono ad altri oggetti o buffer in memoria (presumibilmente nella memoria del dispositivo, per una class che verrà utilizzata sul dispositivo), quindi il processo di rendere questa class di alto livello utilizzabile sul dispositivo diventa più complicato.

    Supponiamo che io abbia una class come questa:

     class myclass{ int myval; int *myptr; } 

    Potrei istanziare la class sopra sull’host, quindi malloc un array di int e assegnare quel puntatore a myptr , e tutto andrebbe bene. Per rendere questa class utilizzabile solo sul dispositivo e sul dispositivo, il processo potrebbe essere simile. Potrei:

    1. cudaMalloc un puntatore alla memoria del dispositivo che manterrà myclass
    2. (facoltativamente) copia un object istanziato di myclass sull’host sul puntatore del dispositivo dal passaggio 1 utilizzando cudaMemcpy
    3. sul dispositivo, utilizzare malloc o new per allocare l’archiviazione del dispositivo per myptr

    La sequenza di cui sopra va bene se non voglio mai accedere alla memoria allocata per myptr sull’host. Ma se voglio che lo spazio di archiviazione sia visibile dall’host, ho bisogno di una sequenza diversa:

    1. cudaMalloc un puntatore alla memoria del dispositivo che manterrà myclass , chiamiamolo mydevobj
    2. (facoltativamente) copia un object istanziato di myclass sull’host al puntatore del dispositivo mydevobj dal punto 1 utilizzando cudaMemcpy
    3. Crea un puntatore int separato sull’host, chiamiamolo myhostptr
    4. cudaMalloc int storage sul dispositivo per myhostptr
    5. cudaMemcpy il valore del puntatore di myhostptr dall’host al puntatore del dispositivo &(mydevobj->myptr)

    Dopodiché, puoi cudaMemcpy i dati puntati dal puntatore incorporato myptr alla regione allocata (tramite cudaMalloc ) su myhostptr

    Notare che al punto 5, poiché sto prendendo l’indirizzo di questa posizione del puntatore, questa operazione cudaMemcpy richiede solo il puntatore mydevobj sull’host, che è valido in un’operazione cudaMemcpy (solo).

    Il valore del puntatore del dispositivo myint verrà quindi impostato correttamente per eseguire le operazioni che si sta tentando di eseguire. Se poi vuoi cudaMemcpy dati da e verso myint, usi il puntatore myhostptr in ogni chiamata cudaMemcpy, non mydevobj->myptr . Se provassimo ad usare mydevobj->myptr , richiederebbe il dereferenziamento di mydevobj e quindi usarlo per recuperare il puntatore che è memorizzato in myptr e quindi usare quel puntatore come copia da / verso la posizione. Questo non è accettabile nel codice host. Se si tenta di farlo, si otterrà un errore di seg. (Nota che per analogia, mydevobj è come il tuo Grid_dev e il mio myptr è come il tuo cdata )

    Nel complesso è un concetto che richiede un pensiero attento la prima volta che ci si imbatte in esso, e quindi domande come questa hanno una certa frequenza su SO. Potresti voler studiare alcune di queste domande per vedere esempi di codice (dato che non hai fornito il tuo codice che configura Grid_dev ):

    1. Esempio 1
    2. esempio 2
    3. esempio 3