C++开发基础之初探CUDA计算环境搭建

news2024/11/26 21:28:21

一、前言

项目中有使用到CUDA计算的相关内容。但是在早期CUDA计算环境搭建的过程中,并不是非常顺利,编写此篇文章记录下。对于刚刚开始研究的你可能会有一定的帮助。

二、环境搭建

搭建 CUDA 计算环境涉及到几个关键步骤,包括安装适当的 CUDA 驱动程序和工具包、设置开发环境和编译器,以及编写和运行 CUDA 程序。感谢Davis lee详细的介绍 CUDA安装及环境配置——最新详细版以下是一个基本的搭建过程:

步骤 1:检查硬件兼容性

首先,确保的计算机上的 GPU 支持 CUDA。可以在 NVIDIA 的官方网站上查找 GPU 的型号以确定其是否支持 CUDA。
在这里插入图片描述

步骤 2:安装 CUDA 驱动程序

访问 NVIDIA 的官方网站,下载并安装与你的 GPU 兼容的最新 CUDA 驱动程序。安装过程中,根据向导提示进行操作。
在这里插入图片描述

步骤 3:安装 CUDA 工具包

下载并安装与你的 CUDA 驱动程序版本相匹配的 CUDA 工具包。CUDA 工具包中包含了编译器、库和工具,用于开发和运行 CUDA 程序。

https://developer.nvidia.com/cuda-downloads

步骤 4:安装适当的开发环境

你可以使用多种开发环境来编写 CUDA 程序,如 NVIDIA 提供的 CUDA Toolkit 中自带的 nvcc 编译器,或者集成了 CUDA 开发支持的 IDE,如 Visual Studio(需要安装适当的 CUDA 插件)或 JetBrains 的 CLion 等。

步骤 5:设置环境变量

在你的操作系统中设置 CUDA 相关的环境变量,包括 PATHCUDA_PATH 等,以便系统可以找到 CUDA 工具和库。

步骤 6:编写和编译 CUDA 程序

使用你选择的开发环境编写 CUDA 程序,并使用 CUDA 编译器(如 nvcc)编译程序。确保您的程序正确地链接了 CUDA 库,并且编译选项正确设置。

步骤 7:运行 CUDA 程序

将编译生成的可执行文件部署到你的计算机上,并在 CUDA 支持的环境中运行程序。你可能需要在程序运行时指定相应的 GPU 设备。

总之就是,在搭建时适配自己的电脑配置要求。做到最新即可。

三、实践编码过程

新增一个空的解决方案,我们命名为VectorProject.sln。

3.1 使用CUDA编写动态库

1、新增动态链接库 ,命名为VectorLibrary;

2、配置CUDA编译环境:
生成依赖项–>生成自定义
在这里插入图片描述
选择CUDA 12.3(targets,props)
在这里插入图片描述
这里如果不配置CUDA编译环境,会报错,无法正常编译通过的。配置完成后,可以查看项目的属性页。能看到CUDA C/C++配置部分
在这里插入图片描述
3、编写接口代码

这里主要定义两个向量的加法运算。

#pragma once
#include "pch.h"
#include <Windows.h>

#ifdef VECTOR_LIBRARY_EXPORTS
#define VECTOR_LIBRARY_API __declspec(dllexport)
#else
#define VECTOR_LIBRARY_API __declspec(dllimport)
#endif

BOOL VECTOR_LIBRARY_API vectorAddCPU(const float* A, const float* B, float* C, int N);
BOOL VECTOR_LIBRARY_API vectorAddGPU(const float* A, const float* B, float* C, int N);

4、编写CPU方法实现过程

// 封装CUDA函数的C++代码
#include "pch.h"
#include "vectorAdd.h"

// CPU上的向量加法函数
 BOOL vectorAddCPU(const float* A, const float* B, float* C, int N)
{
	for (int i = 0; i < N; ++i) {
		C[i] = A[i] + B[i];
	}
	return true;
}

5、编写GPU方法实现过程
新增一个核函数声明文件 kernelVectorAdd.cuh

#include <iostream>
void kernelVectorAdd(const float* A, const float* B, float* C, int N);

编写核函数实现

#include <cuda_runtime.h>
#include <iostream>
#include <vector>
#include <device_launch_parameters.h>
#include "kernelVectorAdd.cuh"

// CUDA核函数:在GPU上执行的向量加法
__global__ void kernelVectorAddImp(const float* A, const float* B, float* C, int N) {
    int i = blockDim.x * blockIdx.x + threadIdx.x;
    if (i < N) {
        C[i] = A[i] + B[i];
    }
}

void kernelVectorAdd(const float* A, const float* B, float* C, int N) {
    float* d_A, * d_B, * d_C;
    size_t size = N * sizeof(float);

    // 分配设备内存
    cudaMalloc(&d_A, size);
    cudaMalloc(&d_B, size);
    cudaMalloc(&d_C, size);

    // 将数据从主机内存复制到设备内存
    cudaMemcpy(d_A, A, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_B, B, size, cudaMemcpyHostToDevice);

    // 执行CUDA核函数
    int threadsPerBlock = 256;
    int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;
    kernelVectorAddImp <<<blocksPerGrid, threadsPerBlock >>> (d_A, d_B, d_C, N);

    // 等待所有CUDA核函数执行完毕
    cudaDeviceSynchronize();

    // 将结果从设备内存复制回主机内存
    cudaMemcpy(C, d_C, size, cudaMemcpyDeviceToHost);

    // 释放设备内存
    cudaFree(d_A);
    cudaFree(d_B);
    cudaFree(d_C);
}

在编写核函数调用的C++代码

// 封装CUDA函数的C++代码
#include "pch.h"
#include "kernelVectorAdd.cuh"
#include "vectorAdd.h"

BOOL vectorAddGPU(const float* A, const float* B, float* C, int N) {
	kernelVectorAdd(A, B, C, N);
	return true;
}

这里需要把核函数进行封装,否则会报错,相关解决办法可见 关于CUDA C 项目中“ error C2059: 语法错误:“<” ”问题的解决方法.

6、现在我们编译下项目
在这里插入图片描述

3.2 编写C++控制台程序

1、新增C++控制台程序,VectorCpp
在这里插入图片描述
2、配置VectorLibrary.dll的引用
打开属性页,找到C/C++目录,附加包含目录添加配置

$(SolutionDir)VectorLibrary;

在这里插入图片描述
链接器–>常规–>附加库目录

$(TargetDir);%(AdditionalLibraryDirectories)

在这里插入图片描述
链接器–>输入–>附加依赖项

VectorLibrary.lib;%(AdditionalDependencies)

在这里插入图片描述
配置完成这些,就可以对VectorLibrary.dll正常引用了。

2、编写调用代码

// CudaWrapper.cpp
#include "pch.h"
#include <iostream>
#include <random>
#include <chrono>
#include "vectorAdd.h"

// 生成随机数并填充到数组
void generateRandomNumbers(float* array, int N) {
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    std::default_random_engine generator(seed);
    std::uniform_real_distribution<float> distribution(0.0, 1.0); // 范围从0到1之间

    for (int i = 0; i < N; ++i) {
        array[i] = distribution(generator);
    }
}

int main()
{
    int N = 1000000;
    float* pA = new float[N];
    float* pB = new float[N];
    float* pC_GPU = new float[N];
    float* pC_CPU = new float[N];

    // 为pA和pB生成随机数
    generateRandomNumbers(pA, N);
    generateRandomNumbers(pB, N);

    // 测量 CPU 端向量加法函数的执行时间
    auto start_cpu = std::chrono::high_resolution_clock::now();
    vectorAddCPU(pA, pB, pC_CPU, N);
    auto end_cpu = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsed_cpu = end_cpu - start_cpu;
    std::cout << "CPU 端向量加法函数的执行时间: " << elapsed_cpu.count() << " 秒" << std::endl;

    // 测量 GPU 端向量加法函数的执行时间
    auto start_gpu = std::chrono::high_resolution_clock::now();
    vectorAddGPU(pA, pB, pC_GPU, N);
    auto end_gpu = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsed_gpu = end_gpu - start_gpu;
    std::cout << "GPU 端向量加法函数的执行时间: " << elapsed_gpu.count() << " 秒" << std::endl;

    // 验证结果
    for (int i = 0; i < N; ++i)
    {
        if ((pC_CPU[i] - pC_GPU[i]) > 1e-5)
        {
            std::cout << "结果不匹配" << std::endl;
            break;
        }
    }
    std::cout << "结果匹配" << std::endl;

    // 记得释放内存
    delete[] pA;
    delete[] pB;
    delete[] pC_CPU;
    delete[] pC_GPU;

    return 0;
}

3、运行程序
完整的项目结构
在这里插入图片描述
运行结果
在这里插入图片描述
在这个示例中,成功运行得出结果。这个时候,你会发现为什么CPU的计算结果远远高于GPU。那是因为:

  • 数据传输开销:在CUDA中,数据必须在主机(CPU)和设备(GPU)之间进行传输。在每次调用CUDA函数之前和之后,都需要将数据从主机内存复制到设备内存,然后将结果从设备内存复制回主机内存。这些数据传输的开销会降低CUDA的性能,特别是当数据量较大时。
  • Kernel调用开销:在CUDA中,每次调用核函数都需要一定的开销,包括启动核函数、将数据传递给核函数、核函数在GPU上执行等。如果向量大小较小,核函数的启动开销可能会占据相当大的比例,从而降低CUDA的性能。
  • 并行化效率不佳:在某些情况下,CUDA核函数可能无法充分利用GPU的并行计算能力。这可能是因为向量大小太小,无法充分填充GPU的计算单元,或者核函数的计算密度不够高,无法实现最大的并行化效率。
  • 内存访问模式:CUDA核函数的性能受到内存访问模式的影响。如果核函数中的内存访问模式不利于GPU的缓存和内存访问优化,性能可能会受到影响。

究其根本原因就是,这个算法太简单了,CPU就可以搞定,用不上GPU。

四、总结

在这个项目中,我们主要体会框架的用法,以及CUDA计算环境搭建的。通过编码实践,构建项目成功实验了CUDA计算环境搭建,为接下来的工作准备好环境。

五、参考文档

错误 MSB4062 未能从程序集加载任务

VS加载CUDA项目出错:未找到导入的项目

整理:warning LNK4098: 默认库“LIBCMT”与其他库的使用冲突;请使用 /NODEFAULTLIB:library

Win10下在VS2019中配置使用CUDA进行加速的C++项目 (配置.h文件,.dll以及.lib文件等)

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

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

相关文章

【C++】 使用CRT 库检测内存泄漏

CRT 库检测内存泄漏 一、CRT 库简介二、CRT 库的使用1、启用内存泄漏检测2、设置应用退出时显示内存泄漏报告3、丰富内存泄漏报告4、演示使用 内存泄漏是 C/C 应用程序中最微妙、最难以发现的 bug&#xff0c;存泄漏是由于之前分配的内存未能正确解除分配而导致的。 最开始的少…

MySQL主从同步优化指南:架构、瓶颈与解决方案

前言 ​ 在现代数据库架构中&#xff0c;MySQL 主从同步是实现高可用性和负载均衡的关键技术。本文将深入探讨主从同步的架构、延迟原因以及优化策略&#xff0c;并提供专业的监控建议。 MySQL 主从同步架构 ​ 主从复制流程&#xff1a; 从库生成两个线程&#xff0c;一个…

如何替换fmod studio的.bank文件内的音效?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

【Java毕业设计】基于JavaWeb的旅游论坛管理系统

文章目录 摘 要目 录1 概述1.1 研究背景及意义1.2 国内外研究现状1.3 拟研究内容1.4 系统开发技术1.4.1 Java编程语言1.4.2 vue技术1.4.3 MySQL数据库1.4.4 B/S结构1.4.5 Spring Boot框架 2 系统需求分析2.1 可行性分析2.2 系统流程2.2.1 操作流程2.2.2 登录流程2.2.3 删除信息…

【微信小程序开发(从零到一)】——个人中心页面的实战项目(一)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

(ICLR,2024)HarMA:高效的协同迁移学习与模态对齐遥感技术

文章目录 相关资料摘要引言方法多模态门控适配器目标函数 实验 相关资料 论文&#xff1a;Efficient Remote Sensing with Harmonized Transfer Learning and Modality Alignment 代码&#xff1a;https://github.com/seekerhuang/HarMA 摘要 随着视觉和语言预训练&#xf…

Rhino-Grasshopper:小白从入门开始学习

前言&#xff1a; 小编在这里即将开启一个新系列学习课程&#xff0c;主要内容为基于Rhino的3D打印学习&#xff0c;具体包括Rhino中的Python使用&#xff0c;Grasshopper的功能&#xff0c;讲解视频会陆续更新在B站&#xff0c;希望大家多多支持&#xff01; 关于相关学习、…

list(二)和_stack_queue

嗨喽大家好&#xff0c;时隔许久阿鑫又给大家带来了新的博客&#xff0c;list的模拟实现&#xff08;二&#xff09;以及_stack_queue&#xff0c;下面让我们开始今天的学习吧&#xff01; list(二)和_stack_queue 1.list的构造函数 2.设计模式之适配器和迭代器 3.新容器de…

HTML静态网页成品作业(HTML+CSS)—— 保护环境环保介绍网页(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

公检法部门保密网文件导出,这样做才是真正的安全又便捷

公检法是司法机关的核心组成&#xff0c;也是社会管理的重要组成&#xff0c;公检法部门的业务中涉及大量的居民数据、个人隐私、司法案件等信息&#xff0c;因此&#xff0c;数据的安全性至关重要。 根据我国法律要求&#xff0c;同时基于对数据的保护需要&#xff0c;我国的公…

Vue06-el与data的两种写法

一、el属性 用来指示vue编译器从什么地方开始解析 vue的语法&#xff0c;可以说是一个占位符。 1-1、写法一 1-2、写法二 当不使用el属性的时候&#xff1a; 两种写法都可以。 v.$mount(#root);写法的好处&#xff1a;比较灵活&#xff1a; 二、data的两种写法 2-1、对象式…

discuz点微同城源码34.7+全套插件+小程序前端

discuz点微同城源码34.7全套插件小程序前后端 模板挺好看的 带全套插件 自己耐心点配置一下插件 可以H5可以小程序

重磅就业报告前美股涨势消减,标普暂别纪录高位,英伟达盘中闪崩近6%,欧央行降息预期“退烧”,欧元跳涨

标普纳指创盘中历史新高后转跌&#xff0c;道指三连涨至近两周新高&#xff1b;芯片股指和台积电美股跌落纪录高位&#xff0c;英伟达三日收创历史新高后回落&#xff1b;游戏驿站盘中一度暴拉50%。中概股指回落&#xff0c;财报后蔚来收跌6.8%。欧央行会后&#xff0c;欧元盘中…

Dvws靶场

文章目录 一、XXE外部实体注入二、No-SQL注入三、Insecure Direct Object Reference四、Mass Assignment五、Information Disclosure六、Command Injection七、SQL注入 一、XXE外部实体注入 访问http://192.168.92.6/dvwsuserservice?wsdl&#xff0c;发现一个SOAP服务。在SO…

Golang | Leetcode Golang题解之第136题只出现一次的数字

题目&#xff1a; 题解&#xff1a; func singleNumber(nums []int) int {single : 0for _, num : range nums {single ^ num}return single }

【微信小程序】页面事件

下拉刷新 上拉触底 上拉触底距离指的是触发上拉触底事件时&#xff0c;滚动条距离页面底部的距离。 可以在全局或页面的json配置文件中&#xff0c;通过onReachBottomDistance属性来配置上拉触底的距离。 小程序默认的触底距离是50x,在实际开发中&#xff0c;可以根据自己的需…

三石峰汽车生产厂的设备振动检测项目案例

汽车生产厂的设备振动检测项目 ----天津三石峰科技&#xff08;http://www.sange-cbm.com&#xff09; 汽车产线有很多传动设备需要长期在线运行&#xff0c;会出现老化、疲劳、磨损等问题&#xff0c;为了避免意外停机造成损失&#xff0c;需要加装一些健康监测设备&#xf…

002.数据分析_Pandas初识

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

基于pulseaudio实现一个边录边播的demo

文章目录 前言一、主要APIpa_simple_newpa_simple_readpa_simple_write 二、C代码实现三、注意事项1、必须装有 libpulsedev 包2、编译方式3、运行说明 前言 通过上一讲&#xff0c;我们实现了一个加载pulseaudio的module-loopback的功能来实现侦听&#xff0c;那么除了加载模…

软件杯 题目:基于深度学习卷积神经网络的花卉识别 - 深度学习 机器视觉

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基…