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

news2024/11/15 12:26:51

        由于工作需要在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;
}

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

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

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

相关文章

无敌保姆级华为认证 HCIE 笔试+实验考试指引,简直不要太详细

HCIE&#xff08;Huawei Certified ICT Expert&#xff0c;华为认证ICT专家&#xff09;是华为认证体系中最高级别的ICT技术认证&#xff0c;旨在打造高含金量的专家级认证&#xff0c;为技术融合背景下的ICT产业提供新的能力标准&#xff0c;以实现华为认证引领ICT行业技术认证…

网安面试设备篇幅:安全准入

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》http://mp.weixin.qq.com/s…

悦数 RAG 正式亮相 :从知识到应用的飞跃,只要几分钟

自 2023 年 8 月悦数与 LlamaIndex 联合发布 Graph RAG 以来&#xff0c;该技术就一直处于技术潮流的前沿。它通过提供更具上下文感知的能力和数据训练的方法&#xff0c;缓解了传统搜索增强技术的幻觉&#xff0c;确保所提供的回复不仅精确&#xff0c;而且有足够丰富的信息。…

科目三灯光模拟满分操作大全!建议收藏

今天一起备考一下科三的灯光模拟考试吧~它可以说是科目三中容易被扣分的操作了&#xff0c;考试开始一旦操作错误&#xff0c;就直接挂科了&#xff01;要想满分通过&#xff0c;这里为大家总结了下面这些窍门~ 操作步骤归类总结 01.开启近光灯 语音指令&#xff1a; 夜间与…

sql 4,创建表类型

1&#xff0c;整数类型(类型,占有空间,范围)标准sql:int / integer 4字节 无符号 0 - 2/32-1 有符号 -2 31 / 2 / 31 -1 smallint 2字节 无符号 0 - 2/16-1 有符号 -2 17 / 2 / 17 -1mysql方言:tinyint 1字节 无符号 0 - 2/8 -1 有符号 -2 7 / 2/7-1med…

Dnspy代码分析/反编译工具的简单使用:以骑砍2为例

前言&#xff1a; 如果我们需要做些反编译工作&#xff0c;改变游戏源码的时候&#xff0c;且该项目是由C#制作而成&#xff0c;便可以使用Dnspy工具。 1&#xff0c;安装 代码分析工具dnspy&#xff1a; ​ ​ 2&#xff0c;使用&#xff1a; 我们拿《骑砍2》举例&#x…

【IDEA】一键重启多个服务

点击Edit Configurations 点击加号&#xff0c;选择Compound 添加需要重启的服务&#xff0c;保存 选择配置好的Compound,一键重启 附加&#xff1a; 调整服务运行内存&#xff0c;Add VM options&#xff0c;填写合适的内存大小

日观芯设、亿方联创即将亮相IDAS 2024设计自动化产业峰会!

第二届设计自动化产业峰会IDAS 2024&#xff08;Intelligent Design Automation Summit 2024&#xff09;将于2024年9月23日-24日在上海张江科学会堂隆重举行。 上海日观芯设自动化有限公司、亿方联创科技有限公司将亮相峰会&#xff01;期待与您相聚&#xff0c;与全球行业领…

登上神坛!这本代码逐行解读注释的transformer宝藏书籍,哪怕是零编程基础也能学懂!

PART.01 transformer必看好书 不管你是现在要学transformer&#xff0c;还是以后要学&#xff0c;这本书都值得你花时间来认真学习&#xff01; 别看它封面平平无奇&#xff0c;这可还是除了《处理几乎所有机器学习问题》之外&#xff0c;我看到的第二本代码解读注释如此详细…

汽车小程序怎么做 汽车服务小程序系统开发制作方法

最近很多老板想要做一个自己公司的汽车服务小程序系统&#xff0c;但是不知道该怎么做&#xff0c;本次瀚林就为大家详细介绍一下汽车服务小程序系统的开发制作方法为大家做参考。 目前市面上的汽车服务有很多类型例如常见的&#xff1a;汽车维修、汽车用品、养护、汽车销售、新…

【动态规划 逆向】837. 新 21 点

本文涉及知识点 C动态规划 LeetCode837. 新 21 点 爱丽丝参与一个大致基于纸牌游戏 “21点” 规则的游戏&#xff0c;描述如下&#xff1a; 爱丽丝以 0 分开始&#xff0c;并在她的得分少于 k 分时抽取数字。 抽取时&#xff0c;她从 [1, maxPts] 的范围中随机获得一个整数作…

常用的人力资源管理系统的价格是多少?

企业的发展实质上就是企业人才的发展&#xff0c;做好人才管理是企业稳步发展的重要手段。许多企业意识到这点也开始建立自己的人力资源管理体系&#xff0c;但往往被自己遇到的复杂事务性问题所束缚&#xff0c;无法深入的去做人力资源管理。随着互联网的发展&#xff0c;人力…

3个方法对症下药:iphone备忘录删了怎么恢复?

iPhone的备忘录应用为我们的生活提供了很多便利&#xff0c;我们可以使用备忘录来记录生活中重要的事项&#xff0c;如密码&#xff0c;行程、会议内容等。但是&#xff0c;如果这些备忘录的内容不小心删除了怎么办呢&#xff1f;今天这篇文章就是来解决大家关于iPhone备忘录删…

MATLAB智能优化算法-学习笔记(1)——遗传算法求解0-1背包问题【过程+代码】

一、问题描述 (1)数学模型 (2)模型总结 目标函数:最大化背包中的总价值 Z。约束条件:确保背包中的物品总重量不超过容量 W。决策变量:每个物品是否放入背包,用0或1表示。这个数学模型是一个典型的0-1整数线性规划问题。由于其NP完全性,当问题规模较大时,求解此问题通…

某投资集团数据采集分析项目:实施全过程解析

在数字化转型的浪潮中&#xff0c;某投资集团以前瞻性的视野&#xff0c;积极响应市场变化&#xff0c;致力于构建高效、智能的数据管理体系。面对海量、复杂且分散的投资经营数据&#xff0c;该集团勇于探索&#xff0c;通过一系列精心策划与高效执行&#xff0c;携手亿信华辰…

盘点国内外好用的10款文件加密软件:企业文件防泄密的最佳选择

随着企业数据安全的重要性日益增加&#xff0c;文件加密软件成为保护敏感信息、防止数据泄漏的关键工具。无论是保护内部文档、财务记录还是客户数据&#xff0c;选择合适的加密软件都是企业信息安全的基础。以下是国内外好用的10款文件加密软件推荐&#xff0c;帮助企业提升数…

ZBrush入门使用介绍——12、折边

大家好&#xff0c;我是阿赵。   继续介绍ZBrush的功能。   如果拿一个立方体&#xff0c;进行CtrlD增加细分 会出现在边缘的线会被平滑的情况&#xff0c;这时候原来立方体的形状会发生一定的变化&#xff0c;不能保持原来的形状。 如果立方体真的只有8个顶点&#xff0…

关于怎么使用Charles

一、原理图二、Charles优点三、Charles组件介绍四、安装与使用1. 安装Charles2. Charles快速查找接口的四种技巧2.1 过滤1&#xff09;通过filter功能过滤2&#xff09;通过find功能过滤3&#xff09;通过Recording Settings功能过滤4&#xff09;通过Focus或Ignore聚焦或忽略指…

Stream DATA From openai GPT-3 API using php

题意&#xff1a;“使用 PHP 从 OpenAI GPT-3 API 流式传输数据” 问题背景&#xff1a; Im having trouble with the OpenAI API, Basically what Im trying to do is stream each data node that is streamed back from the openai API response and output each data node …

ModBus RTU、ModBus ASCII、ModBus TCP,它们有什么区别?

ModBus是一种通信协议&#xff0c;用于连接自动化设备&#xff08;如PLC、温度控制器等&#xff09;和计算机。它被广泛应用于工业控制系统&#xff0c;支持多种通信方式&#xff0c;包括ModBus RTU、ModBus ASCII和ModBus TCP 3种模式。 Modbus是一种应用层协议&#xff0c;它…