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

【Lesson 3】CUDA 编程模型:线程、块、网格 —— Ubuntu 22.04 + RTX 30/40 系列实战

目录

0. 回顾 & 环境检查

1. 为什么需要线程/块/网格?

2. 内置变量

3. 全局线程 ID 公式

4. 实战 1:向量加法

5. 实战 2:二维图像反转(负片)

6. Nsight Compute 看线程利用率

7. 常见坑 & Debug Tips

8. 小结 & 下节预告


0. 回顾 & 环境检查

Lesson 2 我们跑通了第一个Hello World CUDA。 在继续之前,先确认一下 GPU 信息:

$ nvidia-smi
$ nvcc --version

能看到类似 CUDA Version: 11.8 就 OK。接下来所有代码都在 Ubuntu 22.04 + CUDA 11.x 下验证通过。


1. 为什么需要线程/块/网格?

CPU 上我们用 for(i=0;i<N;i++) 串行干活;GPU 上要并行,就得把 N 个任务拆成 “线程”“线程块”“网格” 三层结构。

层级英文最大尺寸(Ampere)主要作用
线程thread1024/块最小执行单元
线程块block1024×1024×64共享内存、同步
网格grid2^31-1 个块全局索引

一句话:grid 是“整个任务”,block 是“工作组”,thread 是“工人”


2. 内置变量

在核函数里,CUDA 直接给你以下 dim3 变量:

threadIdx.x   threadIdx.y   threadIdx.z
blockIdx.x    blockIdx.y    blockIdx.z
blockDim.x    blockDim.y    blockDim.z
gridDim.x     gridDim.y     gridDim.z

dim3 可以是一维、二维或三维,例如:

dim3 blockSize(16, 16);   // 每块 16×16 = 256 线程
dim3 gridSize(32, 32);    // 网格 32×32 = 1024 块

3. 全局线程 ID 公式

  • threadIdx:线程在 当前 block 中的索引

  • blockIdx:当前线程块在 网格 中的索引

  • blockDim:每个线程块中的线程数量

  • gridDim:网格中线程块的数量

最常见的一维情况:

int globalId = blockIdx.x * blockDim.x + threadIdx.x;

二维图像处理:

int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int idx = y * width + x;

4. 实战 1:向量加法

文件:vec_add.cu

#include <stdio.h>
#include <cuda_runtime.h>
​
__global__ void vecAdd(const float* A, const float* B, float* C, int n)
{int i = blockIdx.x * blockDim.x + threadIdx.x;if (i < n) C[i] = A[i] + B[i];
}
​
int main()
{int n = 1 << 24;               // 16M 元素size_t bytes = n * sizeof(float);
​float *h_A, *h_B, *h_C;cudaMallocHost(&h_A, bytes);cudaMallocHost(&h_B, bytes);cudaMallocHost(&h_C, bytes);
​for (int i = 0; i < n; ++i) { h_A[i] = 1.0f; h_B[i] = 2.0f; }
​float *d_A, *d_B, *d_C;cudaMalloc(&d_A, bytes);cudaMalloc(&d_B, bytes);cudaMalloc(&d_C, bytes);
​cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);
​int threads = 256;int blocks  = (n + threads - 1) / threads;vecAdd<<<blocks, threads>>>(d_A, d_B, d_C, n);
​cudaMemcpy(h_C, d_C, bytes, cudaMemcpyDeviceToHost);
​// 校验float maxError = 0.0f;for (int i = 0; i < n; ++i) maxError = fmax(maxError, fabs(h_C[i] - 3.0f));printf("Max error: %f\n", maxError);
​cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);cudaFreeHost(h_A); cudaFreeHost(h_B); cudaFreeHost(h_C);return 0;
}

编译 & 运行:

$ nvcc -o vec_add vec_add.cu
$ ./vec_add
Max error: 0.000000

5. 实战 2:二维图像反转(负片)

文件:img_negate.cu

#include <opencv2/opencv.hpp>
#include <cuda_runtime.h>
​
__global__ void negateKernel(uchar3* img, int width, int height)
{int x = blockIdx.x * blockDim.x + threadIdx.x;int y = blockIdx.y * blockDim.y + threadIdx.y;if (x >= width || y >= height) return;
​int idx = y * width + x;img[idx].x = 255 - img[idx].x;img[idx].y = 255 - img[idx].y;img[idx].z = 255 - img[idx].z;
}
​
int main()
{cv::Mat h_img = cv::imread("lena.jpg", cv::IMREAD_COLOR);if (h_img.empty()) { printf("Load image failed\n"); return -1; }
​int width = h_img.cols;int height = h_img.rows;uchar3 *d_img;cudaMalloc(&d_img, width * height * sizeof(uchar3));cudaMemcpy(d_img, h_img.data, width * height * sizeof(uchar3), cudaMemcpyHostToDevice);
​dim3 block(16, 16);dim3 grid((width + 15) / 16, (height + 15) / 16);negateKernel<<<grid, block>>>(d_img, width, height);
​cudaMemcpy(h_img.data, d_img, width * height * sizeof(uchar3), cudaMemcpyDeviceToHost);cv::imwrite("lena_neg.jpg", h_img);
​cudaFree(d_img);return 0;
}

编译时需要 OpenCV:

$ sudo apt install libopencv-dev
$ nvcc -o img_negate img_negate.cu `pkg-config --cflags --libs opencv4`

6. Nsight Compute 看线程利用率

$ ncu ./vec_add

ncu 报告里关注:

  • SM 利用率(%)

  • Active Threads / Active Warps

  • Occupancy(理论 vs 实际)


7. 常见坑 & Debug Tips

解决办法
invalid configuration argumentblockDim.x * blockDim.y * blockDim.z > 1024
结果全 0忘了 cudaMemcpy 或 kernel 没 launch
性能差block大小不是 32 的倍数 → warps 没填满
printf 没输出kernel 里用 printf 需要 cudaDeviceSynchronize()

8. 小结 & 下节预告

今天我们学会了:

  • threadIdx/blockIdx 计算全局索引

  • 一维向量加法和二维图像处理

  • 用 Nsight Compute 看线程利用率



文章转载自:

http://Zmt43ExX.wmmjw.cn
http://QkQAzCCw.wmmjw.cn
http://fhrPbfe7.wmmjw.cn
http://hUOAlTXP.wmmjw.cn
http://a5PRUyBP.wmmjw.cn
http://BlaJcEGW.wmmjw.cn
http://Z2VMlHrL.wmmjw.cn
http://zX2elJpD.wmmjw.cn
http://8kx84fFv.wmmjw.cn
http://wFEk1ISb.wmmjw.cn
http://jtfPJuee.wmmjw.cn
http://0EwdgPGO.wmmjw.cn
http://YlmC79hl.wmmjw.cn
http://1CPTESBC.wmmjw.cn
http://JrBHMKFa.wmmjw.cn
http://Zj0soeoE.wmmjw.cn
http://GDzjGVNQ.wmmjw.cn
http://z2zhMZrI.wmmjw.cn
http://oxgfbC0v.wmmjw.cn
http://NSZSJMYb.wmmjw.cn
http://YDuSMbVt.wmmjw.cn
http://1QEyH2XD.wmmjw.cn
http://L6fMfL9C.wmmjw.cn
http://QxWKmux9.wmmjw.cn
http://fcOBTn7s.wmmjw.cn
http://j6zXs6fb.wmmjw.cn
http://KagvxF6n.wmmjw.cn
http://Jp2tbeIj.wmmjw.cn
http://A8NLZjvd.wmmjw.cn
http://Jj0SonDp.wmmjw.cn
http://www.dtcms.com/a/385115.html

相关文章:

  • [Windows]C盘瘦身 --- 软件搬家
  • CLIP 完全上手指南:从安装、下载加速、文本/图像编码到图文匹配,一篇全搞定!
  • BKY莱德因:5大黑科技逆转时光
  • 开源嵌入模型推荐与选型指南
  • 科普:Python中为什么“from .utils” 不能写成 “from ./utils”?
  • 客户粘性提升策略是什么?系统化策略提升客户粘性指南
  • Spring 框架从入门到精通(第一篇)—— 框架核心与 IOC 容器实践
  • 通过DSL生成Jenkins流水线
  • 构建AI智能体:三十四、LangChain SQLDatabaseToolkit终极指南:架构、优势与最佳实践
  • 算法 --- 字符串
  • PDF 文件创建时间属性怎样批量修改详细教程
  • hutool DesensitizedUtil
  • train.py代码学习 自学
  • 安全与效率的平衡术:安全空间
  • 【Unity】事件分发系统的使用示例
  • dinov3 源码 笔记1
  • 飞书项目,再交卷中国智造
  • c++多线程(3)------休眠函数sleep_for和sleep_until
  • 正则表达式 - 元字符
  • RDS-MYSQL,这个RDS是什么?和mysql有什么区别?
  • HarmonyOS事件订阅与通知:后台事件处理
  • 医疗器械飞检常见问题:强生测量系统分析中30%误差的改进方法
  • 可视化数字平台如何重塑未来城市空间?
  • vue防抖节流,全局定义,使用
  • Defender防火墙高级防护配置的部署指南
  • Java——集合
  • AI 重塑制造业:智能质检降本 30%、预测性维护减少停机,传统工厂的 “智改” 路径
  • CKS-CN 考试知识点分享(7) 网络策略 Deny和Allow
  • 已收货数量与已出货数量不一致,不能关闭订单
  • Spring 框架从入门到精通(第二篇)—— 依赖注入(DI)与 AOP 面向切面编程