CUDA-事件计时方法cudaEventElapsedTime

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

实现原理

       事件计时方法是CUDA编程中一种用于测量GPU内核执行时间的高效手段。其基本步骤包括:

  1. 创建事件: 使用 cudaEvent_t 类型的变量来创建事件,通常有两个事件:一个用于记录开始时间,另一个用于记录结束时间。

  2. 记录开始事件: 在调用内核函数之前,使用 cudaEventRecord(start, 0) 记录开始时间。这个事件标记了内核执行的起始点。

  3. 调用GPU内核: 执行CUDA内核函数,例如使用kernel<<<gridSize, blockSize>>>(...)的方式。

  4. 记录结束事件: 内核执行完毕后,使用 cudaEventRecord(end, 0) 记录结束时间。这个事件标记了内核执行的结束点。

  5. 同步事件: 使用 cudaEventSynchronize(end) 确保CPU等待GPU完成内核执行,以确保在计算时间时所有操作都已结束。

  6. 计算耗时: 使用 cudaEventElapsedTime(&spendtime, start, end) 计算两个事件之间的时间差,将结果存储在指定变量中,通常以毫秒为单位。

  7. 输出结果: 将测量的时间输出,帮助分析和优化GPU内核的性能。

       该方法相比较一般的计时方法,如clock_t等,更适合在GPU场景计时,它有如下好处:

  1. 高精度: CUDA事件提供了高精度的计时,能够精确到毫秒级别,适合测量GPU内核的性能。

  2. 非阻塞: 使用事件记录的计时方式不会阻塞主机线程,允许CPU与GPU并行工作。这种非阻塞特性使得你可以在GPU执行的同时进行其他计算或处理。

  3. 自动同步: 通过调用 cudaEventSynchronize,你可以确保在计算时间时GPU的所有操作都已完成,避免了时间测量的不准确性。

  4. 易于使用: CUDA事件的使用相对简单,易于集成到现有的CUDA程序中,不需要复杂的编程或额外的库。

  5. 适用于多次测量: 可以很方便地进行多次测量,帮助你比较不同算法或参数设置的性能,进行性能调优。

  6. 支持并行计算: 你可以在多个内核调用中使用事件,监测不同内核的执行时间,进而优化内核的调度和资源利用。

  7. 便于调试和分析: 通过分析内核的执行时间,你可以识别瓶颈,帮助你更好地理解GPU的性能,并进行相应的优化。

       本文将通过一个之前分享过的中值滤波CUDA代码,进行事件计时方法的应用展示。

C++测试代码

Filter.h

#pragma once
#include <cuda_runtime.h>
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include <device_launch_parameters.h>using namespace cv;
using namespace std;#define TILE_WIDTH 16// 预准备过程
void warmupCUDA();// 中值滤波-CPU
cv::Mat filterMedian_CPU(cv::Mat input, int FilterWindowSize);// 中值滤波-GPU
cv::Mat filterMedian_GPU(cv::Mat input, int FilterWindowSize);

Filter.cu

#include "Filter.h"// 预准备过程
void warmupCUDA()
{float* dummy_data;cudaMalloc((void**)&dummy_data, sizeof(float));cudaFree(dummy_data);
}// 中值滤波-CPU
cv::Mat filterMedian_CPU(cv::Mat input, int FilterWindowSize)
{int row = input.rows;int col = input.cols;// 预设输出cv::Mat output = input.clone();// 中值滤波int r = FilterWindowSize / 2;
#pragma omp parallel forfor (int i = 0; i < row; ++i){vector<uchar> datas;for (int j = 0; j < col; ++j){// 卷积窗口边界限制,防止越界int ms = ((i - r) > 0) ? (i - r) : 0;int me = ((i + r) < (row - 1)) ? (i + r) : (row - 1);int ns = ((j - r) > 0) ? (j - r) : 0;int ne = ((j + r) < (col - 1)) ? (j + r) : (col - 1);// 求窗口内有效数据的中值datas.clear();for (int m = ms; m <= me; ++m){for (int n = ns; n <= ne; ++n){datas.push_back(input.at<uchar>(m, n));}}sort(datas.begin(), datas.end());output.at<uchar>(i, j) = datas[datas.size() / 2];}}return output;
}
// 中值滤波核函数
__global__ void medianFilter_CUDA(uchar* inputImage, uchar* outputImage, int width, int height, int windowSize)
{int row = blockIdx.y * blockDim.y + threadIdx.y;int col = blockIdx.x * blockDim.x + threadIdx.x;if (row < height && col < width){// 参数预设uchar datas[100];int r = windowSize / 2;int ms = max(row - r, 0);int me = min(row + r, height - 1);int ns = max(col - r, 0);int ne = min(col + r, width - 1);// 赋值int count = 0;for (int m = ms; m <= me; ++m){for (int n = ns; n <= ne; ++n){datas[count++] = inputImage[m * width + n];}}// 选择排序for (int i = 0; i < count - 1; i++){int minIndex = i;for (int j = i + 1; j < count; j++){if (datas[j] < datas[minIndex]){minIndex = j;}}uchar temp = datas[i];datas[i] = datas[minIndex];datas[minIndex] = temp;}outputImage[row * width + col] = datas[count / 2];}
}
// 中值滤波-GPU
cv::Mat filterMedian_GPU(cv::Mat input, int FilterWindowSize)
{int row = input.rows;int col = input.cols;// 定义计时器float spendtime = 0.0f;cudaEvent_t start, end;cudaEventCreate(&start);cudaEventCreate(&end);// 分配GPU内存	uchar* d_inputImage, *d_outputImage;cudaMalloc(&d_inputImage, row * col * sizeof(uchar));cudaMalloc(&d_outputImage, row * col * sizeof(uchar));// 将输入图像数据从主机内存复制到GPU内存cudaMemcpy(d_inputImage, input.data, row * col * sizeof(uchar), cudaMemcpyHostToDevice);// 计算块和线程的大小dim3 blockSize(TILE_WIDTH, TILE_WIDTH);dim3 gridSize((col + blockSize.x - 1) / blockSize.x, (row + blockSize.y - 1) / blockSize.y);// 调用CUDA内核cudaEventRecord(start, 0);medianFilter_CUDA << <gridSize, blockSize >> > (d_inputImage, d_outputImage, col, row, FilterWindowSize);cudaEventRecord(end, 0);cudaEventSynchronize(end);cudaEventElapsedTime(&spendtime, start, end);cout << "medianFilter_CUDA spend time:" << spendtime << endl;// 将处理后的图像数据从GPU内存复制回主机内存cv::Mat output(row, col, CV_8UC1);cudaMemcpy(output.data, d_outputImage, row * col * sizeof(uchar), cudaMemcpyDeviceToHost);// 清理GPU内存cudaFree(d_inputImage);cudaFree(d_outputImage);return output;
}

main.cpp

#include "Filter.h"void main()
{// 预准备warmupCUDA();cout << "medianFilter test begin." << endl;// 加载cv::Mat src = imread("test pic/test1.jpg", 0);int winSize = 9;cout << "filterWindowSize:" << winSize << endl;cout << "size: " << src.cols << " * " << src.rows << endl;// CPU版本clock_t s1, e1;s1 = clock();cv::Mat output1 = filterMedian_CPU(src, winSize);e1 = clock();cout << "CPU time:" << double(e1 - s1) / 1000 << endl;// GPU版本clock_t s2, e2;s2 = clock();cv::Mat output2 = filterMedian_GPU(src, winSize);e2 = clock();cout << "GPU time:" << double(e2 - s2) / 1000 << endl;// 检查int row = src.rows;int col = src.cols;bool flag = true;for (int i = 0; i < row; ++i){for (int j = 0; j < col; ++j){if (output1.at<uchar>(i, j) != output2.at<uchar>(i, j)){cout << "i:" << i << " j:" << j << endl;flag = false;break;}}if (!flag){break;}}if (flag){cout << "ok!" << endl;}else{cout << "error!" << endl;}// 查看输出cv::Mat test1 = output1.clone();cv::Mat test2 = output2.clone();cout << "medianFilter test end." << endl;}

测试效果 

       事件计时方法有精确度更高的计时效果,便于在调试和优化代码时使用。

       如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

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

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

相关文章

Python模拟鼠标轨迹[Python]

一.鼠标轨迹模拟简介 传统的鼠标轨迹模拟依赖于简单的数学模型&#xff0c;如直线或曲线路径。然而&#xff0c;这种方法难以捕捉到人类操作的复杂性和多样性。AI大模型的出现&#xff0c;能够通过深度学习技术&#xff0c;学习并模拟更自然的鼠标移动行为。 二.鼠标轨迹算法实…

echarts柱图

样式如下 关键代码 // html部分<div class"echarts-container" :id"echartDiv id"></div>// js部分 data() {return {myChart: null,xAxisData: [云南, 浙江, 山东, 广东, 四川, 河南, 广西, 河南, 广西],yAxisData: [{value: 230,num: {r…

超实用线程调度方法!

文章目录 前言一、实现思路二、实现实例三、实现效果 前言 你有想过这样一个问题吗&#xff1f;线程之间是如何调度的&#xff1f; 更具体点说是这样&#xff0c;有两个线程&#xff1a;线程A和线程B&#xff0c;线程B要先于线程A运行&#xff0c;或者是线程B运行了多次之后在…

2024年华为杯中国研究生数学建模竞赛F题保姆级教程思路分析

2024年中国研究生数学建模竞赛F题保姆级教程思路分析 F题题目&#xff1a;X射线脉冲星光子到达时间建模 本题目围绕脉冲星导航与X射线光子到达时间建模展开。脉冲星由于其自转稳定性和规律性&#xff0c;被认为是宇宙中精确的时钟&#xff0c;并可以用作航天器的定位和导航基…

Ubuntu 22.04 源码下载、编译

Kernel/BuildYourOwnKernel - Ubuntu Wikihttps://wiki.ubuntu.com/Kernel/BuildYourOwnKernel 一、查询当前系统内核版本 rootubuntu22:~# uname -r 5.15.0-118-generic 二、查询本地软件包数据库中的内核源码信息 rootubuntu22:~# apt search linux-source Sorting... Do…

使用Maven创建一个Java项目并在repository中使用

JDK环境&#xff1a;1.8.0_371 Maven环境 &#xff1a;Apache Maven 3.6.3 配置完成jdk和mvn后&#xff0c;进入到指定文件夹下执行如下语句&#xff1a; mvn archetype:generate -DgroupIdtop.chengrongyu -DartifactIdCyberSpace -DarchetypeArtifactIdmaven-archetype-quic…

(20)Shell脚本的书写

linux中的shell脚本&#xff0c;其实是系统中真正的命令。Shell语言写的程序不需编译.定义各种参数和变量、使用条件命令、控制结构以及其他高级特性。 一、shell脚本基本元素 1.1变量 定义&#xff1a;定义一个名称&#xff0c;将参数赋予给这个名称。就叫变量。变量名可以…

C++【类和对象】(一)

文章目录 前言1.类的定义1.1类定义格式1.2 访问限定符1.3 类域 2. 实例化2.1 实例化的概念2.2 对象大小 3.this指针结语 前言 在前文我们讲解了C基础语法知识。本文将会讲解C的类和对象。 1.类的定义 1.1类定义格式 class name {}&#xff1b;class为定义类的关键字&#x…

Linux进阶命令-rsync

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 经过上一章Linux日志的讲解&#xff0c;我们对Linux系统自带的日志服务已经有了一些了解。我们接下来将讲解一些进阶命令&am…

erlang学习:Linux常用命令2

目录操作命令 对目录进行基本操作 相关cd切换目录之类的就直接省去了&#xff0c;以下操作中都会用到 查看当前目录下的所有目录和文件 ls 列表查看当前目录下的所有目录和文件&#xff08;列表查看&#xff0c;显示更多信息&#xff09; ls -l 或 ll 在当前目录下创建一个…

高性能并发计数器的比较

参考文档&#xff1a;https://baijiahao.baidu.com/s?id1742540809477784106&wfrspider&forpc 一、常用的并发计数方法 1、synchronized synchronized早期是一个重量级锁&#xff0c;因为线程竞争锁会引起操作系统用户态和内核态切换&#xff0c;浪费资源&#xff…

Element Plus中button按钮相关大全

一、基本用法 使用 type、plain、round 和 circle 来定义按钮的样式。 样式代码如下&#xff1a; <template><div class"mb-4"><el-button>Default</el-button><el-button type"primary">Primary</el-button><el…

C语言常见字符串函数模拟实现一

strlen模拟实现 重点&#xff1a;1.字符串已经\0作为结束标志&#xff0c;strlen返回的是字符串\0前面出现的字符个数&#xff08;不包含\0&#xff09; 2.参数指向的字符串必须要以\0结束。 3.注意函数的返回值是size_t&#xff0c;是无符号的&#xff0c;加减是无法对比的。…

卡西欧相机SD卡格式化后数据恢复指南

在数字摄影时代&#xff0c;卡西欧相机以其卓越的性能和便携性成为了众多摄影爱好者的首选。然而&#xff0c;随着拍摄量的增加&#xff0c;SD卡中的数据管理变得尤为重要。不幸的是&#xff0c;有时我们可能会因为操作失误或系统故障而将SD卡格式化&#xff0c;导致珍贵的照片…

数据类型转换中存在的问题分析

隐式类型转换&#xff08;implicit type conversion&#xff09; 隐式类型转换&#xff08;implicit type conversion&#xff09;包括整型提升&#xff08;integer promotion&#xff09;和标准算数转换&#xff08;usual arithmetic conversions&#xff09; 遵循较大范围优…

堡垒机(Bastion Host)概述

Bastion Host 堡垒机 一、什么是堡垒机&#xff1f; A bastion host is a computer specially designed to mitigate cyberattacks and manage access rights to an internal network. 堡垒机Bastion Host是一种专门设计用于缓解网络攻击并管理内部网络访问权限的计算机。 在…

肖扬新书《微权力下的项目管理》读书笔记2

一个核心思想&#xff1a;“借力” 合格的项目经理是不热衷于培养人的。项目经理的工作场景和职能经理的工作场景往往有很 大不同。职能经理的工作方式通常适用于常态化工作&#xff0c;要有足够的时间去培养人&#xff0c;先把人培 养起来&#xff0c;然后再干事&#xff0c;可…

加油卡APP定制搭建,让加油更便捷!

在汽车时代中&#xff0c;汽车的数量不断增加&#xff0c;加油已经成为了大众生活中不可缺少的一部分。同时&#xff0c;加油卡的出现也为大众的汽车加油提供了更多的优惠方式&#xff0c;为大众节省经济开支&#xff0c;为车主带来便利&#xff1b;同时加油卡的发展也提高了加…

2024年华为杯研赛(E题)数学建模竞赛解题思路|完整代码论文集合

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…

如何远程访问局域网内的电脑?局域网内远程桌面怎么实现?揭秘4种干货技巧

想象一下&#xff0c;你正在办公室A&#xff0c;而你想访问办公室B里的某台电脑&#xff0c;却不想起身到另一楼层甚至是另一个房间。 如何不动身就能控制局域网内的另一台电脑呢&#xff1f; 这并不是科幻&#xff0c;而是完全可以通过远程桌面技术来实现。 今天&#xff0…