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

CUDA的编译与调试

一、手动编译 CUDA 程序:理解nvcc关键参数

为同时满足 “环境验证” 和 “后续调试” 需求,需用nvcc调试模式编译程序。以下是详细拆解:

1.1 核心编译命令

编译hello_world.cu的命令如下

nvcc -g -G -O0 -o hello ./hello_world.cu
参数解析表
参数作用说明
-g生成主机端(CPU)代码的调试信息,确保gdb能识别 CPU 代码的行号、变量
-G生成设备端(GPU)代码的调试信息(关键!无此参数无法调试 CUDA 核函数)
-O0关闭所有代码优化(优化会导致行号与执行顺序不匹配,破坏调试准确性)
-o hello指定输出的可执行文件名为hello
源文件路径输入的 CUDA 源文件(此处为./hello_world.cu

示例代码

#include <cuda_runtime.h>  // CUDA运行时核心头文件
#include <iostream>// __global__:CUDA核函数修饰符(仅在GPU上执行,CPU可调用)
__global__ void hello_world(void) {// GPU线程打印“块ID”和“线程ID”printf("block idx:%d thread idx: %d\n", blockIdx.x, threadIdx.x);// 仅线程ID为0的GPU线程打印问候语if (threadIdx.x == 0) {printf("GPU: Hello world!\n");}
}int main(int argc, char **argv) {// CPU主线程先打印问候语printf("CPU: Hello world!\n");// 启动CUDA核函数:<<<网格维度, 块维度>>>hello_world<<<1, 1>>>();  // 配置:1个块(block)、每个块1个线程(thread)// 同步GPU与CPU:等待GPU核函数执行完,再继续执行CPU代码cudaDeviceSynchronize();// 检查CUDA调用是否出错(调试必备的错误检查)if (cudaGetLastError() != cudaSuccess) {std::cerr << "CUDA error: " << cudaGetErrorString(cudaGetLastError())<< std::endl;return 1;} else {std::cout << "GPU: Hello world finished!" << std::endl;}std::cout << "CPU: Hello world finished!" << std::endl;return 0;
}
  • __global__:核函数的专属标识,必须添加此修饰符才能在 GPU 上执行;
  • <<<1, 1>>>:核函数启动配置,第一个参数是 “网格维度”(块的数量),第二个是 “块维度”(每个块的线程数量);
  • cudaDeviceSynchronize():避免 GPU 未执行完时 CPU 已退出,导致 GPU 输出缺失。

二、cuda-gdb调试:定位 GPU 代码问题

当需要调试 GPU 核函数时,cuda-gdb是 NVIDIA 官方提供的专用工具。以下基于已编译的 “调试版本” 程序,演示核心调试步骤:

2.1 启动cuda-gdb

首先加载可执行文件,进入调试交互界面:

cuda-gdb ./hello

启动成功后会显示如下提示(表示调试信息已加载):

(base) test_fss@node4:~/code/cuda_code$ cuda-gdb ./hello 
...
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./hello...
(cuda-gdb)  # 此处等待输入调试命令

2.2 查看源代码(list命令)

list 行号(简写l 行号)查看指定行附近的代码,例如查看第 5 行(核函数内部):

(cuda-gdb) l 5
1       #include <cuda_runtime.h>
2
3       #include <iostream>
4       __global__ void hello_world(void) {
5         printf("block idx:%d thread idx: %d\n", blockIdx.x, threadIdx.x);
6         if (threadIdx.x == 0) {
7           printf("GPU: Hello world!\n");
8         }
9       }
10

通过该命令可确认:调试信息已正确关联源代码,行号匹配无误。

2.3 设置断点(break命令)

在需要暂停的代码行设置断点,例如在第 5 行(核函数的打印语句处):

(cuda-gdb) b 5
Breakpoint 1 at 0x8eaa: file ./hello_world.cu, line 5.

b 行号会在指定行的开头设置断点,程序运行到此处会自动暂停。

2.4 运行程序并触发断点(run命令)

输入run(简写r)启动程序,执行到断点时会暂停:

(cuda-gdb) run
Starting program: /home/test_fss/code/cuda_code/hello 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
CPU: Hello world!  # CPU代码执行完成,即将启动GPU核函数
[New Thread 0x7ffff21c0000 (LWP 3908127)]  # GPU相关线程启动
[New Thread 0x7ffff0f9c000 (LWP 3908128)]
[Detaching after fork from child process 3908129]
[New Thread 0x7fffe899c000 (LWP 3908268)]
[New Thread 0x7fffd855a000 (LWP 3908269)]
# 切换到GPU线程上下文,暂停在第5行断点
[Switching focus to CUDA kernel 0, grid 1, block (0,0,0), thread (0,0,0), device 0, sm 0, warp 0, lane 0]CUDA thread hit Breakpoint 1, hello_world<<<(1,1,1),(1,1,1)>>> () at course1/hello_world.cu:5
5         printf("block idx:%d thread idx: %d\n", blockIdx.x, threadIdx.x);

关键提示:Switching focus to CUDA kernel...说明cuda-gdb已成功切换到 GPU 核函数的线程上下文,当前暂停在块 (0,0,0)、线程 (0,0,0) 。

2.5 查看核函数配置(info cuda kernels

通过该命令可查看当前运行的 CUDA 核函数的 “块 / 线程配置”:

(cuda-gdb) info cuda kernelsKernel Parent Dev Grid Status                 SMs Mask GridDim BlockDim Invocation    
*      0      -   0    1 Active 0x0000000000000000000001 (1,1,1)  (1,1,1) hello_world()

字段解释:

  • GridDim (1,1,1):网格维度为 1(仅 1 个块);
  • BlockDim (1,1,1):块维度为 1(每个块仅 1 个线程);
  • Invocation hello_world():当前运行的核函数是hello_world

注意:若需切换到第 6 个线程(如用户示例),需在核函数启动时配置更多线程(如hello_world<<<1, 10>>>),此时BlockDim会变为(10,1,1),才能切换到thread (6,0,0)

2.6 切换 GPU 线程(cuda block/thread命令)

若核函数配置了多个线程,可通过cuda block (x,y,z) thread (x,y,z)切换到指定线程。例如切换到块 (0,0,0)、线程 (6,0,0) :

(cuda-gdb) cuda block (0,0,0) thread (6,0,0)
[Switching focus to CUDA kernel 0, grid 1, block (0,0,0), thread (6,0,0), device 0, sm 0, warp 0, lane 6]
5         printf("block idx:%d thread idx: %d\n", blockIdx.x, threadIdx.x);

切换后,cuda-gdb的焦点会转移到指定线程,后续调试操作仅针对该线程。

2.7 查看变量值(print命令)

print(简写p)查看当前线程的blockIdx.x(块 ID)和threadIdx.x(线程 ID),验证线程切换是否成功:

(cuda-gdb) p blockIdx.x
$1 = 0  # 当前块ID为0(与切换命令一致)
(cuda-gdb) p threadIdx.x
$2 = 6  # 当前线程ID为6(切换成功)

http://www.dtcms.com/a/353938.html

相关文章:

  • Mac 上录制视频有几种常见方式
  • 基于springboot的校园资料分享平台(源码+论文+PPT答辩)
  • 网络安全监控中心
  • 【笔记】Windows 安装 Triton 的工作记录(之二)
  • IDR的RWA金融逻辑RWA:全球金融革命的底层协议
  • 数学建模——马尔科夫链(Markov Chain Model)
  • 集成学习之 Stacking(堆叠集成)
  • django配置多个app使用同一个static静态文件目录
  • 使用openCV(C ++ / Python)的Alpha混合
  • 【高级机器学习】 2. Loss Functions(损失函数)
  • 一、快速掌握Python 中的文件操作知识体系
  • mysql zip包安装步骤
  • 2025(秋)中国国际健康产业(成都)博览会:探索健康未来辉煌
  • TCP 并发服务器构建
  • 场外期权能做套利吗?
  • 二叉树的工程实践与高频问题(续):从LeetCode真题到系统设计的深度剖析
  • centos7 安装指定版本的fastfds
  • 了解CDC(变更数据捕获)如何革新数据集成方式
  • Linux 系统调优工具与实践指南
  • 个人博客系统系统---测试报告
  • HarmonyOS布局实战:用声明式UI构建自适应电商卡片
  • 【源码分析】@vue/runtime-dom/src/apiCustomElement.ts 解析
  • 重磅升级,Pixso 2.0赋能HarmonyOS应用设计与开发
  • 安卓11 12系统修改定制化_____如何修改固件 实现给指定内置的应用无障碍权限
  • Sybase 安装与备份
  • 【c++】超好玩游戏
  • 一、CSS3 新增选择器(非 “属性”,但为核心基础)
  • day082-初识ElasticStack
  • 路由基础(二):路由表和FIB表
  • Ansible文件管理与Jinja2模板