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

相关推荐
强哥聊算力1 小时前
《算力江湖·Intel帝国往事》第三集:安迪·格鲁夫的铁血时代:Intel为什么能把对手逼到墙角?
运维·人工智能·科技·算法·ai·gpu算力
二十七日秋3 小时前
算法刷题笔记「Day 1|四道 OJ 入门题里的深坑」
c语言·汇编·数据结构·c++·笔记·python·算法
abc light4 小时前
ARM 裸机开发(1)——ARM 体系结构
c语言·arm开发
Navigator_Z4 小时前
LeetCode //C - 1309. Decrypt String from Alphabet to Integer Mapping
c语言·算法·leetcode
爱聊灵光的维C5 小时前
C语言初阶:关于函数设计的详细讲解和注意事项
c语言·开发语言·青少年编程·蓝桥杯·学习方法
wuminyu6 小时前
JVM规范学习总结:class执行性能差异
java·linux·c语言·jvm·c++
智星云算力6 小时前
GPU平台怎么选才跑得快?智星云、AutoDL、阿里云与腾讯云,先比较数据能不能跟上
gpu算力·gpu算力服务器·gpu算力选型
C++ 老炮儿的技术栈6 小时前
Linux 动态库踩坑:明明重新编译了 .so,调用函数返回值却一直不变(libfjrobot.so 版本号异常排查实录)
linux·c语言·网络·c++·visual studio
小向是个Der15 小时前
嵌入式进阶——MCU上电后的动作
c语言·单片机·mcu·学习·链接脚本
bkspiderx18 小时前
基于Socket的广播通信及C语言实现详解
c语言·开发语言·socket·广播·socket的广播通信