《CUDA编程》4.CUDA程序的错误检测

news2024/11/25 20:24:21

在编写CUDA程序时,有的错误在编译过程中被发现,称为编译错误,有的在运行时出现,称为运行时刻错误,本章讨论如何排查运行时刻错误

1 一个检测CUDA运行时错误的宏函数

1.1 编写错误检查宏函数

在《CUDA编程》3.简单CUDA程序的基本框架 中列举的函数,返回值是cudaError_t,只有在返回cudaSuccess时,才表示调用成功,否则返回一个错误代码,下面新建一个CUDA头文件并编写一个错误检查的宏函数:

①新建CUDA头文件
在这里插入图片描述
新建的文件是error_check.cuh,注意后缀变化。
②编写错误检查代码
在定义宏时,如果一行写不下,需要在行末写 \,表示续行*,错误检查代码如下:

#pragma once
#include <stdio.h>

#define CHECK(call) \
do { \
    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 message: %s\n", cudaGetErrorString(error_code)); \
        exit(1); \
    } \
} while (0)

该段代码会检查返回值是否为cudaSuccess,如果不是,则返回错误代码的位置

1.2 把检查函数添加到CUDA程序中

这里以《CUDA编程》3.简单CUDA程序的基本框架中的代码例子为例,注意在头文件中添加#include "error_check.cuh",并为代码中分配内存的函数进行检查。

然后手动将39行的代码修改为 CHECK(cudaMemcpy(d_x, h_x, M, cudaMemcpyDeviceToHost));,修改后是错误代码,原本应该是cudaMemcpyHostToDevice

#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
#include <stdio.h>
#include "error_check.cuh"


const double EPS = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;

// 希望 add 函数在 GPU 上执行
__global__ void add(const double* x, const double* y, double* z);
void check(const double* z, const int N);

int main(void) {
    const int N = 100000000; // 定义数组的长度为 10 的 8 次方
    const int M = sizeof(double) * N; // 每个数组所需的字节数

    // 分配host内存
    double* h_x = (double*)malloc(M);
    double* h_y = (double*)malloc(M);
    double* h_z = (double*)malloc(M);


    for (int n = 0; n < N; ++n) {
        h_x[n] = a;
        h_y[n] = b;
    }

    //分配device内存
    double* d_x, * d_y, * d_z;
    CHECK(cudaMalloc((void**)&d_x, M));
    CHECK(cudaMalloc((void**)&d_y, M));
    CHECK(cudaMalloc((void**)&d_z, M));
    
    // 将数据从主机复制到设备上
    CHECK(cudaMemcpy(d_x, h_x, M, cudaMemcpyDeviceToHost));
    CHECK(cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice));

    const int block_size = 128;
    // 计算网格尺寸,确保所有元素都能被处理
    const int grid_size = (N + block_size - 1) / block_size;

    // 调用内核函数在设备中进行计算
    add << <grid_size, block_size >> > (d_x, d_y, d_z);

    // 将计算结果从设备复制回主机
    CHECK(cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost));
    check(h_z, N);

    // 释放内存
    free(h_x);
    free(h_y);
    free(h_z);
    CHECK(cudaFree(d_x));
    CHECK(cudaFree(d_y));
    CHECK(cudaFree(d_z));
    return 0;
}

__global__ void add(const double* x, const double* y, double* z) {
    const int n = blockIdx.x * blockDim.x + threadIdx.x;
    z[n] = x[n] + y[n];
}

void check(const double* z, const int N) {
    bool has_error = false;
    for (int n = 0; n < N; ++n) {
        if (fabs(z[n] - c) > EPS) {
            has_error = true;
        }
    }
    printf("Has error: %d\n", has_error);
}

运行结果如下:
在这里插入图片描述
指出了错误代码的信息,包括文件位置、行数、个数、和错误类型invalid argument,及代表该行函数出现了非法参数,正是由于我们手动修改导致的错误

PS: 大部分代码都可以使用该宏函数,除了cudaEventQuery(),因为它可能返回cudaErrorNotReady,但并不是代码出错了

1.3 使用该宏函数检查核函数错误

使用上述方法并不能捕捉核函数的错误,因为核函数不返回任何值,所以若想捕捉和函数的错误,应该在调用核函数之后使用如下语句:

CHECK(cudaDeviceSynchronize());
CHECK(cudaGetLastError());
  • 第一个语句是同步主机和设备,因为核函数的调用是异步的,使用该函数可以确保之前的CUDA操作全部完成,以便检查这些操作是否成功
  • 返回自上次调用 cudaGetLastError() 或者自程序开始以来最后一个 CUDA API 调用的错误代码。

依旧以上面的函数作为例子,手动的将block_size修改为1280,但我们知道该参数不能超过1024,所以会报错,代码如下:

#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
#include <stdio.h>
#include "error_check.cuh"


const double EPS = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;

// 希望 add 函数在 GPU 上执行
__global__ void add(const double* x, const double* y, double* z);
void check(const double* z, const int N);

int main(void) {
    const int N = 100000000; // 定义数组的长度为 10 的 8 次方
    const int M = sizeof(double) * N; // 每个数组所需的字节数

    // 分配host内存
    double* h_x = (double*)malloc(M);
    double* h_y = (double*)malloc(M);
    double* h_z = (double*)malloc(M);


    for (int n = 0; n < N; ++n) {
        h_x[n] = a;
        h_y[n] = b;
    }

    //分配device内存
    double* d_x, * d_y, * d_z;
    CHECK(cudaMalloc((void**)&d_x, M));
    CHECK(cudaMalloc((void**)&d_y, M));
    CHECK(cudaMalloc((void**)&d_z, M));
    
    // 将数据从主机复制到设备上
    CHECK(cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice));
    CHECK(cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice));

    const int block_size = 1280;
    // 计算网格尺寸,确保所有元素都能被处理
    const int grid_size = (N + block_size - 1) / block_size;

    // 调用内核函数在设备中进行计算
    add << <grid_size, block_size >> > (d_x, d_y, d_z);
    CHECK(cudaDeviceSynchronize());
    CHECK(cudaGetLastError());

    // 将计算结果从设备复制回主机
    CHECK(cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost));
    check(h_z, N);

    // 释放内存
    free(h_x);
    free(h_y);
    free(h_z);
    CHECK(cudaFree(d_x));
    CHECK(cudaFree(d_y));
    CHECK(cudaFree(d_z));
    return 0;
}

__global__ void add(const double* x, const double* y, double* z) {
    const int n = blockIdx.x * blockDim.x + threadIdx.x;
    z[n] = x[n] + y[n];
}

void check(const double* z, const int N) {
    bool has_error = false;
    for (int n = 0; n < N; ++n) {
        if (fabs(z[n] - c) > EPS) {
            has_error = true;
        }
    }
    printf("Has error: %d\n", has_error);
}

输出结果如下:
在这里插入图片描述
即表示配置错误,如果不使用该函数,则只能发现有一个错误,而不知道具体的错误信息。

PS: cudaDeviceSynchronize()非常消耗时间,所以一般不在内存循环中调用,否则会严重降低程序性能

2 用CUDA-MEMCHECK检查内存错误

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

  • memcheck:用于检测内存访问错误,包括越界访问、未初始化内存访问等,常见错误类型有:
    –Global Out-of-bounds:访问超出全局内存范围。
    –Local Out-of-bounds:访问超出局部内存范围。
    –Uninitialized Access:访问未初始化的内存。
    –Invalid Device Pointer:使用无效的设备指针
  • racecheck:用于检测数据竞争,即多个线程同时访问同一内存位置且至少有一个线程在写入,常见错误类型有:
    –Race Condition:多个线程同时访问同一内存位置且至少有一个线程在写入。
  • synccheck:用于检测同步错误,即线程之间的同步问题,常见错误类型有:
    –Barrier Synchronization Error:线程在屏障同步点出现错误。
    –Grid Synchronization Error:线程在网格同步点出现错误。
  • initcheck:用于检测未初始化内存的使用,常见错误类型有:
    –Uninitialized Memory Use:使用未初始化的内存。

以上4个工具都可由cuda-memcheck执行文件调用,其中调用memcheck时,可以简化,注意,只能对编译后的文件进行检查,通常是.out ,命令如下:

cuda-memcheck ./my_cuda_program.out

其他三个不可以简化

使用racecheck工具:

cuda-memcheck --tool racecheck ./my_cuda_program.out

使用synccheck工具

cuda-memcheck --tool synccheck ./my_cuda_program.out

使用initcheck工具

cuda-memcheck --tool initcheck ./my_cuda_program.out

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2190479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从0到1:培训机构排课小程序开发笔记一

业务调研 随着人们生活水平的提高&#xff0c;健康意识和学习需求日益增强&#xff0c;私教、健身和培训机构的市场需求迅速增长。高效的排课系统不仅可以提升机构的管理效率&#xff0c;还能提高学员的满意度。解决传统的排课方式存在的时间冲突、信息不对称、人工操作繁琐等…

51单片机的家用煤气报警系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温度传感器CO传感器蓝牙LED、蜂鸣器等模块构成。适用于家用天然气泄露报警器、煤气泄露报警器、无线报警等相似项目。 可实现功能: 1、LCD1602实时显示温度和煤气浓度 2、温度传感器DS18B20采集环境温度 3、CO传…

【Mybatis篇】Mybatis的关联映射详细代码带练 (多对多查询、Mybatis缓存机制)

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】,【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;一.关联映射概述 &#x1f6a…

【BUG】P-tuningv2微调ChatGLM2-6B时所踩的坑

0.前言 P-tuning v2的实验在网上一抓一大把&#xff0c;这里就说一下我在微调过程中遇到的有些bug&#xff0c;踩过的一些坑&#xff0c;在网上找了很久都没有一些好的解决方案&#xff0c;在这里记录一下。 1.下载预训练模型 在官方给出的教程中&#xff0c;并不需要预先将模…

【springboot】简易模块化开发项目整合Swagger2

接上一项目【springboot】简易模块化开发项目整合MyBatis-plus&#xff0c;进行拓展项目 1.新建模块 右键项目→New→Module&#xff0c;新建一个模块 父项目选择fast-demo&#xff0c;命名为fast-demo-config&#xff0c;用于存放所有配置项 添加后&#xff0c;项目结构如图…

X3U·可编程控制器的定位控制

FX3U可编程控制器的定位控制进行说明。 一、概要 FX3U可编程控制器可以向伺服电机、步进电机等输出脉冲信号&#xff0c;从而进行定位控制。 脉冲频率高的时候&#xff0c;电机转得快:脉冲数多的时候&#xff0c;电机转得多。用脉冲频率、脉冲数来设定定位对象…

Linux基本命令及vim应用实训练习

Linux基本命令及vim应用实训练习 1. 2. 3. 4. 5. 使用man cp找出

4 思科模拟器的介绍和使用

4 思科模拟器的介绍和使用 思科的IOS给我们提供了三大模式 设备开机后&#xff0c;进入的模式是【用户模式】 Router表示设备的名称 “>”表示用户模式 在用户模式输入"?" 可列出在用户模式可以使用的命令 第二种模式是特权模式,输入enable进入特权模式&…

RNN经典案例——构建人名分类器

RNN经典案例——人名分类器 一、数据处理1.1 去掉语言中的重音标记1.2 读取数据1.3 构建人名类别与人名对应关系字典1.4 将人名转换为对应的onehot张量 二、构建RNN模型2.1 构建传统RNN模型2.2 构建LSTM模型2.3 构建GRU模型 三、构建训练函数并进行训练3.1 从输出结果中获得指定…

字符和ACSII编码

1.字符和ASCII编码 C语言中char类型&#xff0c;专门用来创建字符变量&#xff0c;字符放在单引号中 char ch a ASCII码表 c官网&#xff0c;最全de c官网链接 数字字符0~9对应ASCII码十进制48~57 字符 大写字母A~Z对应ASCII码十进制65~90 字符 小写字母a~z对应ASCII码…

EtherCAT 转 EtherNet/IP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899协议转换通信网关 EtherCAT 转 EtherNet/IP GW系列型号 MS-GW12 概述 MS-GW12 是 EtherCAT 和 EtherNet/IP 协议转换网关&#xff0c;为用户提供两…

突发!Meta重磅发布Movie Gen入局视频生成赛道!

引言 Meta于2024年10月4日首次推出 Meta Movie Gen&#xff0c;号称是迄今为止最先进的媒体基础模型。Movie Gen 由 Meta 的 AI 研究团队开发&#xff0c;在一系列功能上获取最先进的效果&#xff0c;包括&#xff1a;文生视频、创建个性化视频、精准的视频编辑和音频创作。 …

递归--C语言

1 递归定义 函数自己调用自己的过程&#xff0c;称为递归。 2 递归的必要条件 1.必须要有终止条件。达到条件就停止递归&#xff0c;退出函数。2.每次调用自己都要越来越接近这个终止条件。 因此写函数的时候&#xff0c;也分两部分 第一部分&#xff1a;写终止条件&#x…

点击按钮提示气泡信息(Toast)

演示效果&#xff1a; 目录结构&#xff1a; activity_main.xml(布局文件)代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http:…

【第三版 系统集成项目管理工程师】第15章 组织保障

持续更新。。。。。。。。。。。。。。。 【第三版】第十五章 组织保障 15.1信息和文档管理15.1.1 信息和文档1.信息系统信息-P5462.信息系统文档-P546 15.1.2 信息(文档)管理规则和方法1.信息(文档)编制规范-P5472.信息(文档)定级保护-P5483.信息(文档)配置管理-P549练习 15.…

38 文件包含(标准库头文件、自定义头文件)、相对路径与绝对路径、条件编译(#if、#ifdef、#if define、#ifndef)

目录 1 文件包含 1.1 #include 指令 1.2 包含标准库头文件 1.3 包含自定义头文件 1.3.1 使用相对路径 1.3.2 使用绝对路径 2 条件编译 2.1 #if … #endif 2.1.1 语法格式 2.1.2 功能说明 2.1.3 流程分析 2.1.4 案例演示&#xff1a;#if 0 ... #endif 2.1.5 案例演…

关于懒惰学习与渴求学习的一份介绍

在这篇文章中&#xff0c;我将介绍些懒惰学习与渴求学习的算法例子&#xff0c;会介绍其概念、优缺点以及其python的运用。 一、渴求学习 1.1概念 渴求学习&#xff08;Eager Learning&#xff09;是指在训练阶段构建出复杂的模型&#xff0c;然后在预测阶段运用这个构建出的…

分布式锁--redission 最佳实践!

我们知道如果我们的项目服务不只是一个实例的时候&#xff0c;单体锁就不再适用&#xff0c;而我们自己去用redis实现分布式锁的话&#xff0c;会有比如锁误删、超时释放、锁的重入、失败重试、Redis主从一致性等等一系列的问题需要自己解决。 当然&#xff0c;上述问题并非无…

3dsMax合并FBX的时候相同的节点会被合并(重命名解决),3Ds MAX创建空物体(虚拟对象或者点)

3dsMax合并FBX的时候相同的节点会被合并 3dsamax的文档&#xff0c;但是并没有说FBX的合并如何处理 https://help.autodesk.com/view/3DSMAX/2024/CHS/?guidGUID-98146EB8-436F-4954-8682-C57D4E53262A模型节点信息&#xff0c;yase&#xff0c;Points&#xff0c;Mesh 都是点…

【优选算法】(第二十一篇)

目录 外观数列(medium) 题目解析 讲解算法原理 编写代码 数⻘蛙&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 外观数列(medium) 题目解析 1.题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2.题目描述 给定⼀个正整数n&#xff0…