当前位置: 首页 > news >正文

cuda编程笔记(2)--传递参数、设备属性

以下是最简单的带参数的核函数使用过程:

#include<iostream>
#include<cstdio>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
__global__ void add(int a,int b,int *c) {*c = a + b;
}
int main() {int c;int* dev_c;cudaMalloc((void **)&dev_c,sizeof(int));add << <1, 1 >> > (2, 7, dev_c);cudaMemcpy(&c, dev_c, sizeof(int), cudaMemcpyDeviceToHost);//这里会隐式同步,等待核函数执行完printf("2+7=%d\n", c);cudaFree(dev_c);cudaError_t err = cudaDeviceSynchronize();if (err != cudaSuccess) {std::cerr << "CUDA Error: " << cudaGetErrorString(err) << std::endl;}return 0;
}

add的参数:

  • int a, int b:两个通过值传递的整型参数。

  • int *c:一个指针,指向 GPU 设备内存,用于返回结果。

cudaMalloc 的作用

cudaError_t cudaMalloc(void **devPtr, size_t size);
  • 作用:在 GPU 设备内存上分配一块大小为 size 字节的内存空间。

  • 参数说明

    • void **devPtr:一个指向设备指针的指针,cudaMalloc 会将申请到的设备内存地址写入这个指针。

    • size:需要分配的内存字节数。

  • 返回值:返回 cudaSuccess(表示成功),或者其他错误码。

为什么要用 cudaMalloc

因为 GPU 上运行的核函数(__global__)不能访问 CPU 的内存(host memory),所以:

  • 要传递结果回 CPU,必须在 GPU 内存中有一块空间存放结果

  • 你不能直接传一个 CPU 指针(如 int* c)给核函数,否则会产生非法内存访问;

  • 所以你要用 cudaMalloc 分配一块 GPU 内存(例如 int* dev_c),传给核函数用于写入结果。

 示意图:

+----------------+        cudaMemcpy        +----------------+
|    Host (CPU)  | <----------------------> |  Device (GPU)   |
|  int c;        |                          |  int* dev_c     |
+----------------+        cudaMalloc        +----------------+↑dev_c = GPU上的内存地址

在主机代码中,不能对 dev_c 解引用,只能通过 cudaMemcpy 把它里面的数据拷回来后使用。

区域地址空间是否可以解引用该地址?
主机内存(Host)主机地址只能在 CPU 代码中解引用
设备内存(Device)显存地址只能在 GPU 核函数中解引用

 但是在主机上可以对它进行参数传递等不涉及访问内存的操作。

cudaMemcpy

cudaError_t cudaMemcpy(void* dst, const void* src, size_t count, cudaMemcpyKind kind);
参数名说明
dst目标地址(可以是主机或设备内存)
src源地址(可以是主机或设备内存)
count拷贝的字节数
kind拷贝类型,说明从哪拷到哪(见下表)
类型含义
cudaMemcpyHostToDevice从主机(Host)拷贝到设备(Device)
cudaMemcpyDeviceToHost从设备(Device)拷贝到主机(Host)
cudaMemcpyDeviceToDevice设备内存之间拷贝(GPU → GPU)
cudaMemcpyHostToHost主机内存之间拷贝(等价于 memcpy

cudaMemcpy同步操作,它会阻塞主机线程直到拷贝完成。这保证了数据安全,但也意味着它会造成 CPU 等待。

⚠️ 如果你希望异步传输,需要使用 cudaMemcpyAsync 并配合 CUDA 流(streams)。

释放显存的函数 —— cudaFree

cudaError_t cudaFree(void* devPtr);

 在 GPU 上释放之前通过 cudaMalloc 分配的内存。

  • devPtr:需要释放的设备指针(即之前通过 cudaMalloc 得到的 GPU 地址)。

  • 返回值:返回一个 cudaError_t 类型的错误码(cudaSuccess 表示成功)。

就像在 CPU 上使用 malloc 后要调用 free,在 GPU 上使用 cudaMalloc 分配内存后也必须调用 cudaFree 来释放显存:

  • 否则会造成 显存泄漏

  • 长时间运行或循环分配会 耗尽 GPU 显存

  • 导致 CUDA 程序崩溃或性能严重下降。

核函数的参数规则

一、核函数参数的基本要求

参数必须是可以被复制(copiable)的数据类型

  • 标量类型(如 int, float, double, char

  • 指针类型(如 int*, float*

  • 结构体或类(必须是 trivially copyable)

不支持的:

  • 引用类型(如 int&

  • 虚函数、继承的类等复杂对象

二、参数传递方式:值传递(by value)

CUDA 核函数的参数都是 按值传递,即参数会从主机拷贝一份副本传递给设备。

  • 标量变量:直接复制一份到 GPU。

  • 指针变量:复制的是主机这边的指针值(指向 GPU 内存的地址)。

三、指针指向的内存必须是设备内存

你传给内核的指针必须指向显存(设备内存),否则会导致错误或非法访问:

反例:

int c;
add<<<1, 1>>>(2, 3, &c);  // ❌ 错误!主机地址传入设备代码,非法访问

四、核函数参数个数限制

CUDA 对核函数的参数总字节数有限制(包括传入的所有变量)。

  • 通常限制为 最多 256 字节(不同架构可能有差异);

  • 如果你传入很多参数(如结构体),建议:

    • 封装为一个结构体;

    • 或使用显存中数据结构代替参数(通过指针传入)。

  • 不能使用可变参数

五、结构体作为参数的限制

你可以将结构体作为核函数参数传入,但有几点要注意:

  • 结构体必须是简单结构体(POD 类型,不能有构造函数、虚函数、继承);

  • 会被按值拷贝到设备上;

  • 如果结构体内部有指针,那些指针必须指向设备内存。

六、核函数参数必须可在 host 代码中准备好

由于核函数只能从 host 调用,所有参数必须能在 host 端构造并传入(不能传 GPU 上运行时才能生成的数据结构,比如 GPU 上的临时指针等)。

查询设备

cudaGetDeviceCount

cudaError_t cudaGetDeviceCount(int* count);

获取当前系统中 可用的 CUDA 设备数量(即 GPU 的个数)。

参数:

  • count: 一个指针,用来存放返回的设备数量。

返回值:

  • cudaSuccess 表示成功;

  • 否则返回错误码(如没有安装驱动、无 GPU 等)。

cudaGetDeviceProperties

cudaError_t cudaGetDeviceProperties(cudaDeviceProp* prop, int device);

功能:

获取指定编号 GPU 的详细属性(例如显存大小、线程数量、SM 架构等)。

参数:

  • prop: 指向 cudaDeviceProp 结构体的指针,用于接收设备信息;

  • device: 设备编号,范围是 [0, count - 1]

struct cudaDeviceProp

struct cudaDeviceProp {char name[256];                         // GPU 名称字符串size_t totalGlobalMem;                 // 全局显存总大小(单位:字节)size_t sharedMemPerBlock;             // 每个线程块可用的共享内存大小int regsPerBlock;                     // 每个线程块可用的寄存器数量int warpSize;                         // 一个 warp 中的线程数量(通常为32)size_t memPitch;                      // 最大内存复制宽度(以字节为单位)int maxThreadsPerBlock;              // 每个线程块支持的最大线程数量int maxThreadsDim[3];                // 每个线程块在 x, y, z 三维的最大线程数int maxGridSize[3];                  // 每个网格在 x, y, z 三维的最大块数int clockRate;                       // 时钟频率(kHz)size_t totalConstMem;                // 常量内存总大小(字节)int major;                           // 计算能力主版本号int minor;                           // 计算能力次版本号size_t textureAlignment;            // 纹理对齐要求(字节)size_t texturePitchAlignment;       // 对二维纹理中行对齐的要求(字节)int deviceOverlap;                  // 是否支持设备与主机的重叠执行(1 是,0 否)int multiProcessorCount;            // SM(流式多处理器)数量int kernelExecTimeoutEnabled;       // 是否启用了内核执行超时(1 是,0 否)int integrated;                     // 是否为集成 GPU(1 是,0 否)int canMapHostMemory;              // 是否支持映射主机内存到设备地址空间int computeMode;                    // 计算模式(0: 默认,1: 仅主机访问,2: 禁止访问)int maxTexture1D;                   // 最大 1D 纹理尺寸int maxTexture1DMipmap;            // 最大 1D Mipmap 纹理尺寸int maxTexture1DLinear;            // 最大 1D 线性纹理尺寸(仅支持一维纹理)int maxTexture2D[2];               // 最大 2D 纹理尺寸(width, height)int maxTexture2DMipmap[2];         // 最大 2D Mipmap 尺寸int maxTexture2DLinear[3];         // 最大 2D 线性纹理尺寸(width, height, pitch)int maxTexture2DGather[2];         // 最大 2D Gather 纹理尺寸int maxTexture3D[3];               // 最大 3D 纹理尺寸(width, height, depth)int maxTexture3DAlt[3];            // 替代的最大 3D 纹理尺寸int maxTextureCubemap;             // 最大立方体纹理尺寸int maxTexture1DLayered[2];        // 最大 1D 分层纹理尺寸(width, layers)int maxTexture2DLayered[3];        // 最大 2D 分层纹理尺寸(width, height, layers)int maxTextureCubemapLayered[2];   // 最大立方体分层纹理尺寸(width, layers)int maxSurface1D;                  // 最大 1D surface 尺寸int maxSurface2D[2];               // 最大 2D surface 尺寸int maxSurface3D[3];               // 最大 3D surface 尺寸int maxSurface1DLayered[2];        // 最大 1D 分层 surface 尺寸int maxSurface2DLayered[3];        // 最大 2D 分层 surface 尺寸int maxSurfaceCubemap;             // 最大立方体 surface 尺寸int maxSurfaceCubemapLayered[2];   // 最大立方体分层 surface 尺寸size_t surfaceAlignment;           // surface 对齐要求(字节)int concurrentKernels;             // 是否支持多个 kernel 并发执行int ECCEnabled;                    // 是否启用 ECC(错误检查与纠正)int pciBusID;                      // PCI 总线 IDint pciDeviceID;                   // PCI 设备 IDint pciDomainID;                   // PCI 域 IDint tccDriver;                     // 是否为 TCC 驱动(用于专业显卡如 Tesla)int asyncEngineCount;              // 同时支持异步传输与执行的引擎数量int unifiedAddressing;            // 是否支持统一虚拟地址空间(UVA)int memoryClockRate;              // 显存时钟频率(kHz)int memoryBusWidth;               // 显存总线宽度(位)int l2CacheSize;                  // L2 缓存大小(字节)int maxThreadsPerMultiProcessor;  // 每个 SM 支持的最大线程数int streamPrioritiesSupported;    // 是否支持流优先级int globalL1CacheSupported;       // 是否支持全局 L1 缓存int localL1CacheSupported;        // 是否支持本地 L1 缓存size_t sharedMemPerMultiprocessor; // 每个 SM 可用的共享内存大小(字节)int regsPerMultiprocessor;         // 每个 SM 可用的寄存器数量int managedMemory;                 // 是否支持托管内存int isMultiGpuBoard;              // 是否为多 GPU 板卡的一部分int multiGpuBoardGroupID;         // 多 GPU 板卡上的组 ID
};


配合使用,获取设备属性

#include <iostream>
#include <cuda_runtime.h>int main() {int deviceCount = 0;cudaError_t err = cudaGetDeviceCount(&deviceCount);if (err != cudaSuccess) {std::cerr << "cudaGetDeviceCount failed: " << cudaGetErrorString(err) << std::endl;return -1;}std::cout << "Found " << deviceCount << " CUDA device(s).\n";for (int i = 0; i < deviceCount; ++i) {cudaDeviceProp prop;cudaGetDeviceProperties(&prop, i);std::cout << "\nDevice " << i << ": " << prop.name << "\n";std::cout << "  Total Global Memory: " << (prop.totalGlobalMem >> 20) << " MB\n";std::cout << "  Compute Capability: " << prop.major << "." << prop.minor << "\n";std::cout << "  Multiprocessors: " << prop.multiProcessorCount << "\n";std::cout << "  Max Threads Per Block: " << prop.maxThreadsPerBlock << "\n";std::cout << "  Max Threads Dim: ("<< prop.maxThreadsDim[0] << ", "<< prop.maxThreadsDim[1] << ", "<< prop.maxThreadsDim[2] << ")\n";std::cout << "  Max Grid Size: ("<< prop.maxGridSize[0] << ", "<< prop.maxGridSize[1] << ", "<< prop.maxGridSize[2] << ")\n";}return 0;
}

cudaGetDevice

cudaGetDevice 是 CUDA 运行时 API 中的一个函数,用来获取当前线程所使用的 CUDA 设备编号(Device ID)。它的常见用途是:

  • 查询当前使用的是哪一个 GPU。

  • cudaSetDevice(int device) 搭配使用,切换或记录设备上下文。

cudaError_t cudaGetDevice(int* device);

参数说明:

  • int* device:一个指向整数的指针,函数会把当前设备的 ID(从 0 开始)写入这里。

配合使用:cudaSetDevice

cudaSetDevice(1);       // 绑定设备1
cudaGetDevice(&id);     // 确认当前设备 id 是 1

cudaChooseDevice

cudaChooseDevice 是 CUDA Runtime API 中的一个函数,它的作用是:根据你指定的一些性能偏好,选择最适合的 CUDA 设备(GPU)并返回设备编号(ID)

cudaError_t cudaChooseDevice(int* device, const cudaDeviceProp* prop);

参数说明:

  • int* device:返回选择的设备编号。

  • const cudaDeviceProp* prop:你的“理想设备”配置(可以只设置关键字段)。

使用方式:

    cudaDeviceProp desiredProp = {};desiredProp.major = 7;  // 至少计算能力为 7.x(如 Volta, Turing, Ampere)desiredProp.totalGlobalMem = 4L * 1024 * 1024 * 1024;  // 至少 4GB 显存int chosenDevice = -1;cudaError_t err = cudaChooseDevice(&chosenDevice, &desiredProp);

相关文章:

  • 让爱延续,共筑养老服务产业的美好未来
  • 企业级AI开启落地战,得场景者得天下
  • 快速入门Java+Spring Ai+deepseek 开发
  • 快速入门基于stm32 CubeMX生成的freeRTOS操作系统开发
  • 七:操作系统文件系统之空闲空间管理
  • Guava限频器RateLimiter的使用示例
  • Batch Normalization[[
  • 辛格迪客户案例 | 合规升级之路:辛格迪助力倍特药业迈向卓越
  • Selenium 测试框架 - JavaScript
  • Python 进阶【二】:多进程编程
  • PyTorch安装Cuda版本选择
  • xxl-job快速创建复制任务
  • H-ZERO导出组件支持智能分片、异步导出、动态配置等高级特性,让“一键导出”成为现实!
  • 【ARM】MDK浏览信息的生成对于构建时间的影响
  • MySql--explain的用法
  • 第2讲、从启动到表单加载:Odoo 18 的完整执行流程详解
  • Dolphin文档解析从理论到实践——保姆级教程
  • SSM 框架深度整合实战:从零构建企业级 Java 应用
  • Redission学习专栏(一):快速入门及核心API实践
  • 【操作系统】硬件结构知识点详解
  • 西双网站建设/培训机构招生7个方法
  • 魅族的网站建设与安全/长沙seo排名公司
  • 网站设计公司竞争优势/济南百度推广公司电话
  • 赤坎网站制作/电商推广
  • 黑龙江专业建站/关键词优化流程
  • 湖北现代城市建设集团网站/西点培训班一般要多少学费