深度学习部署笔记(五): CUDA 驱动API, 检查功能

1. 案例(Cuda-Driver API 1.2)

也是官方的方法


// CUDA驱动头文件cuda.h
#include <cuda.h>

#include <stdio.h>
#include <string.h>

// 使用有参宏定义检查cuda driver是否被正常初始化, 并定位程序出错的文件名、行数和错误信息
// 宏定义中带do...while循环可保证程序的正确性
#define checkDriver(op)    
    do{                    
        auto code = (op);  
        if(code != CUresult::CUDA_SUCCESS){     
            const char* err_name = nullptr;     
            const char* err_message = nullptr;  
            cuGetErrorName(code, &err_name);    
            cuGetErrorString(code, &err_message);   
            printf("%s:%d  %s failed. n  code = %s, message = %sn", __FILE__, __LINE__, #op, err_name, err_message);   
            return -1;   
        }                
    }while(0)

int main(){

    //检查cuda driver的初始化。虽然不初始化或错误初始化某些API不会报错(不信你试试),但安全起见调用任何API前务必检查cuda driver初始化
    cuInit(2); // 正确的初始化应该给flag = 0
    checkDriver(cuInit(0));

    // 测试获取当前cuda驱动的版本
    int driver_version = 0;
    checkDriver(cuDriverGetVersion(&driver_version));
    printf("Driver version is %dn", driver_version);

    // 测试获取当前设备信息
    char device_name[100];
    CUdevice device = 0;
    checkDriver(cuDeviceGetName(device_name, sizeof(device_name), device));
    printf("Device %d name is %sn", device, device_name);
    return 0;
}

可读性较差,看希望推荐的版本

2. 完善check功能(Cuda-Driver API 1.3)


// CUDA驱动头文件cuda.h
#include <cuda.h>

#include <stdio.h>
#include <string.h>

// 很明显,这种代码封装方式,更加的便于使用
//宏定义 #define <宏名>(<参数表>) <宏体>
#define checkDriver(op)  __check_cuda_driver((op), #op, __FILE__, __LINE__)

bool __check_cuda_driver(CUresult code, const char* op, const char* file, int line){

    if(code != CUresult::CUDA_SUCCESS){    
        const char* err_name = nullptr;    
        const char* err_message = nullptr;  
        cuGetErrorName(code, &err_name);    
        cuGetErrorString(code, &err_message);   
        printf("%s:%d  %s failed. n  code = %s, message = %sn", file, line, op, err_name, err_message);   
        return false;
    }
    return true;
}

int main(){

    // 检查cuda driver的初始化
    // 实际调用的是__check_cuda_driver这个函数
    checkDriver(cuInit(0));

    // 测试获取当前cuda驱动的版本
    int driver_version = 0;
    if(!checkDriver(cuDriverGetVersion(&driver_version))){
        return -1;
    }
    printf("Driver version is %dn", driver_version);

    // 测试获取当前设备信息
    char device_name[100];
    CUdevice device = 0;
    checkDriver(cuDeviceGetName(device_name, sizeof(device_name), device));
    printf("Device %d name is %sn", device, device_name);
    return 0;
}

3. 完善版本的整体解读

  1. 宏定义checkDriver(op),在宏体内部调用__check_cuda_driver((op), #op, FILE, LINE),其中#op表示将op参数转化为字符串。
  2. __check_cuda_driver函数的作用是检查CUDA driver的初始化是否成功。函数的参数包括:code表示待检查的返回值,op表示操作名称(即checkDriver宏定义的op参数),file表示文件名,line表示行号。函数首先检查code是否等于CUDA_SUCCESS,如果不是,则调用cuGetErrorName和cuGetErrorString函数获取错误代码的字符串描述,然后输出错误信息。最后返回false。
  3. 在主函数main中,先调用checkDriver(cuInit(0))检查CUDA driver的初始化是否成功。如果成功,接着测试获取当前CUDA驱动的版本和当前设备信息。其中,获取驱动版本和设备名称的操作也通过调用checkDriver函数进行了检查。如果checkDriver返回false,则退出程序。
  4. 通过这样的封装和检查,可以让CUDA驱动的调用更加安全和可靠。

4. 通过这个案例复宏定义

  1. 宏定义和内联函数都可以避免函数调用的开销,提高程序的效率。不过它们的实现方式不同,宏定义是在预处理阶段对代码进行替换,而内联函数则是在编译阶段将函数的代码直接嵌入到调用处。此外,宏定义可以接受任意类型的参数,而内联函数的参数类型必须是编译时确定的。
#define checkDriver(op)  __check_cuda_driver((op), #op, __FILE__, __LINE__)
  1. 宏定义 #define <宏名>(<参数表>) <宏体>
  2. 上面这个宏中,宏名是checkDriver, 参数表是op,这里对应的是后面的CUDA API返回的CUresult code,宏体是 __check_cuda_driver((op), #op, FILE, LINE)。它是一个函数调用语句,调用了名为 __check_cuda_driver 的函数,并传递了 4 个参数:(op)、#op、FILE, LINE
  3. FILELINE 是 C/C++ 中的预定义宏,分别表示当前代码所在文件名和行号。将它们作为参数传递给 __check_cuda_driver 函数,可以在函数内部将调用该函数的代码位置信息打印出来,方便在调试时定位错误。
  4. __check_cuda_driver 是作者自定义的代码不应使用这些标识符,以免与系统标识符产生冲突。可以自己换的
  5. 所以可以写成下面这种简便的版本,这样更好理解什么是宏定义
#include <cuda.h> // CUDA驱动头文件cuda.h

#include <stdio.h> // 使用printf
#include <string.h>

#define checkDriver(op) Mycheck((op), #op, __FILE__, __LINE__)

bool Mycheck(CUresult code, const char* op, const char* file, int line)
{   
    if (code != 0)
    {
        printf("Something went Wrongn");
    }
    else
    {
        printf("Everything is finen");
    }
    
    
}

int main()
{   
    // 检查cuda driver的初始化
    checkDriver(cuInit(0)); 
    checkDriver(cuInit(1));
    return 0;
}
Everything is fine
Something went Wrong

5. 完整自己理解注释版

#include <cuda.h> // CUDA驱动头文件cuda.h

#include <stdio.h> // 使用printf
#include <string.h>

#define checkDriver(op) Mycheck((op), #op, __FILE__, __LINE__)

bool Mycheck(CUresult code, const char* op, const char* file, int line)
{   
    if (code != CUresult::CUDA_SUCCESS) // 等同于 if(code != 0)
    {
        const char* err_name = nullptr;
        const char *err_message = nullptr;
        // 修改err_name, error_message指针,指向错误信息,报错的字符串的首地址
        cuGetErrorName(code, &err_name);      
        cuGetErrorString(code, &err_message);
        printf("%s, %d %s 失败n", file, line, op);
        printf("错误名字: %sn", err_name);
        printf("错误信息: %sn", err_message);
        return false;
    }

    return true;
}

int main()
{   
    // 检查cuda driver的初始化
    checkDriver(cuInit(0));
    

    // 测试当前CUDA版本
    int driver_version = 0;
    if (!checkDriver(cuDriverGetVersion(&driver_version)) ) // if (false)
    {
        return -1;
    }
    printf("当前驱动版本是: %dn", driver_version);

    // 测试当前设备信息
    char device_name[100];
    int device = 0;  // CUdevice device = 0

    if(!checkDriver(cuDeviceGetName(device_name, sizeof(device_name), device)))
    {
        return -1;
    };
    printf("Device %d name is %sn", device, device_name);
    return 0;
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>