第4章 CUDA程序的错误检测
和编写 C++程序一样,编写 CUDA 程序时难免会出现各种各样的错误。有的错误在编译的过程中就可以被编译器捕捉,称为编译错误。有的错误在编译期间没有被发现,但在运行的时候出现,称为运行时刻的错误。一般来说,运行时刻的错误更难排除。本章讨论如何检测运行时刻的错误,包括使用一个检查 CUDA 运行时 API函数返回值的宏函数及使用 CUDA-MEMCHECK 工具。
4.1 一个检测CUDA运行时错误的宏函数
#pragma once
#include<stdio.h>
#define CHECK(call) \
{\
const cudaError_t error_code=call;\
if(error_code!=cudaSuccess)\
\
printf("CUDA Error:\n"); \
printf(" File: %s\n", __FILE__); \
printf(" Line: %d\n", __LINE__); \
printf(" Error code: %d\n",error_code);\
printf(" Error text: %s\n",cudaGetErrorString(error_code));\
exit(1);\
}\
}while(0)
(1)该文件开头一行的 #pragma once 是一个预处理指令,其作用是确保当前文件在一个编译单元中不被重复包含。该预处理指令和如下复合的预处理指令作用相当,但更加简洁:
#ifndef ERROR_CUH
#define ERROR_CUH
头文件中的内容(即上述文件中第2~17行的内容)#endif
(2) 该宏函数的名称是 CHECK,参数 call 是一个 CUDA 运行时 API函数
(3)在定义宏时,如果一行写不下,则需要在行末写 ,表示续行。
(4)第7行定义了一个 cudaError_t 类型的变量 error_code,并初始化为参数 call的返回值。
(5)第8行判断该变量的值是否为 cudaSuccess。如果不是在第 9~16 行报告相关文件、行数、错误代号及错误的文字描述并退出程序。cudaGetErrorString()显然也是一个 CUDA 运行时 API 函数,作用是将错误代号转化为错误的文字描述。在使用该宏函数时,只要将一个 CUDA 运行时 API函数当作参数传入该宏函数即可。例如,如下宏函数的调用:
CHECK(cudaFree(d_x));
4.2 用CUDA-MEMCHECK检查内存错误
CUDA 提供了名为CUDA-MEMCHECK的工具集,具体包括 memcheck、racecheck、initcheck、synccheck共 4 个工具。它们可由可执行文件 cuda-memcheck 调用:
$ cuda-memcheck --tool memcheck [options] app_name [options]
$cuda-memcheck --tool racecheck [options] app_name [options]
$cuda-memcheck --tool initcheck [options] app_name [options]
$cuda-memcheck --tool synccheck [options] app_name [options]
对于 memcheck 工具,可以简化为
$ cuda-memcheck [options] app_name [options]
我们这里只给出一个使用 memcheck 工具的例子。如果将第 3章的文件add3if.cu 中的 if 语去掉,编译后用
$ cuda-memcheck ./a.out
运行程序,可得到一大串输出,其中最后一行为(读者得到的数字可能不一定是下面的 36)
======== ERROR SUMMARY: 36 error
这说明程序有内存错误,与第 3 章的讨论一致。将 if 语句加上,编译后再用$cuda-memcheck ./a.out
运行,将得到简单的输出,其中最后一行为
==== ERROR SUMMARY: 0 errors