IT评测·应用市场-qidao123.com技术社区

标题: 《CUDA编程》4.CUDA步伐的错误检测 [打印本页]

作者: 老婆出轨    时间: 2024-10-6 08:23
标题: 《CUDA编程》4.CUDA步伐的错误检测
在编写CUDA步伐时,有的错误在编译过程中被发现,称为编译错误,有的在运行时出现,称为运行时刻错误,本章讨论怎样排查运行时刻错误
  1 一个检测CUDA运行时错误的宏函数

1.1 编写错误查抄宏函数

在《CUDA编程》3.简朴CUDA步伐的基本框架 中枚举的函数,返回值是cudaError_t,只有在返回cudaSuccess时,才表现调用乐成,否则返回一个错误代码,下面新建一个CUDA头文件并编写一个错误查抄的宏函数:
①新建CUDA头文件

新建的文件是error_check.cuh,注意后缀变化。
②编写错误查抄代码
在界说宏时,如果一行写不下,必要在行末写 \,表现续行*,错误查抄代码如下:
  1. #pragma once
  2. #include <stdio.h>
  3. #define CHECK(call) \
  4. do { \
  5.     const cudaError_t error_code = call; \
  6.     if (error_code != cudaSuccess) { \
  7.         printf("CUDA Error:\n"); \
  8.         printf("File: %s\n", __FILE__); \
  9.         printf("Line: %d\n", __LINE__); \
  10.         printf("Error code: %d\n", error_code); \
  11.         printf("Error message: %s\n", cudaGetErrorString(error_code)); \
  12.         exit(1); \
  13.     } \
  14. } while (0)
复制代码
该段代码会查抄返回值是否为cudaSuccess,如果不是,则返回错误代码的位置
1.2 把查抄函数添加到CUDA步伐中

这里以《CUDA编程》3.简朴CUDA步伐的基本框架中的代码例子为例,注意在头文件中添加#include "error_check.cuh",并为代码中分配内存的函数进行查抄。
然后手动将39行的代码修改为 CHECK(cudaMemcpy(d_x, h_x, M, cudaMemcpyDeviceToHost));,修改后是错误代码,原本应该是cudaMemcpyHostToDevice
  1. #include <cuda.h>
  2. #include <cuda_runtime.h>
  3. #include <math.h>
  4. #include <stdio.h>
  5. #include "error_check.cuh"
  6. const double EPS = 1.0e-15;
  7. const double a = 1.23;
  8. const double b = 2.34;
  9. const double c = 3.57;
  10. // 希望 add 函数在 GPU 上执行
  11. __global__ void add(const double* x, const double* y, double* z);
  12. void check(const double* z, const int N);
  13. int main(void) {
  14.     const int N = 100000000; // 定义数组的长度为 10 的 8 次方
  15.     const int M = sizeof(double) * N; // 每个数组所需的字节数
  16.     // 分配host内存
  17.     double* h_x = (double*)malloc(M);
  18.     double* h_y = (double*)malloc(M);
  19.     double* h_z = (double*)malloc(M);
  20.     for (int n = 0; n < N; ++n) {
  21.         h_x[n] = a;
  22.         h_y[n] = b;
  23.     }
  24.     //分配device内存
  25.     double* d_x, * d_y, * d_z;
  26.     CHECK(cudaMalloc((void**)&d_x, M));
  27.     CHECK(cudaMalloc((void**)&d_y, M));
  28.     CHECK(cudaMalloc((void**)&d_z, M));
  29.    
  30.     // 将数据从主机复制到设备上
  31.     CHECK(cudaMemcpy(d_x, h_x, M, cudaMemcpyDeviceToHost));
  32.     CHECK(cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice));
  33.     const int block_size = 128;
  34.     // 计算网格尺寸,确保所有元素都能被处理
  35.     const int grid_size = (N + block_size - 1) / block_size;
  36.     // 调用内核函数在设备中进行计算
  37.     add << <grid_size, block_size >> > (d_x, d_y, d_z);
  38.     // 将计算结果从设备复制回主机
  39.     CHECK(cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost));
  40.     check(h_z, N);
  41.     // 释放内存
  42.     free(h_x);
  43.     free(h_y);
  44.     free(h_z);
  45.     CHECK(cudaFree(d_x));
  46.     CHECK(cudaFree(d_y));
  47.     CHECK(cudaFree(d_z));
  48.     return 0;
  49. }
  50. __global__ void add(const double* x, const double* y, double* z) {
  51.     const int n = blockIdx.x * blockDim.x + threadIdx.x;
  52.     z[n] = x[n] + y[n];
  53. }
  54. void check(const double* z, const int N) {
  55.     bool has_error = false;
  56.     for (int n = 0; n < N; ++n) {
  57.         if (fabs(z[n] - c) > EPS) {
  58.             has_error = true;
  59.         }
  60.     }
  61.     printf("Has error: %d\n", has_error);
  62. }
复制代码
运行结果如下:

指出了错误代码的信息,包罗文件位置、行数、个数、和错误范例invalid argument,及代表该行函数出现了非法参数,正是由于我们手动修改导致的错误
PS: 大部分代码都可以利用该宏函数,除了cudaEventQuery(),因为它大概返回cudaErrorNotReady,但并不是代码出错了
1.3 利用该宏函数查抄核函数错误

利用上述方法并不能捕捉核函数的错误,因为核函数不返回任何值,以是若想捕捉和函数的错误,应该在调用核函数之后利用如下语句:
  1. CHECK(cudaDeviceSynchronize());
  2. CHECK(cudaGetLastError());
复制代码

仍然以上面的函数作为例子,手动的将block_size修改为1280,但我们知道该参数不能高出1024,以是会报错,代码如下:
  1. #include <cuda.h>
  2. #include <cuda_runtime.h>
  3. #include <math.h>
  4. #include <stdio.h>
  5. #include "error_check.cuh"
  6. const double EPS = 1.0e-15;
  7. const double a = 1.23;
  8. const double b = 2.34;
  9. const double c = 3.57;
  10. // 希望 add 函数在 GPU 上执行
  11. __global__ void add(const double* x, const double* y, double* z);
  12. void check(const double* z, const int N);
  13. int main(void) {
  14.     const int N = 100000000; // 定义数组的长度为 10 的 8 次方
  15.     const int M = sizeof(double) * N; // 每个数组所需的字节数
  16.     // 分配host内存
  17.     double* h_x = (double*)malloc(M);
  18.     double* h_y = (double*)malloc(M);
  19.     double* h_z = (double*)malloc(M);
  20.     for (int n = 0; n < N; ++n) {
  21.         h_x[n] = a;
  22.         h_y[n] = b;
  23.     }
  24.     //分配device内存
  25.     double* d_x, * d_y, * d_z;
  26.     CHECK(cudaMalloc((void**)&d_x, M));
  27.     CHECK(cudaMalloc((void**)&d_y, M));
  28.     CHECK(cudaMalloc((void**)&d_z, M));
  29.    
  30.     // 将数据从主机复制到设备上
  31.     CHECK(cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice));
  32.     CHECK(cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice));
  33.     const int block_size = 1280;
  34.     // 计算网格尺寸,确保所有元素都能被处理
  35.     const int grid_size = (N + block_size - 1) / block_size;
  36.     // 调用内核函数在设备中进行计算
  37.     add << <grid_size, block_size >> > (d_x, d_y, d_z);
  38.     CHECK(cudaDeviceSynchronize());
  39.     CHECK(cudaGetLastError());
  40.     // 将计算结果从设备复制回主机
  41.     CHECK(cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost));
  42.     check(h_z, N);
  43.     // 释放内存
  44.     free(h_x);
  45.     free(h_y);
  46.     free(h_z);
  47.     CHECK(cudaFree(d_x));
  48.     CHECK(cudaFree(d_y));
  49.     CHECK(cudaFree(d_z));
  50.     return 0;
  51. }
  52. __global__ void add(const double* x, const double* y, double* z) {
  53.     const int n = blockIdx.x * blockDim.x + threadIdx.x;
  54.     z[n] = x[n] + y[n];
  55. }
  56. void check(const double* z, const int N) {
  57.     bool has_error = false;
  58.     for (int n = 0; n < N; ++n) {
  59.         if (fabs(z[n] - c) > EPS) {
  60.             has_error = true;
  61.         }
  62.     }
  63.     printf("Has error: %d\n", has_error);
  64. }
复制代码
输出结果如下:

即表现配置错误,如果不利用该函数,则只能发现有一个错误,而不知道具体的错误信息。
PS: cudaDeviceSynchronize()非常斲丧时间,以是一般不在内存循环中调用,否则会严重低沉步伐性能
2 用CUDA-MEMCHECK查抄内存错误

CUDA提供了CUDA-MEMCHECK工具集,可以帮助你发现诸如越界访问、未初始化内存访问、内存泄漏等内存错误,从而进步代码的可靠性和性能。一共包含了4个工具:

以上4个工具都可由cuda-memcheck执行文件调用,此中调用memcheck时,可以简化,注意,只能对编译后的文件进行查抄,通常是.out ,命令如下:
  1. cuda-memcheck ./my_cuda_program.out
复制代码
其他三个不可以简化
利用racecheck工具:
  1. cuda-memcheck --tool racecheck ./my_cuda_program.out
复制代码
利用synccheck工具
  1. cuda-memcheck --tool synccheck ./my_cuda_program.out
复制代码
利用initcheck工具
  1. cuda-memcheck --tool initcheck ./my_cuda_program.out
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4