Provavelmente, a melhor maneira de verificar se há erros no código da API de tempo de execução é definir uma função de manipulador de estilo assert e uma macro de wrapper como esta:
#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);
}
}
Em seguida, você pode agrupar cada chamada de API com a gpuErrchk
macro, que processará o status de retorno da API chamada de quebra, por exemplo:
gpuErrchk( cudaMalloc(&a_d, size*sizeof(int)) );
Se houver um erro em uma chamada, será emitida uma mensagem de texto descrevendo o erro e o arquivo e linha no seu código em que o erro ocorreu stderr
e o aplicativo será encerrado. É possível modificar gpuAssert
para gerar uma exceção, em vez de chamar exit()
um aplicativo mais sofisticado, se necessário.
Uma segunda questão relacionada é como verificar se há erros nos lançamentos do kernel, que não podem ser envolvidos diretamente em uma chamada de macro como as chamadas da API de tempo de execução padrão. Para kernels, algo como isto:
kernel<<<1,1>>>(a);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaDeviceSynchronize() );
primeiro verificará o argumento de inicialização inválido e forçará o host a esperar até que o kernel pare e verifique se há um erro de execução. A sincronização pode ser eliminada se você tiver uma chamada subsequente à API de bloqueio como esta:
kernel<<<1,1>>>(a_d);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaMemcpy(a_h, a_d, size * sizeof(int), cudaMemcpyDeviceToHost) );
nesse caso, a cudaMemcpy
chamada pode retornar os erros que ocorreram durante a execução do kernel ou os da própria cópia de memória. Isso pode ser confuso para o iniciante, e eu recomendaria o uso da sincronização explícita após o lançamento do kernel durante a depuração para facilitar a compreensão de onde os problemas podem estar surgindo.
Observe que, ao usar o CUDA Dynamic Parallelism , uma metodologia muito semelhante pode e deve ser aplicada a qualquer uso da API de tempo de execução CUDA nos kernels do dispositivo, bem como após o lançamento de qualquer kernel do 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
, que fazem praticamente o que é descrito na resposta aceita . Veja as amostras para demonstrações. Basta optar por instalar as amostras junto com o kit de ferramentas e você o terá.