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

一、前言

项目中有使用到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文件等)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

:长亭雷池社区版动态防护体验测评

序 长亭雷池在最近发布了动态防护功能&#xff0c;据说可以动态加密保护网页前端代码和阻止爬虫行为、阻止漏洞扫描行为等。今天就来体验测试一下 WAF 是什么 WAF 是 Web Application Firewall 的缩写&#xff0c;也被称为 Web 应用防火墙。区别于传统防火墙&#xff0c;WAF …

Error:..\FreeRTOS\portable\RVDS\ARM_CM7\r0p1\port.c,265

移植完FreeRTOS后&#xff0c;使用Keil进行编译&#xff0c;编译未报错&#xff0c;串口打印助手打印了错误报告。 串口打印的错误报告&#xff1a; Error:..\FreeRTOS\portable\RVDS\ARM_CM7\r0p1\port.c,265看一下265行 该行所在函数为prvTaskExitError函数&#xff0c;功能…

阅读笔记:Multi-threaded Rasterization in the Chromium Compositor

Multi-threaded Rasterization in the Chromium Compositor PPT 原始链接&#xff1a; https://docs.google.com/presentation/d/1nPEC4YRz-V1m_TsGB0pK3mZMRMVvHD1JXsHGr8I3Hvc/edit?uspsharing PPT主要介绍了Chromium浏览器中使用多线程光栅化(Impl-side painting)的机制&a…

目标检测——FGVC-Aircraft数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

【Vue】Vue路由-重定向

问题 网页打开时&#xff0c; url 默认是 / 路径&#xff0c;未匹配到组件时&#xff0c;会出现空白 解决方案 重定向 → 匹配 / 后, 强制跳转 /home 路径 语法 { path: 匹配路径, redirect: 重定向到的路径 }, 比如&#xff1a; { path:/ ,redirect:/home }代码示例 const…

docker构建jdk17镜像

资料参考 参考自黑马教程&#xff1a;10.Docker基础-自定义镜像_哔哩哔哩_bilibili 更多详细语法声明&#xff0c;请参考官网文档&#xff1a;https://docs.docker.com/engine/reference/builder 初步准备 1、下载jdk17包&#xff08;linux版&#xff09;&#xff0c;我这边版…

微信小程序多端框架打包后发布到APP Store

IPA 上架 App Store 生成 iOS 证书和 Provisioning Profile iOS 开发者账号缴/续费的发票查看和获取 个人开发者把小程序发布到 App Store 5个步骤&#xff08;保姆级教程&#xff09; 一、参数的设置、证书的生成、生成profile文件 微信小程序多端应用Donut IOS相关的参数…

佳能5DMARK IV mov视频覆盖的恢复方法

5DMARK IV算是佳能比较经典的一款摄像机&#xff0c;是佳能早期使用MOV的摄像机之一&#xff0c;MOV是当初佳能高端机的象征&#xff0c;当然现在佳能已经不在通过MOV和MP4来区分硬件级别了。下边这个案例是文件拍摄时断电&#xff0c;结果变成0字节&#xff0c;然后覆盖了部分…

解决Spark流处理产生的小文件问题

做流批一体&#xff0c;湖仓一体的大数据架构&#xff0c;常见的做法就是&#xff1a; 数据源->spark Streaming->ODS&#xff08;数据湖&#xff09;->spark streaming->DWD&#xff08;数据湖&#xff09;->... 那么数据源->spark Streaming->ODS&…

充电桩产业链及商业模式

产业链概况 充电桩产业链分为上游元器件和设备生产商、建设商&#xff0c;中游为运营商&#xff0c;下游为各类充电场景。其中&#xff0c;上游零部件厂商提供充电模块&#xff08;IGBT、逆变器等&#xff09;、配电滤波设备、监控计费设备、充电枪等&#xff1b;中游充电桩厂…

Linux Ext2/3/4文件系统

文章目录 前言一、Linux文件系统简介1.1 简介1.2 Linux File System Structure1.3 Directory Structure 二、Ext2/3/4文件系统2.1 Minix2.2 EXT2.3 EXT22.4 EXT32.5 EXT4 三、EXT Inode参考资料 前言 这篇文章介绍了Linux文件系统的一些基础知识&#xff1a;Linux 文件系统简介…

sing-task message

文章目录 1.起因2.查因过程2.1 定位job2.2 定位sql text2.3 定位db_link2.4 测试dblink2.5 tnsping host2.6 检查host信息2.7检查网路状况 3.处置办法&#xff1a;4.结论 1.起因 在巡查长事务时&#xff0c;有两个事务执行了很长时间没有完成 SELECT SE.SID,SE.SERIAL#,to_ch…

创新案例 | AI数据驱动下的全域数字化转型的五大关键洞见

近年来通过全域数字化转型在竞争激烈的市场中脱颖而出。传统零食行业面临市场竞争加剧和消费者需求多样化的挑战&#xff0c;如何利用数据驱动和AI技术&#xff0c;能更好地实现会员运营效率和用户满意度的显著提升呢&#xff1f;本文将探讨全域数字化转型的五大关键洞见&#…

Application UI

本节包含关于如何用DevExpress控件模拟许多流行的应用程序ui的教程。 Windows 11 UI Windows 11和最新一代微软Office产品启发的UI。 Office Inspired UI Word、Excel、PowerPoint和Visio等微软Office应用程序启发的UI。 How to: Build an Office-inspired UI manually 本教…

关于Stream.toList()方法使用小记

对照示例 public static void main(String[] args) {final List<String> list new ArrayList<>();list.add("aa");list.add("bb");list.add("cc");list.remove("cc");System.out.println(list);}结果&#xff1a; Stre…

华为机考入门python3--(33)牛客33-图片整理

分类&#xff1a;排序 知识点&#xff1a; 对字符串中的字符ASCII码排序 sorted(my_str) 题目来自【牛客】 def sort_images(s):# 可以使用ord(A)求A的ASCII值&#xff0c;需要注意的是A的值&#xff08;65&#xff09;比a的值小&#xff08;97&#xff09;sorted_images …

经济与安全兼顾:茶饮店购买可燃气体报警器的价格考量

可燃气体报警器在如今的社会中扮演着至关重要的角色。它们用于检测环境中的可燃气体浓度&#xff0c;及早发现潜在的火灾隐患&#xff0c;保护人们的生命和财产安全。 在这篇文章中&#xff0c;佰德将介绍可燃气体报警器的安装、检定以及价格&#xff0c;通过实际案例和数据&a…

【MySQL】SQL通用语法

【MySQL】SQL通用语法 SQL是结构化查询语言&#xff08;Structured Query Language&#xff09;的缩写&#xff0c;是一种专门用来管理和操作关系型数据库的标准化语言。SQL能够实现数据库的创建、查询、更新和删除操作&#xff0c;以及对数据进行存储、检索和管理。通过SQL语句…

【MySQL】数据库的增删查改

文章目录 前言1. 新增1.1 全插入1.2 指定某些列名插入1.3 多行插入1.4 边查询边插入 2. 约束2.1 非空约束2.2 唯一性约束2.3 默认值约束2.4 主键约束2.5 外键约束2.6 check 约束2.7 外键的逻辑删除 3. 查询 - 初阶3.1 全列查询3.2 指定列查询3.3 指定表达式查询3.4 别名查询3.5…

Python pandas openpyxl excel合并单元格,设置边框,背景色

Python pandas openpyxl excel合并单元格&#xff0c;设置边框&#xff0c;背景色 1. 效果图2. 源码参考 1. 效果图 pandas设置单元格背景色&#xff0c;字体颜色&#xff0c;边框 openpyxl合并单元格&#xff0c;设置丰富的字体 2. 源码 # excel数字与列名互转 import o…