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

相关推荐
豪气的程序猿1 小时前
电商图片工作流怎么选?Lingko AI 对比折叠键盘主图与详情页
人工智能
小刘快学习2 小时前
把 AI 账单拆到部门:企业 AI 网关的精准分账思路
人工智能
米小虾2 小时前
你让监控模型读的思维链,可能是攻击者写好的剧本
人工智能
deepseek232 小时前
Iris 开源搜索智能体拆解:35B 与 397B 中文仅差 0.3 分,上下文管理胜过堆参数
人工智能·ai agent·开源模型
ai小陈2 小时前
CUDA Stream实战:让数据传输与GPU计算真正重叠
人工智能·深度学习·ai·pdf·云计算·gpu算力
residual_fan2 小时前
【学术论文】航空发动机故障诊断智能体:基于持续对比强化学习的动态优化方法
人工智能·算法·数据挖掘·数据分析
东风破_2 小时前
从 RAG 到 Agentic RAG:第二步,把复杂问题拆开再检索
人工智能
dehuisun2 小时前
第07篇:RAG+Agent 部署架构、资源评估与私有化方案
人工智能
米小虾2 小时前
拆给 8 个子智能体,只拿回 2.3 倍信息:多智能体分解的产出守恒律
人工智能·agent
虹科网络安全3 小时前
Redis 安全公告:CVE-2026-81934 TLS 处理漏洞及修复建议
网络·人工智能·网络安全