Examinando as respostas e os comentários sobre as perguntas da CUDA, e no wiki da tag CUDA , vejo que é frequentemente sugerido que o status de retorno de cada chamada de API seja verificado quanto a erros. A documentação da API contém funções como cudaGetLastError
, cudaPeekAtLastError
e cudaGetErrorString
, mas o que é a melhor maneira de colocá-los juntos para erros de captura e relatório de forma confiável sem a necessidade de um monte de código extra?
cuda
error-checking
talonmies
fonte
fonte
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á.Respostas:
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:
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: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 modificargpuAssert
para gerar uma exceção, em vez de chamarexit()
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:
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:
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:
fonte
cudaDeviceReset()
antes de sair também? E uma cláusula para desalocação de memória?A resposta das garras acima é uma ótima maneira de abortar um aplicativo de
assert
maneira estilo.Ocasionalmente, podemos desejar relatar e recuperar de uma condição de erro em um contexto C ++ como parte de um aplicativo maior.
Aqui está uma maneira razoavelmente concisa de fazer isso lançando uma exceção C ++ derivada do
std::runtime_error
usothrust::system_error
:Isso incorporará o nome do arquivo, o número da linha e uma descrição do idioma inglês
cudaError_t
no.what()
membro da exceção lançada :A saída:
Um cliente de
some_function
pode distinguir erros CUDA de outros tipos de erros, se desejar:Como
thrust::system_error
é astd::runtime_error
, podemos alternativamente lidar com isso da mesma maneira que uma ampla classe de erros se não exigirmos a precisão do exemplo anterior:fonte
<thrust/system/cuda_error.h>
agora é efetivamente<thrust/system/cuda/error.h>
.A maneira canônica de C ++: Não verifique se há erros ... use as ligações C ++ que geram exceções.
Eu costumava ficar irritado com esse problema; e eu costumava ter uma solução de função macro-cum-wrapper, como nas respostas de Talonmies e Jared, mas, honestamente? Torna o uso da API do CUDA Runtime ainda mais feio e parecido com o C.
Então, eu abordei isso de uma maneira diferente e mais fundamental. Para uma amostra do resultado, aqui está parte da
vectorAdd
amostra CUDA - com verificação completa de erros de cada chamada da API em tempo de execução:Novamente - todos os erros em potencial são verificados e uma exceção se ocorrer um erro (ressalva: se o kernel causou algum erro após o lançamento, ele será capturado após a tentativa de copiar o resultado, não antes; para garantir que o kernel tenha êxito, precisa verificar se há um erro entre o lançamento e a cópia com um
cuda::outstanding_error::ensure_none()
comando).O código acima usa meu
Wrappers Modern-C ++ finos para a biblioteca de API de tempo de execução CUDA (Github)
Observe que as exceções contêm uma explicação de cadeia e o código de status da API de tempo de execução CUDA após a chamada com falha.
Alguns links para como os erros CUDA são verificados automaticamente com estes wrappers:
fonte
A solução discutida aqui funcionou bem para mim. Esta solução usa funções cuda embutidas e é muito simples de implementar.
O código relevante é copiado abaixo:
fonte