[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编程的最佳实践,兼具灵活性(适配任意数组大小)和性能(合并内存访问)。

相关推荐
炸膛坦客6 小时前
单片机/C/C++八股:(二十六)IIC 专题(I²C)---- 上集
c语言·c++·单片机
Smoothcloud润云9 小时前
国内GPU算力租赁平台横向测评:资源、成本、稳定性三维对比
人工智能·ai·云计算·gpu算力·gpu
程序员Rock13 小时前
上位机开发-MODBUS面试常见问题
c语言·c++·面试·职场和发展·上位机
blevoice18 小时前
抖抖燃脂机单 AC6966B 主控调试踩坑:Type-C 整机供电喇叭无声排障
c语言·开发语言·单片机
小小晓.18 小时前
C++小白记:C风格字符串和数组用法
c语言·开发语言·c++
深念Y19 小时前
开发者如何清理和迁移C盘堆积的垃圾
c语言·开发语言
十月的皮皮19 小时前
STM32从零到量产开发:四路继电器工业控制模块开发实战笔记(小白视角)
c语言·笔记·stm32·stm32cubemx·hal库
不正经学生19 小时前
C 语言函数深入剖析(基础篇)—— 从零理解函数的每一个细节
c语言·开发语言·数据结构·算法·c#
天空'之城21 小时前
C 语言工业级通用组件手写 10:字节序转换
c语言·字节序·大小端·嵌入式通信·工业级组件
C++ 老炮儿的技术栈1 天前
OpenCV 实现社保卡透视矫正:Hough 直线提取四角并转正证件图像
c语言·c++·人工智能·opencv·算法·计算机视觉·c