[GPU运算]cuda调用

这段代码是一个标准的CUDA GPU内核函数,用于并行计算向量加法。让我详细解释每一部分的写法和目的:

📌 函数声明部分

c 复制代码
__global__
void vector_add_gpu(float *a, float *b, float *c, int n)

__global__ 关键字的含义

  • 作用:告诉编译器这是一个GPU内核函数,从CPU调用但在GPU上执行
  • 调用方式 :通过 <<<grid, block>>> 语法从CPU调用
  • 特点 :不能有返回值(必须是void),支持异步执行

参数设计要求

  • 指针参数 :a, b, c 必须是GPU内存地址(设备指针)
  • 值传递 :n 是普通整数,直接传递给所有线程
  • 限制:不能使用函数指针、不能是类的成员函数、不能有变长参数

🧵 线程索引计算部分

c 复制代码
int index = blockIdx.x * blockDim.x + threadIdx.x;

为什么要这样计算?

CUDA使用**三维网格(Grid)和三维块(Block)**的层次结构:

复制代码
Grid (整个计算空间)
├── Block 0 (包含N个线程)
├── Block 1
└── Block 2
    ├── Thread 0
    ├── Thread 1
    └── Thread 2

计算公式拆解:

  • blockIdx.x:当前block在grid中的索引(0,1,2...)
  • blockDim.x:每个block包含的线程数(如1024)
  • threadIdx.x:当前线程在block内的索引(0~1023)

举例:假设每个block有256个线程

  • Block 0的线程:索引0~255
  • Block 1的线程:索引256~511
  • Block 2的线程:索引512~767

🚶 Stride模式部分

c 复制代码
int stride = blockDim.x * gridDim.x;

Stride的含义

stride = 每个block的线程数 × 总block数 = 总共的线程总数

例如:

  • 每个block 256线程 × 总共128个block = stride = 32768个线程

为什么要用stride模式?

c 复制代码
for (int i = index; i < n; i += stride) {
    c[i] = a[i] + b[i];
}

这种模式不是 让每个线程只处理一个元素,而是处理多个元素:

工作原理:

  • 线程0处理:索引 0, stride, 2×stride, 3×stride...
  • 线程1处理:索引 1, stride+1, 2×stride+1...
  • 线程2处理:索引 2, stride+2, 2×stride+2...

优点:

  1. 适配任意数组大小:n不一定能被线程总数整除
  2. 负载均衡:所有线程工作量相近
  3. 合并访问:连续的线程访问连续的内存地址,提高内存带宽利用率

📊 实际运行示例

假设配置:<<<2个block, 4个线程/block>>>

复制代码
总线程数 = 2 × 4 = 8个线程
stride = 8

处理数组 n=10个元素:

线程0 (index=0): 处理 [0, 8]
线程1 (index=1): 处理 [1, 9]
线程2 (index=2): 处理 [2]
线程3 (index=3): 处理 [3]
线程4 (index=4): 处理 [4]
线程5 (index=5): 处理 [5]
线程6 (index=6): 处理 [6]
线程7 (index=7): 处理 [7]

⚠️ 写法的关键要求

1. 内存一致性

c 复制代码
// ❌ 错误:不能访问主机内存
__global__ void kernel(int *host_ptr) { 
    int val = host_ptr[0]; // 错误!host_ptr在CPU内存
}

// ✅ 正确:必须使用设备内存
__global__ void kernel(int *dev_ptr) {
    int val = dev_ptr[0]; // 正确
}

2. 线程安全

c 复制代码
// 避免:多个线程写同一位置(竞态条件)
if (threadIdx.x == 0) {
    c[0] = a[0] + b[0];  // 只有一个线程写,安全
}

// 危险:所有线程都写同一位置
c[0] = a[index] + b[index];  // ❌ 数据竞争!

3. 边界检查

c 复制代码
for (int i = index; i < n; i += stride) {  // ✅ 检查边界
    c[i] = a[i] + b[i];
}

如果不检查 i < n,越界访问会导致未定义行为。

🎯 启动配置要求

c 复制代码
int threads_per_block = 256;  // 通常选32的倍数(warp大小)
int blocks_per_grid = (n + threads_per_block - 1) / threads_per_block;

vector_add_gpu<<<blocks_per_grid, threads_per_block>>>(d_a, d_b, d_c, n);

限制:

  • 每个block最多1024线程(CUDA限制)
  • block数量可以很大(最多2³¹-1)

💡 性能优化要点

  1. Coalesced Memory Access:stride模式确保相邻线程访问相邻地址
  2. Occupancy:每个SM(流多处理器)的活跃线程数影响性能
  3. 同步 :如果需要在block内同步,使用__syncthreads()

这个设计模式被称为**"grid-stride loop"**,是CUDA编程的最佳实践,兼具灵活性(适配任意数组大小)和性能(合并内存访问)。

相关推荐
C++ 老炮儿的技术栈32 分钟前
C++ const 的3个核心作用
服务器·c语言·c++·算法·c
Escalating_xu12 小时前
【C 语言】数据在内存中的存储:补码、大小端、整型陷阱与 IEEE 754 全解析
java·c语言·网络
玩AI的奶茶19 小时前
从零到跑通:在算家云上部署大模型的完整操作手册(实例创建 / 镜像选择 / 远程连接 / 成本控制)
人工智能·ai·gpu算力·token·算力租赁
神仙别闹20 小时前
基于C语言开发的捕捉流星小游戏
c语言·开发语言
水饺编程21 小时前
第1章,[Win32 章节]:API 及 内存管理模式
c语言·c++·windows·visual studio
Logic10121 小时前
C语言/数据结构位运算题解:异或XOR找出优秀项目组中的“独特编号“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
彦21 小时前
C和C++笔记
c语言·c++·笔记
智星云算力1 天前
哪个GPU平台用起来表现更好?从一张展览主视觉,看智星云、腾讯云HAI与火山引擎的差别
gpu算力·gpu租用·gpu使用教程
Code_Solitude1 天前
反码,补码,原码
c语言
FLJwu1 天前
运动相机按键与连接器硬件选型实战02:USB‑C接口选型|防水失效、虚接断连、腐蚀烧口量产大坑
c语言·开发语言·经验分享·数码相机·相机·运动相机·oem 量产