封装CUDA为动态链接库+Qt调用

2024-08-28 07:28

本文主要是介绍封装CUDA为动态链接库+Qt调用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        由于工作需要在Qt中调用CUDA做并行计算,加速算法实现时间,发现有两种方法可以在Qt中调用CUDA代码。

        第一种是在项目中创建CUDA的cu文件,编写CUDA的核函数给其他的QT代码调用,Qt的代码正常编译,CUDA代码使用nvcc编译器编译。这种方法只要配置一下pro文件就可以了,适合CUDA代码比较少的项目,只需要几个核函数调用CUDA进行一下加速运算,具体方法可以看我的另一篇博客:QT+CUDA 同时编译Qt和CUDA代码文章浏览阅读1k次,点赞29次,收藏13次。工作需要把cuda的代码移植到QT中,和Qt项目一起编译,这里记录一下。_qt win .exe加cudahttps://blog.csdn.net/Sakuya__/article/details/141264954?spm=1001.2014.3001.5502

        第二种是把CUDA代码编译成动态链接库,Qt程序直接调用动态链接库中的接口,就像调用其他C++库一样。这种方法适合CUDA代码比较多的项目,比如用CUDA代码实现了一整个深度学习算法,有几十上百个CUDA文件,并且互相包含引用。这时候第一种方法在编译时就可能会有问题,并且也不好进行管理。

        这里记录一下把CUDA代码编译成动态链接库的过程。


一、编译CUDA的动态链接库

1.创建动态链接库工程

        我使用的是VS2022,建立工程,选择具有导出项的动态链接库

        项目名称是CudaDynamicCores,这是我创建完成后的项目目录结构 

        点击项目名称,右键 —> 生成依赖项 —> 生成自定义,勾选上你要用的CUDA版本,然后点击确定

2.添加CUDA文件

        点击项目名称,右键选择添加—>新建项

        选择添加CUDA文件,命名为Test.cu: 

        然后同样的再添加CUDA头文件,命名为Test.cuh 

        创建完后,右键一下Test.cu文件 —>属性,确认文件类型选择的是 CUDA C/C++。我用的VS2022,在第一步中把生成自定义项改为使用CUDA后,创建的cuda文件自动就会选择为 CUDA C/C++,如果不是的话自己点击下拉框选择一下

 3.写入CUDA代码

        Test.cuh 

#ifndef _Test_H
#define _Test_H#include "CudaDynamicCores.h"
#include "cuda_runtime.h"  
#include "device_launch_parameters.h"CUDADYNAMICCORES_API int CUDA_VectorAdd(int c[], int a[], int b[], int size);CUDADYNAMICCORES_API void CUDA_ShowDeviceProp(void);#endif	// _Test_H

        Test.cu

#include "Test.cuh"
#include <iostream>// 向量相加  
int CUDA_VectorAdd(int c[], int a[], int b[], int size)
{int result = -1;int* dev_a = 0;int* dev_b = 0;int* dev_c = 0;cudaError_t cudaStatus;// 选择用于运行的GPU  cudaStatus = cudaSetDevice(0);if (cudaStatus != cudaSuccess) {result = 1;goto Error;}// 在GPU中为变量dev_a、dev_b、dev_c分配内存空间.cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));if (cudaStatus != cudaSuccess) {result = 2;goto Error;}cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));if (cudaStatus != cudaSuccess) {result = 3;goto Error;}cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));if (cudaStatus != cudaSuccess) {result = 4;goto Error;}// 从主机内存复制数据到GPU内存中.  cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);if (cudaStatus != cudaSuccess) {result = 5;goto Error;}cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);if (cudaStatus != cudaSuccess) {result = 6;goto Error;}// 启动GPU内核函数  addKernel << <1, size >> > (dev_c, dev_a, dev_b);// 采用cudaDeviceSynchronize等待GPU内核函数执行完成并且返回遇到的任何错误信息  cudaStatus = cudaDeviceSynchronize();if (cudaStatus != cudaSuccess) {result = 7;goto Error;}// 从GPU内存中复制数据到主机内存中  cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);if (cudaStatus != cudaSuccess) {result = 8;goto Error;}result = 0;// 重置CUDA设备,在退出之前必须调用cudaDeviceReset  cudaStatus = cudaDeviceReset();if (cudaStatus != cudaSuccess) {return 9;}
Error://释放设备中变量所占内存  cudaFree(dev_c);cudaFree(dev_a);cudaFree(dev_b);return result;
}//显示设备信息
void CUDA_ShowDeviceProp(void)
{int i, count;cudaDeviceProp prop;cudaError_t cudaStatus = cudaGetDeviceCount(&count);if (cudaStatus == cudaSuccess) {std::cout << "共有设备数目:" << count << std::endl;if (count > 0){for (i = 0; i < count; i++){cudaGetDeviceProperties(&prop, i);//获取设备的属性信息std::cout << "第" << i + 1 << "个设备信息:" << std::endl;std::cout << "设备名称:" << prop.name << std::endl;std::cout << "总内存:" << prop.totalGlobalMem / 1048576 << "M" << std::endl;std::cout << "常量内存:" << prop.totalConstMem << "字节" << std::endl;std::cout << "设备中处理器数目:" << prop.multiProcessorCount << "个" << std::endl;std::cout << "每个线程块最多包含线程数目:" << prop.maxThreadsPerBlock << "个" << std::endl;std::cout << "一个线程格中可包含的线程块数目:I=" << prop.maxGridSize[0]<< " J=" << prop.maxGridSize[1] << " K=" << prop.maxGridSize[2] << std::endl;std::cout << "多维线程块中可以包含的最大线程数目:I=" << prop.maxThreadsDim[0]<< " J=" << prop.maxThreadsDim[1] << " K=" << prop.maxThreadsDim[2] << std::endl;}}}else{std::cout << "没有获取到设备信息!请检查计算机是否具有支持CUDA的显卡设备以及CUDA驱动程序版本是否需要更新!" << std::endl;}
}

        CudaDynamicCores.h,前面自动生成的导出类、函数和变量的示例可以删也可以不删,如果删的话,cpp中的实现也一起删掉。在之后加上你自己要导出的函数声明,所有要导出的函数都要用上面宏定义的 CUDADYNAMICCORES_API 修饰,并且用extern "C" 加大括号 { } 括起来。

#ifdef CUDADYNAMICCORES_EXPORTS
#define CUDADYNAMICCORES_API __declspec(dllexport)
#else
#define CUDADYNAMICCORES_API __declspec(dllimport)
#endif// 此类是从 dll 导出的
class CUDADYNAMICCORES_API CCudaDynamicCores {
public:CCudaDynamicCores(void);// TODO: 在此处添加方法。
};extern CUDADYNAMICCORES_API int nCudaDynamicCores;CUDADYNAMICCORES_API int fnCudaDynamicCores(void);extern "C" 
{/********************************************************************************函数:		CUDA_ShowDeviceProp参数:		/返回值:		/说明:		读取设备显卡属性,并打印出来*********************************************************************************/CUDADYNAMICCORES_API void CUDA_ShowDeviceProp(void);/********************************************************************************函数:		CUDA_VectorAdd参数:		[out] c			向量a和向量b相加的结果[in] a			向量相加计算的第一个向量[in] b          向量相加计算的第二个向量[in] size       向量的大小返回值:		计算结果成功和失败的错误码说明:		计算两个向量相加*********************************************************************************/CUDADYNAMICCORES_API int CUDA_VectorAdd(int c[], int a[], int b[], int size);
}

4.添加链接器的附加依赖项

        点击项目名称,右键选择属性,在链接器 —> 输入 —> 附加依赖项,查看其中有没有cudart.lib,VS2022在设置自定义生成为CUDA后这里会自动有集成值cudart.lib,如果没有的话自己手动添加一下,添加后点击应用和确定。

 5.生成

        点击项目名称,右键选择生成,等待编译完成。

        然后就可以在项目目录下看到一个x64文件夹,里面有Debug或者Release文件夹,取决于你刚才生成之前,解决方案项目配置的是Debug还是Release,文件夹下都有一个lib文件和一个dll文件。你需要看调试信息的话就用Debug,不需要的话就编译Release,我这里用的是Release。


 二、Qt中使用编译好的CUDA动态链接库

1.添加头文件和lib文件

        这里和Qt调用其他C++的动态链接库是一样的,首先创建一个 Qt 的项目。在项目路径下新建一个 include 文件夹,然后把前面的 CudaDynamicCores.h 文件放在里面,再新建一个 lib 文件夹,把上面的 CudaDynamicCores.lib 文件放在里面,最后把 CudaDynamicCores.dll 文件放在你程序运行的路径下,也就是和你程序生成的 exe 文件在同一个目录下。

         在 pro 文件中加入下面这两行,引入头文件和lib文件

2.测试 

        这样就可以使用dll中导出的接口函数了,使用下面的代码测试一下,mainwindow.h

#include "mainwindow.h"
#include "ui_mainwindow.h"#include "CudaDynamicCores.h"#include <iostream>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 显示设备信息CUDA_ShowDeviceProp();// 两个向量相加int a[5] = {1,2,3,4,5};int b[5] = {3,4,2,3,5};int c[5];CUDA_VectorAdd(c,a,b,5);std::cout << a[0] << ' ' << a[1] << ' ' << a[2] << ' ' << a[3] << ' ' << a[4] << std::endl;std::cout << b[0] << ' ' << b[1] << ' ' << b[2] << ' ' << b[3] << ' ' << b[4] << std::endl;std::cout << c[0] << ' ' << c[1] << ' ' << c[2] << ' ' << c[3] << ' ' << c[4] << std::endl;
}MainWindow::~MainWindow()
{delete ui;
}

         可以看到打印出来的结果,大功告成!

这篇关于封装CUDA为动态链接库+Qt调用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1114115

相关文章

SpringBoot中封装Cors自动配置方式

《SpringBoot中封装Cors自动配置方式》:本文主要介绍SpringBoot中封装Cors自动配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot封装Cors自动配置背景实现步骤1. 创建 GlobalCorsProperties

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

SpringCloud之LoadBalancer负载均衡服务调用过程

《SpringCloud之LoadBalancer负载均衡服务调用过程》:本文主要介绍SpringCloud之LoadBalancer负载均衡服务调用过程,具有很好的参考价值,希望对大家有所帮助,... 目录前言一、LoadBalancer是什么?二、使用步骤1、启动consul2、客户端加入依赖3、以服务

Vue 调用摄像头扫描条码功能实现代码

《Vue调用摄像头扫描条码功能实现代码》本文介绍了如何使用Vue.js和jsQR库来实现调用摄像头并扫描条码的功能,通过安装依赖、获取摄像头视频流、解析条码等步骤,实现了从开始扫描到停止扫描的完整流... 目录实现步骤:代码实现1. 安装依赖2. vue 页面代码功能说明注意事项以下是一个基于 Vue.js

Qt 中 isHidden 和 isVisible 的区别与使用小结

《Qt中isHidden和isVisible的区别与使用小结》Qt中的isHidden()和isVisible()方法都用于查询组件显示或隐藏状态,然而,它们有很大的区别,了解它们对于正确操... 目录1. 基础概念2. 区别清见3. 实际案例4. 注意事项5. 总结1. 基础概念Qt 中的 isHidd

Java导入、导出excel用法步骤保姆级教程(附封装好的工具类)

《Java导入、导出excel用法步骤保姆级教程(附封装好的工具类)》:本文主要介绍Java导入、导出excel的相关资料,讲解了使用Java和ApachePOI库将数据导出为Excel文件,包括... 目录前言一、引入Apache POI依赖二、用法&步骤2.1 创建Excel的元素2.3 样式和字体2.

QT移植到RK3568开发板的方法步骤

《QT移植到RK3568开发板的方法步骤》本文主要介绍了QT移植到RK3568开发板的方法步骤,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录前言一、获取SDK1. 安装依赖2. 获取SDK资源包3. SDK工程目录介绍4. 获取补丁包二

Qt把文件夹从A移动到B的实现示例

《Qt把文件夹从A移动到B的实现示例》本文主要介绍了Qt把文件夹从A移动到B的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录如何移动一个文件? 如何移动文件夹(包含里面的全部内容):如何删除文件夹:QT 文件复制,移动(

JAVA封装多线程实现的方式及原理

《JAVA封装多线程实现的方式及原理》:本文主要介绍Java中封装多线程的原理和常见方式,通过封装可以简化多线程的使用,提高安全性,并增强代码的可维护性和可扩展性,需要的朋友可以参考下... 目录前言一、封装的目标二、常见的封装方式及原理总结前言在 Java 中,封装多线程的原理主要围绕着将多线程相关的操

讯飞webapi语音识别接口调用示例代码(python)

《讯飞webapi语音识别接口调用示例代码(python)》:本文主要介绍如何使用Python3调用讯飞WebAPI语音识别接口,重点解决了在处理语音识别结果时判断是否为最后一帧的问题,通过运行代... 目录前言一、环境二、引入库三、代码实例四、运行结果五、总结前言基于python3 讯飞webAPI语音