CUDA 질문에 대한 답변과 의견과 CUDA 태그 위키 에서 모든 API 호출의 반환 상태에 오류가 있는지 확인하는 것이 좋습니다. API 설명서 같은 기능을 포함하고 cudaGetLastError
, cudaPeekAtLastError
하고 cudaGetErrorString
있지만, 추가 코드를 많이 필요로하지 않고 안정적으로 캐치 및 보고서 오류로이 함께 넣어하는 가장 좋은 방법은 무엇인지?
답변
런타임 API 코드에서 오류를 확인하는 가장 좋은 방법은 다음과 같이 assert 스타일 핸들러 함수 및 랩퍼 매크로를 정의하는 것입니다.
#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);
}
}
그런 다음 각 API 호출을 gpuErrchk
매크로로 랩핑하여 랩핑하는 API 호출의 리턴 상태를 처리합니다. 예를 들면 다음과 같습니다.
gpuErrchk( cudaMalloc(&a_d, size*sizeof(int)) );
호출에 오류가 있으면 오류를 설명하는 텍스트 메시지와 오류가 발생한 코드의 파일 및 행이 표시 stderr
되고 응용 프로그램이 종료됩니다. 필요한 경우보다 복잡한 응용 프로그램을 gpuAssert
호출하는 대신 예외를 발생 시키도록 수정할 수 exit()
있습니다.
두 번째 관련 질문은 표준 런타임 API 호출과 같은 매크로 호출로 직접 래핑 할 수없는 커널 실행 오류를 확인하는 방법입니다. 커널의 경우 다음과 같습니다.
kernel<<<1,1>>>(a);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaDeviceSynchronize() );
먼저 잘못된 시작 인수를 확인한 다음 커널이 중지 될 때까지 호스트가 대기하도록하고 실행 오류를 확인합니다. 다음과 같은 후속 차단 API 호출이있는 경우 동기화를 제거 할 수 있습니다.
kernel<<<1,1>>>(a_d);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaMemcpy(a_h, a_d, size * sizeof(int), cudaMemcpyDeviceToHost) );
이 경우 cudaMemcpy
호출은 커널 실행 중 발생한 오류 또는 메모리 사본 자체의 오류를 리턴 할 수 있습니다. 초보자에게는 혼란 스러울 수 있으므로 디버깅 중 커널 시작 후 명시 적 동기화를 사용하여 문제가 발생한 위치를 쉽게 이해할 수 있도록하는 것이 좋습니다.
CUDA Dynamic Parallelism 을 사용할 때 장치 커널 에서뿐만 아니라 장치 커널이 시작된 후에도 CUDA 런타임 API 사용에 매우 유사한 방법론을 적용 할 수 있으며 적용해야합니다.
#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);
}
}
답변
위의 talonmies의 답변은 응용 프로그램을 assert
스타일 방식으로 중단시키는 좋은 방법 입니다.
때때로 더 큰 응용 프로그램의 일부로 C ++ 컨텍스트에서 오류 조건을보고하고 복구 할 수 있습니다.
다음을 std::runtime_error
사용하여 파생 된 C ++ 예외를 발생시켜 합리적으로 간결한 방법이 있습니다 thrust::system_error
.
#include <thrust/system_error.h>
#include <thrust/system/cuda/error.h>
#include <sstream>
void throw_on_cuda_error(cudaError_t code, const char *file, int line)
{
if(code != cudaSuccess)
{
std::stringstream ss;
ss << file << "(" << line << ")";
std::string file_and_line;
ss >> file_and_line;
throw thrust::system_error(code, thrust::cuda_category(), file_and_line);
}
}
이것은 cudaError_t
던져진 예외 .what()
멤버에 파일 이름, 줄 번호 및 영어 설명을 통합합니다 .
#include <iostream>
int main()
{
try
{
// do something crazy
throw_on_cuda_error(cudaSetDevice(-1), __FILE__, __LINE__);
}
catch(thrust::system_error &e)
{
std::cerr << "CUDA error after cudaSetDevice: " << e.what() << std::endl;
// oops, recover
cudaSetDevice(0);
}
return 0;
}
출력 :
$ nvcc exception.cu -run
CUDA error after cudaSetDevice: exception.cu(23): invalid device ordinal
의 클라이언트는 some_function
원하는 경우 CUDA 오류를 다른 종류의 오류와 구별 할 수 있습니다.
try
{
// call some_function which may throw something
some_function();
}
catch(thrust::system_error &e)
{
std::cerr << "CUDA error during some_function: " << e.what() << std::endl;
}
catch(std::bad_alloc &e)
{
std::cerr << "Bad memory allocation during some_function: " << e.what() << std::endl;
}
catch(std::runtime_error &e)
{
std::cerr << "Runtime error during some_function: " << e.what() << std::endl;
}
catch(...)
{
std::cerr << "Some other kind of error during some_function" << std::endl;
// no idea what to do, so just rethrow the exception
throw;
}
때문에 thrust::system_error
인 std::runtime_error
우리가 이전 예제의 정밀도를 필요로하지 않는 경우에, 우리는 양자 택일 오류의 다양한 클래스의 동일한 방식으로 처리 할 수 :
try
{
// call some_function which may throw something
some_function();
}
catch(std::runtime_error &e)
{
std::cerr << "Runtime error during some_function: " << e.what() << std::endl;
}
답변
C ++ 표준 방법 : 오류를 확인하지 마십시오 … 예외를 발생시키는 C ++ 바인딩을 사용하십시오.
나는이 문제에 짜증이났다. Talonmies 및 Jared의 답변과 마찬가지로 매크로 쿰 래퍼 기능 솔루션을 사용했지만 솔직히 말했습니까? CUDA Runtime API를 훨씬 추악하고 C와 비슷하게 사용합니다.
그래서 나는 다른 근본적인 방법으로 이것에 접근했습니다. 결과 샘플의 경우 모든 런타임 API 호출에 대한 완전한 오류 검사 vectorAdd
와 함께 CUDA 샘플 의 일부가 있습니다 .
// (... prepare host-side buffers here ...)
auto current_device = cuda::device::current::get();
auto d_A = cuda::memory::device::make_unique<float[]>(current_device, numElements);
auto d_B = cuda::memory::device::make_unique<float[]>(current_device, numElements);
auto d_C = cuda::memory::device::make_unique<float[]>(current_device, numElements);
cuda::memory::copy(d_A.get(), h_A.get(), size);
cuda::memory::copy(d_B.get(), h_B.get(), size);
// (... prepare a launch configuration here... )
cuda::launch(vectorAdd, launch_config,
d_A.get(), d_B.get(), d_C.get(), numElements
);
cuda::memory::copy(h_C.get(), d_C.get(), size);
// (... verify results here...)
다시 말하지만 모든 잠재적 오류가 검사되고 오류가 발생한 경우 예외가 발생합니다 (캐비티 : 시작 후 커널에서 오류가 발생한 경우 이전이 아닌 결과를 복사하려고 시도한 후 발견됩니다. 커널이 성공했는지 확인하려면 cuda::outstanding_error::ensure_none()
명령으로 실행과 사본 사이의 오류를 확인해야합니다 ).
위의 코드는 내
CUDA Runtime API 라이브러리 (Github)를 위한 Thin Modern-C ++ 래퍼
예외는 호출 실패 후 문자열 설명과 CUDA 런타임 API 상태 코드를 모두 나타냅니다.
이 래퍼로 CUDA 오류를 자동으로 확인하는 방법에 대한 몇 가지 링크 :
답변
논의 된 솔루션 여기가 잘 날 위해 일했습니다. 이 솔루션은 내장 cuda 함수를 사용하며 구현이 매우 간단합니다.
관련 코드는 다음과 같습니다.
#include <stdio.h>
#include <stdlib.h>
__global__ void foo(int *ptr)
{
*ptr = 7;
}
int main(void)
{
foo<<<1,1>>>(0);
// make the host block until the device is finished with foo
cudaDeviceSynchronize();
// check for error
cudaError_t error = cudaGetLastError();
if(error != cudaSuccess)
{
// print the CUDA error message and exit
printf("CUDA error: %s\n", cudaGetErrorString(error));
exit(-1);
}
return 0;
}