Probabilmente il modo migliore per verificare la presenza di errori nel codice API di runtime è definire una funzione di gestore dello stile di asserzione e una macro wrapper come questa:
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
Puoi quindi racchiudere ogni chiamata API con la gpuErrchk
macro, che elaborerà lo stato di ritorno della chiamata API che avvolge, ad esempio:
gpuErrchk( cudaMalloc(&a_d, size*sizeof(int)) )
Se si verifica un errore in una chiamata, verrà emesso un messaggio di testo che descrive l'errore e il file e la riga nel codice in cui si è verificato l'errore stderr
e l'applicazione verrà chiusa. Si potrebbe plausibilmente modificare gpuAssert
per sollevare un'eccezione piuttosto che chiamare exit()
un'applicazione più sofisticata se fosse necessario.
Una seconda domanda correlata è come verificare la presenza di errori nei lanci del kernel, che non possono essere inseriti direttamente in una chiamata macro come le chiamate API di runtime standard. Per i kernel, qualcosa del genere:
kernel<<<1,1>>>(a);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaDeviceSynchronize() );
per prima cosa verificherà la presenza di argomenti di avvio non validi, quindi costringerà l'host ad attendere fino a quando il kernel si arresta e verifica la presenza di un errore di esecuzione. La sincronizzazione può essere eliminata se si dispone di una successiva chiamata API di blocco come questa:
kernel<<<1,1>>>(a_d);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaMemcpy(a_h, a_d, size * sizeof(int), cudaMemcpyDeviceToHost) );
in questo caso la cudaMemcpy
chiamata può restituire sia gli errori che si sono verificati durante l'esecuzione del kernel, sia quelli della copia di memoria stessa. Questo può creare confusione per i principianti e consiglierei di utilizzare la sincronizzazione esplicita dopo il lancio del kernel durante il debug per rendere più facile capire dove potrebbero sorgere problemi.
Si noti che quando si utilizza il parallelismo dinamico CUDA , una metodologia molto simile può e deve essere applicata a qualsiasi utilizzo dell'API di runtime CUDA nei kernel del dispositivo, nonché dopo il lancio di qualsiasi kernel del dispositivo:
#include <assert.h>
#define cdpErrchk(ans) { cdpAssert((ans), __FILE__, __LINE__); }
__device__ void cdpAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
printf("GPU kernel assert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) assert(0);
}
}
getLastCudaError
echeckCudaErrors
, che fanno più o meno ciò che è descritto nella risposta accettata . Guarda gli esempi per le dimostrazioni. Basta scegliere di installare gli esempi insieme al toolkit e lo avrai.