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

相关推荐
万法若空7 分钟前
排列组合恒等式
c++·算法
码视野11 分钟前
基于 Vue3 + Element Plus 的【基于物联网的智慧居家养老健康关怀与紧急医疗呼叫系统】设计与实现(附完整源码与PRD)
人工智能·物联网·vue3
月亮和九磅十五便士16 分钟前
朝闻 AI|2026-08-25
人工智能
LazzyE22 分钟前
初读 Miles:RL 后训练,难在采样和训练之间
人工智能
txg66625 分钟前
LLM 驱动漏洞传播验证:TransferFuzz-Pro 如何自动调试“继承“来的安全债
人工智能·深度学习·安全
shujudang34 分钟前
从数据闭环到 Agent 协同:企业营销自动化如何演进为智能运营?
大数据·运维·人工智能·数据分析·自动化
吉安特尔雅35 分钟前
2026行业实测|主流AI获客系统功能拆解与选型指南(含剪流全矩阵深度分析)
人工智能·剪流geo·剪流·ai获客哪家靠谱
动恰客流统计36 分钟前
ReID边缘计算视觉客流统计:无网户外场景的数字化落地路径
大数据·人工智能
FL162386312943 分钟前
仙人掌品种类型检测数据集VOC+YOLO格式1991张10类别
人工智能·yolo·机器学习
Sunlly44 分钟前
OpenClaw 的 Elevated 到底给了 Agent 什么权限
人工智能