cuda编程学习——运行错误检测(四)

news2024/11/29 10:47:47

前言

参考资料:

高升博客
《CUDA C编程权威指南》
以及 CUDA官方文档
CUDA编程:基础与实践 樊哲勇

文章、讲解视频同步更新公众《AI知识物语》,B站:出门吃三碗饭

1:编写头文件erro.cuh

编写一个头文件(error.cuh),它包含一个检测CUDA运行 时错误的宏函数(macro function),内容如下:
(1) #pragma once 是一个预处理指令,其作用是确保当前文件在一个 编译单元中不被重复包含。
(2)该宏函数的名称是 CHECK,参数 call 是一个CUDA运行时 API 函数。
(3)定义宏时,如果一行写不下,需要在行末写 \,表示续行。
(4)第 7 行定义了一个 cudaError_t 类型的变量 error_code,并初始化为函数 call 的 返回值。
(5)第 8 行判断该变量的值是否为 cudaSuccess。如果不是,在第 9-16 行报告相关文件、 行数、错误代号及错误的文字描述并退出程序。第14行的cudaGetErrorString()显然也是一个CUDA运行时 API 函数,作用是将错误代号转化为错误的文字描述。

#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 text: %s\n",                \
            cudaGetErrorString(error_code));          \
        exit(1);                                      \
    }                                                 \
} while (0)

2:编写测试程序

#include<stdint.h>
#include<cuda.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include <math.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 text: %s\n",                \
            cudaGetErrorString(error_code));          \
        exit(1);                                      \
    }                                                 \
} while (0)


const double EPSILON = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;
void __global__ add(const double* x, const double* y, double* z, const int N);
void check(const double* z, const int N);

int main(void)
{
    const int N = 100000000;
    const int M = sizeof(double) * N;
    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;
    }

    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));//Set Error
    CHECK(cudaMemcpy(d_y, h_y, M, cudaMemcpyDeviceToHost));

    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, N);

    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;
}

void __global__ add(const double* x, const double* y, double* z, const int N)
{
    const int n = blockDim.x * blockIdx.x + threadIdx.x;
    if (n < N)
    {
        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) > EPSILON)
        {
            has_error = true;
        }
    }
    printf("%s\n", has_error ? "Has errors" : "No errors");
}

在这里插入图片描述

可见,宏函数正确地捕捉到了运行时刻的错误,告诉我们文件 checkerror.cu 的第 50 行代 码中出现了非法的参数。非法参数指的是 cudaMemcpy 函数的参数有问题,因为我们故意 将 cudaMemcpyHostToDevice 写成了 cudaMemcpyDeviceToHost。可见,用了检查错误的宏 函数之后,我们可以得到更有用的错误信息,而不仅仅是一个错误的运行结果。从这里开 始,我们将坚持用这个宏函数包装大部分的 CUDA 运行时 API 函数。有一个例外是 cudaEventQuery 函数,因为它很有可能返回 cudaErrorNotReady,但又不代表程序出错了。

3:检测核函数

上述方法不能捕捉调用核函数的相关错误,因为核函数不返回任何值(回顾一下,核 函必须用 void 修饰)。有一个方法可以捕捉调用核函数可能发生的错误,即在调用核函数之后加上如下两个语句:
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());

第一个语句的作用是捕捉第二个语句之前的最后一个错误,第二个语句的作用是同 步主机与设备。之所以要同步主机与设备,是因为核函数的调用是异步的,即主机发出调用核函数的命令后会立即执行后面的语句,不会等待核函数执行完毕。

编写程序来测试核函数error

线程块大小的最大值 是 1024(这对从开普勒到图灵的所有架构都成立)。假如我们不小心将核函数执行配置中 的线程块大小写成了 1280,该核函数将不能被成功地调用。第 57 行的代码成功地捕获了该错误,告诉我们程序中核函数的执行配置参数有误:

在这里插入图片描述

#include <math.h>
#include <stdio.h>
#include<stdint.h>
#include<cuda.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#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 text: %s\n",                \
            cudaGetErrorString(error_code));          \
        exit(1);                                      \
    }                                                 \
} while (0)

const double EPSILON = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;
void __global__  add(const double* x, const double* y, double* z, const int N);
void check(const double* z, const int N);

int main(void)
{
    const int N = 100000000;
    const int M = sizeof(double) * N;
    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;
    }

    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, N);
    CHECK(cudaGetLastError());
    CHECK(cudaDeviceSynchronize());

    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;
}

void __global__ add(const double* x, const double* y, double* z, const int N)
{
    const int n = blockDim.x * blockIdx.x + threadIdx.x;
    if (n < N)
    {
        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) > EPSILON)
        {
            has_error = true;
        }
    }
    printf("%s\n", has_error ? "Has errors" : "No errors");
}

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

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

相关文章

LabVIEWCompactRIO 开发指南第六章38

LabVIEWCompactRIO 开发指南第六章38 了解数据如何在模块硬件组件和LabVIEW FPGA框图之间传输&#xff0c;可以帮助开发更好的程序并更快地进行调试。本节介绍不同的硬件体系结构&#xff0c;模拟和数字C系列I/O模块以及如何与每个模块通信。这些模块通常用于测量或控制信号&a…

10. python字典

文章目录 一、什么是字典二、访问键-值对三、添加、修改键-值对四、删除键-值对4.1 语句del4.2 方法pop() 五、创建空字典六、遍历字典6.1方法items()6.2方法keys()6.3方法values() 七、嵌套7.1 字典列表7.2 在字典中存储列表7.3 在字典中存储字典 一、什么是字典 #创建一个字…

canal server 标准化集群搭建(一)

1.背景 随这业务增加&#xff0c;数据同步服务 canal server 需求与日俱增&#xff0c;以往私搭乱建的 canal server 不符合运维标准化。 2.目的 规范 canal server 的部署&#xff0c;形成固定操作流程及文档 3. 环境版本 canal server&#xff1a; canal.deployer-1.1.…

电装光庭汽车电子(武汉)有限公司

电装光庭汽车电子&#xff08;武汉&#xff09;有限公司 &#xff08;汽车座舱显示系统&#xff0c;汽车电子产品及其材料和组件的开发&#xff0c;设计&#xff0c;制造&#xff0c;销售&#xff0c;批发&#xff0c;进出口&#xff09; 一、公司介绍 电装光庭汽车电子是一…

Android中使用kotlin进行xutils数据库版本升级

Android中使用kotlin进行xutils数据库版本升级 前言 最近的项目是一个很老的项目&#xff0c;数据库采用的是xutils中的数据库&#xff0c;之前的业务没有关于版本变更和数据库修改的业务&#xff0c;这次新需求数据库需要新加一张表&#xff0c;之前的表也需要修改字段&…

2022机器学习阶段性复盘

2022机器学习阶段性复盘 文章目录 2022机器学习阶段性复盘一、业务洞察1.1 业务调研1.2 采样策略 一、特征工程1.1 特征筛选之iv_psi1.2 特征筛选之启发式搜索1.3 时间特征的曲率变换1.4 多重共线性特征的剔除1.5 什么样的特征适合树模型或LR模型1.5 什么样的特征是稳定可泛化特…

cuda编程学习——GPU加速/时间计时Clock 干货向(五)

前言 参考资料&#xff1a; 高升博客 《CUDA C编程权威指南》 以及 CUDA官方文档 CUDA编程&#xff1a;基础与实践 樊哲勇 文章所有代码可在我的GitHub获得&#xff0c;后续会慢慢更新 文章、讲解视频同步更新公众《AI知识物语》&#xff0c;B站&#xff1a;出门吃三碗饭 …

路径规划算法:基于花授粉优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于花授粉优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于花授粉优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法…

面试官:这么简单的二叉树算法都不会?

今天我们来看一个有趣的算法题&#xff0c;也是一道高频面试题。这个题目是leetcode的第572题&#xff0c;要求是这样的&#xff1a;给定两颗二叉树A和B&#xff0c;判断B是否是A的子树。在下面这个例子中可以看到B是A的子树。 想一想该怎样解决这个问题呢&#xff1f;如果B是A…

Python丨tkinter开发常用的29种功能用法(建议码住)

在Python软件开发中&#xff0c;tkinter中command功能的作用是为按钮、菜单等组件绑定回调函数&#xff0c;用户操作该组件时会触发相应的函数执行。 本文涵盖了各种组件和功能&#xff1a; 1、为Button组件&#xff08;按钮&#xff09;绑定回调函数 import tkinter as tk …

模拟量偏差报警功能块(SCL代码)

工业模拟量采集的相关基础知识,可以查看专栏的系列文章,这里不再赘述,常用链接如下: PLC模拟量采集算法数学基础(线性传感器)_plc傳感器數據轉化_RXXW_Dor的博客-CSDN博客模拟量采集库如何设计,具体算法代码请参看我的另一篇博文:PLC模拟量输入 模拟量转换FC:S_ITR_R…

栈和队列(详解)

&#x1f355;博客主页&#xff1a;️自信不孤单 &#x1f36c;文章专栏&#xff1a;数据结构与算法 &#x1f35a;代码仓库&#xff1a;破浪晓梦 &#x1f36d;欢迎关注&#xff1a;欢迎大家点赞收藏关注 文章目录 &#x1f353;栈1. 栈的概念及结构2. 栈的实现2.1 初始化栈2.…

MySQL运维篇(三)

五.读写分离 5.1 介绍 读写分离,简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作&#xff0c;从数据库提供读操作&#xff0c;这样能有效地减轻单台数据库的压力。 通过MyCat即可轻易实现上述功能&#xff0c;不仅可以支持MySQL&#x…

【论文总结】Composition Kills: A Case Study of Email Sender Authentication

构成杀伤力&#xff1a; 电子邮件发送者认证的案例研究 摘要 基于组件的软件设计是构建现代软件系统的一种主要工程方法。然而&#xff0c;由于不同组件之间对信息的解释可能不一致&#xff0c;这种编程范式产生了安全问题。在本文中&#xff0c;我们利用这种不一致来识别电子…

双列集合 JAVA

双列集合 一次需要添加一对数据&#xff0c;分别为键和值键不可以重复&#xff0c;值可以重复键和值是一一对应的&#xff0c;每一个键只可以找到自己对应的值键值对在java中也叫做Entry对象 #mermaid-svg-zKLj0vUbRaN9zlse {font-family:"trebuchet ms",verdana,ar…

SpringBoot2-基础入门(二)

SpringBoot2 - 基础入门&#xff08;二&#xff09; 了解自动装配原理 文章目录 SpringBoot2 - 基础入门&#xff08;二&#xff09;了解自动装配原理一、依赖管理1.1 父项目做依赖管理1.2 starer场景启动器 2、自动配置2.1 自动配置依赖2.2 组件扫描 3、配置文件3.1 各种配置…

【软件测试知识】

目录 软件测试软件测试模型瀑布模型V 模型W 模型敏捷开发模型 软件开发流程软件测试方法白盒测试黑盒测试 软件测试 软件测试模型 说到开发模型&#xff0c;从软件发展来看&#xff0c;比较典型的有瀑布模型&#xff0c;V 模型和 W 模型以及 敏捷开发模型。并不是说开发模型的…

【论坛java项目】第二章 Spring Boot实践,开发社区登录模块:发送邮件、开发注册功能、会话管理、生成验证码、开发登录、退出功能、

&#x1f600;如果对你有帮助的话&#x1f60a; &#x1f33a;为博主点个赞吧 &#x1f44d; &#x1f44d;点赞是对博主最大的鼓励&#x1f60b; &#x1f493;爱心发射~&#x1f493; 目录 一、发送邮件1、启用客户端SMTP服务2、导入jar包3、邮箱参数配置MailClientdemo.html…

第13届蓝桥杯Scratch省赛真题集锦

编程题 第 1 题 问答题 报数游戏 题目说明 背景信息: 5个男生和3个女生&#xff0c;8个人围成一个圆圈&#xff0c;给定一个数字n (2 小于等于n 小于等于5)。从第一个开始依次报数&#xff0c;当报数为n时&#xff0c;这个人离开圆圈。然后下一个从1开始报数&#xff0c;再次报…

MySQL---使用索引优化、大批量插入数据优化

1. 使用索引优化 索引是数据库优化最常用也是最重要的手段之一, 通过索引通常可以帮助用户解决大多数的MySQL 的性能优化问题&#xff1a; create table tb_seller (sellerid varchar (100),name varchar (100),nickname varchar (50),password varchar (60),status varchar…