CUDA编程的错误处理

转载于CUDA编程的错误处理

关于错误处理

无论是基于CPU的编程还是基于GPU的编程,当我们调用了一个API运行程序产生了错误,就会导致程序运行结果有悖于理论结果,甚至导致程序崩溃。因此,错误的检测和错误的处理在编程中是极为重要的。当我们能够定位错误的原因,错误的纠正才能更快更准确。

Linux C系统编程错误处理

我们来回顾一下在Linux系统编程中的错误处理(基于CPU)。

在Linux系统编程中,错误是通过函数的返回值和特殊变量 errno 描述的。如一些函数调用的返回值只是告知你该函数调用发生了错误,但是并没有告诉你是什么错误。其实,在调用该系统函数发生了错误后,全局变量errno的值将被改写,改写后的值和特定的错误相对应。如当errno的值被改写为EACCES=1时,代表“权限不足”,类似的错误映射还有很多。

在头文件 errno.h 中,定义了errno变量:

extern int errno;

那么,我们如何将errno的值所代表的错误信息告知自己呢?
方法一:

#include 
void perror (const char *str);
//该函数会向stderr打印以str指向的字符串为前缀,后紧跟一个冒号,最后是errno表示的错误信息。举个栗子:
    FILE *fd;
    fd = fopen("file.txt","r");
    if (NULL == fd) {
        perror("fopen");
        exit(0);
    }

当无法找到文件“file.txt”时,系统会在屏幕上打印这样的字样(你可以查看下此时的errno,errno = ENOENT)

fopen: No such file or directory

方法二:

#include 
char * strerror (int errnum);
int strerror_t (int errnum, char *buf, size_t len);
//这两个函数就是将errno作为输入实参,来将错误信息以字符串的形式返回或者写入buf里面。举个例子

    FILE *fd;
    fd = fopen("file.txt","r");
    if (NULL == fd) {
        fprintf(stderr, "fopen : %s\n", strerror(errno));
        exit(0);
    }

当无法找到文件“file.txt”时,系统会在屏幕上打印这样的字样

fopen: No such file or directory

errno使用注意事项:

当跨函数使用errno时,注意保存errno的值到临时变量里面,以防被更改。
多线程编程中,每个线程都维护一个errno,因此它是线程安全的

CUDA编程的错误处理

和errno变量类似,在CUDA编程中,也定义了错误类型 cudaError_t ,只不过这个错误值一般是runtime API的返回值。当且仅当这个API返回值为cudaSuccess时,才说明这个API调用正确。

typedef enumcudaError cudaError_t;
关于cudaError_t类型变量的值很多,有70+种,具体的大家可以去查看nvidia的CUDA Runtime API手册。
那么问题来了,当发生了API调用错误,我们如何知道错误信息呢?

主要通过如下两个函数:

__host____device__ ​cudaError_t cudaGetLastError ( void )
__host____device__ ​cudaError_t cudaPeekAtLastError ( void )

这两个函数获取最后的错误信息,返回值是一个cudaError_t类型。不同的是,cudaGetLastError()函数将重新将系统的全局错误信息变量重置为cudaSuccess,而cudaPeekAtLastError() 函数不会有这样的操作。

注意事项:

任何CUDA API调用都会可能出错。因此,假如你要定位是否是API 1调用错误,要保证这个API之前的所有API调用都没有错误,然后在API 1调用后面加上cudaGetLastError();
Kernel的启动是异步的,为了定位它是否出错,记得加上cudaDeviceSynchronize()函数进行同步,然后再调用cudaGetLastError();
Ok, 同样的,我们得到了错误类型的值,如果知道它代表的错误信息含义呢?去参考手册上查找错误信息映射就太麻烦了,CUDA提供了如下的两个API来显示对应的错误信息:

__host__ ​ __device__ ​const char* cudaGetErrorName ( cudaError_t error )
__host__ ​ __device__ ​const char* cudaGetErrorString ( cudaError_t error )

这两个函数的形参输入就是cudaError_t错误类型变量,返回了错误名称和错误信息。举个栗子:

    cudaError_t err;
    err = cudaMemcpy(p_d, p_h, sizeof(float)*1024, cudaMemcpyHostToDevice);
    if (err != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy : %s\n", cudaGetErrorString(cudaGetLastError()));
        exit(EXIT_FAILURE);
    }

小技巧:
在CUDA examples中我们发现,程序中会有checkCudaErrors( … ),这个是定义了一个宏,来确保函数API调用正确,这种方式会使编程更加的简介明了。我们也可以自己定义一个宏:

#define checkCudaErrors( a ) do { \
    if (cudaSuccess != (a)) { \
    fprintf(stderr, "Cuda runtime error in line %d of file %s \
    : %s \n", __LINE__, __FILE__, cudaGetErrorString(cudaGetLastError()) ); \
    exit(EXIT_FAILURE); \
    } \
    } while(0);
因此,这里,我们就使用这个宏来分析runtime api是否调用正确了:
checkCudaErrors( cudaMemcpy(p_d, p_h, sizeof(float)*1024, cudaMemcpyHostToDevice) );

另外从另一本书上看的处理错误定义的宏

static void HandleError( cudaError_t err,
                         const char *file,
                         int line ) {
    if (err != cudaSuccess) {
        printf( "%s in %s at line %d\n", cudaGetErrorString( err ),
                file, line );
        exit( EXIT_FAILURE );
    }
}
#define HANDLE_ERROR( err ) (HandleError( err, __FILE__, __LINE__ ))


#define HANDLE_NULL( a ) {if (a == NULL) { \
                            printf( "Host memory failed in %s at line %d\n", \
                                    __FILE__, __LINE__ ); \
                            exit( EXIT_FAILURE );}}

你可能感兴趣的:(CUDA)