CUDA编程入门

环境搭建

硬件:NVIDIA GPU

软件:CUDA Toolkit(提供 nvcc 编译器、CUDA 头文件、库文件)

我现在的环境:

GPU: RTX 4060

Driver: 591.86

CUDA Toolkit: 13.1

搭建

  1. 先装 NVIDIA 驱动:验证命令:nvidia-smi
  2. 安装 CUDA Toolkit :验证命令:nvcc --version

具体的环境搭建可以看这篇文章:C++开发基础之初探CUDA计算环境搭建

函数编写流程:

内存模型:Host Memory CPU内存 Device Memory GPU内存

CUDA编程整体代码架构

CPU部分:逻辑控制、内存显存申请&释放(申请CPU、GPU内存、 CPU到GPU内存拷贝、启动kernel (launch kernel)、将结果从GPU拷贝到CPU、释放相关内存

GPU部分:算子编写(执行相应并行计算算子操作)

核函数(kernel function)

基础用法:

函数的定义

在普通函数前+__global__关键字

函数的调用

函数名<<<参数>>>();

cpp 复制代码
#include <cuda_runtime.h>
#include <cuda.h>
#include <stdio.h>
#include <time.h>

__global__ void testPrint() {
    printf("hello kernel\n");
}



int main() {
    testPrint<<<1, 1, 0, nullptr>>>();
    cudaDeviceSynchronize();
    return 0;
}
cpp 复制代码
#include <cuda_runtime.h>
#include <cuda.h>
#include <stdio.h>
#include <time.h>

/*
计算两个数的和(cpu)
*/
double_t numSum(double_t a, double_t b) {
    return a + b;
}

/*
计算两个数字的和(GPU)
*/
__global__ void numSumKernel(double_t* a, double_t* b, double_t* result) {
    *result = *a + *b;
}


int main() {
    
    // 1.在主存上开辟空间即初始化 a\b\result
    // cudaMallocHost(&指针,空间大小)
    double_t* a{nullptr};
    double_t* b{nullptr};
    double_t* result{nullptr};

    cudaMallocHost(&a, sizeof(double_t));
    cudaMallocHost(&b, sizeof(double_t));
    cudaMallocHost(&result, sizeof(double_t));

    *a = 1.4;
    *b = 1.3;
    *result = 0.0;

    // 2.将主存空间中的数据放到显存中
    // cudaMemcpy(目的地,源地址,数据量,搬运方式)
    // 开辟显存的空间
    // cudaMalloc(地址,空间大小)
    double_t* aDevice{nullptr};
    double_t* bDevice{nullptr};
    double_t* resultDevice{nullptr};
    cudaMalloc(&aDevice, sizeof(double_t));
    cudaMalloc(&bDevice, sizeof(double_t));
    cudaMalloc(&resultDevice, sizeof(double_t));
   
    cudaMemcpy(aDevice, a, sizeof(double_t), cudaMemcpyHostToDevice);
    cudaMemcpy(bDevice, b, sizeof(double_t), cudaMemcpyHostToDevice);

    // 3.调用核函数进行运算,结果保存到显存中
    numSumKernel<<<1, 1, 0, nullptr>>>(aDevice, bDevice, resultDevice);


    //4. 把运算结果从显存中搬运到主存中,进行后续操作
    // 把显存内容般涌道主存
    cudaMemcpy(result, resultDevice, sizeof(double_t), cudaMemcpyDeviceToHost);
    printf("result = %f\n", *result);

    // 资源回收
    // 显存资源
    // cudaFree()
    cudaFree(resultDevice);
    cudaFree(bDevice);
    cudaFree(aDevice);
    // 主存
    cudaFree(result);
    cudaFree(b);
    cudaFree(a);


    cudaDeviceSynchronize();
    return 0;
}

第二个代码的实例完全跟函数编写流程一致

大家看下面的例子

cpp 复制代码
#include <cuda_runtime.h>
#include <cuda.h>
#include <stdio.h>
#include <time.h>


/*
多线程中,函数会执行多次,每次调用都会在一个新的线程中执行
*/
__global__ void numSumKernel(uint32_t* a, size_t arr_len_a, uint32_t* b, size_t arr_len_b, uint32_t* result, size_t arr_len_c) {
    // for (int i = 0; i < arr_len_a; ++i) {
    //     result[i] = a[i] + b[i];
    // }
    // threadIdx为核函数内置变量
    // 8个线程、8个加法运算、每次取2个
    // 16条传入-》threadIdx内置变量-》从16条数据中取出对应要处理的数据
    // printf("threadIdx: %d\n", threadIdx.x);
    result[threadIdx.x] = a[threadIdx.x] + b[threadIdx.x];
}

int main() {

    // 申请主存空间
    int32_t* a{nullptr};
    int32_t* b{nullptr};
    int32_t* result{nullptr};

    constexpr size_t ARR_LEN = 8;

    cudaMallocHost(&a, ARR_LEN * sizeof(int32_t));
    cudaMallocHost(&b, ARR_LEN * sizeof(int32_t));
    cudaMallocHost(&result, ARR_LEN * sizeof(int32_t));

    // 主存空间数据初始化
    for (int i = 0; i < ARR_LEN; ++i) {
        a[i] = i;
        b[i] = i;
        result[i] = 0;
    }

    // 申请显存空间,主存空间的数据放到显存中
    uint32_t* aDevice{nullptr};
    uint32_t* bDevice{nullptr};
    uint32_t* resultDevice{nullptr};

    cudaMalloc(&aDevice, ARR_LEN * sizeof(uint32_t));
    cudaMalloc(&bDevice, ARR_LEN * sizeof(uint32_t));
    cudaMalloc(&resultDevice, ARR_LEN * sizeof(uint32_t));

    cudaMemcpy(aDevice, a, sizeof(uint32_t) * ARR_LEN, cudaMemcpyHostToDevice);
    cudaMemcpy(bDevice, b, sizeof(uint32_t) * ARR_LEN, cudaMemcpyHostToDevice);
    cudaMemcpy(resultDevice, result, sizeof(uint32_t) * ARR_LEN, cudaMemcpyHostToDevice);


    // 调用核函数进行运算 blockDim
    numSumKernel<<<1, ARR_LEN, 0, nullptr>>>(aDevice, ARR_LEN, bDevice, ARR_LEN, resultDevice, ARR_LEN);

    // 将显存的结果转移到主存中
    cudaMemcpy(result, resultDevice, sizeof(uint32_t) * ARR_LEN, cudaMemcpyDeviceToHost);

    for (int i = 0; i < ARR_LEN; ++i) {
         printf("result[%zu]: %d\n", i,  result[i]);
    } 
   

    // 释放资源
    cudaFree(resultDevice);
    cudaFree(bDevice);
    cudaFree(aDevice);
    // 释放主存资源
    cudaFree(result);
    cudaFree(b);
    cudaFree(a);

    cudaDeviceSynchronize();

    return 0;
}

上面的例子中,开启了8个线程,一个线程只处理一部分数据的运算。

注意,每次用nvcc ./文件名 -o 可执行文件名.exe。当文件比较多,每次这样都比较麻烦,所以我们可以用cmake来连接。

参考的cmake程序如下:

cpp 复制代码
cmake_minimum_required(VERSION 3.18)

# 设定CUDA程序所运行的目标架构
set(CMAKE_CUDA_ARCHITECTURES "75;80;86;89")

# 设定使用的c++版本
set(CMAKE_CXX_STANDARD 20)
# 检查系统是否满足当前版本的c++规范
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 超出规范的扩展是否需要
set(CMAKE_CXX_EXTENSIONS OFF)

# 声明项目
project(cmake_cuda C CXX CUDA)

# 查找CUDA Tookit
find_package(CUDAToolkit REQUIRED)


# 源码文件
file(GLOB SOURCE_FILES "src/*.cu" "src/*.h")

# 构建目标
add_executable(cmake_cuda ${SOURCE_FILES})

# 链接CUDA库
target_link_libraries(cmake_cuda
    PRIVATE
        CUDA::cudart
        cuda
)

# 把目标可执行文件编译到bin目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)

具体的cmake可以看我的这篇文档:Cmake

相关推荐
Bode_20021 小时前
移动多智能体现场柔性测量与自适应质检的难点与实现路径
人工智能·计算机视觉·制造
Honker_yhw1 小时前
大数据管理与应用系列丛书《数据挖掘》(吕欣等著)读书笔记-集成学习与 AdaBoost
人工智能·数据挖掘·集成学习
weixin_408099671 小时前
2026 AI生成图片快速去水印的5种实测方法(附在线工具 + Python/Java/PHP API代码)
java·人工智能·python·api接口·ai去水印·石榴智能·自动去水印
云智慧AIOps社区1 小时前
直击BEYOND Expo 2026 | 云智慧Cloudwise亮相澳门,发布“三层战略”护航 AI 数实共生
运维·人工智能·运维自动化·ai基础设施可靠性
行业研究员1 小时前
2026 AI Agent记忆解决方案:腾讯云数据库提供全场景支撑
数据库·人工智能·腾讯云·ai记忆
西安同步高经理1 小时前
国产音频频谱分析仪使用案例,多通道音频分析仪,音频频谱分析仪
大数据·人工智能·音视频
好家伙VCC1 小时前
动态因子图谱+滚动SHAP重构量化模型可解释性
java·人工智能·重构
dingzd951 小时前
TikTok创作者AI搜索推出后跨境品牌如何提高达人匹配效率
大数据·人工智能·新媒体运营·市场营销·跨境
chenying9981791 小时前
掩码扩散语音克隆:参考音频为什么会被噪声“污染“?
人工智能·音视频·语音合成