当使用 CUDA 编程时,我们经常需要检查 CUDA 运行时 API 的返回值来判断函数调用是否成功。为了简化这个过程,通常会定义一些宏来实现自定义的错误检查和处理机制。
在 CUDA 设备代码中,由于没有直接的可视化终端输出,我们无法直接打印错误信息,无法使用std::cout,所以要使用std::cerr
定义宏如下:
// 在主机端打印错误信息
void printCudaError(const char* errorMessage, cudaError_t error, int line, const char *file) {
std::cerr << errorMessage << " " << cudaGetErrorName(error) << " at line " << line << " in file " << file << std::endl;
}
#define ck(call) do { \
cudaError_t status = call; \
if (status != cudaSuccess) { \
printCudaError("CUDA runtime API error", status, __LINE__, __FILE__); \
return status; \
} \
} while(0)
关于为什么使用 do..while 循环:
考虑以下情况,如果直接使用普通的 if
语句来定义宏:
#define ck(call) \
cudaError_t status = call; \
if (status != cudaSuccess) { \
printCudaError("CUDA runtime API error", status, __LINE__, __FILE__); \
return status; \ }
这种方式在大多数情况下工作正常,但如果在某些情况下将 ck(call)
宏用于不带大括号的代码块时,就会导致意外的错误。例如:
if (condition) ck(call);
在这种情况下,只有在 if
的条件满足时才会执行 ck(call)
宏。然而,由于没有使用大括号将 ck(call)
包裹起来,只有第一个语句(即 cudaError_t status = call;
)会被视为 if
条件的一部分,而后续的错误处理语句不会被包括在条件之内。这可能会导致错误的行为,比如不会正确返回错误码。
为了避免这种问题,可以使用 do while
循环来定义宏.