学习任何一门语言,如何有效进行调试都是至关重要的一环。最近在学习cuda编程,自然cuda调试是我首先要掌握的基本技能。首先按照“star“原则介绍我的步骤:
情境(Situation)
一台window台式机,win7系统,硬件参数如下:
一台装有centos系统的服务器,只罗列gpu相关的参数:
任务(Task)
使用ide调试向量相加的cuda程序(cuda-gdb太不直观了!!!)
行动(Action)
一,安装以下软件:
1window端安装vscode最新版(别问什么版本,就是最新版!)
2.linux端安装cuda
3.linux端安装cmake
4.vscode安装c++插件和nsight插件,如下图所示:
这里补充一下:
vscode是一个集成开发环境,不限制语言,你可以调试python,c++等任何语言,前提是你得安装对应的插件以及插件对应的软件,比如,安装C/C++插件之前你得确保你安装了gcc/g++,同理,安装cmake插件之前你得保证先安装了cmake,安装nsight插件之前你得安装了cuda-toolkit。vscode只负责安装插件,不负责安装对应的软件,这是要分清楚的!
二,准备调试相关文件
1.一个极具代表性的“向量相加“cuda程序main.cu:
#include </usr/local/cuda-10.1/targets/x86_64-linux/include/cuda_runtime.h>
#include <stdio.h>
__global__ void addKernel( int* c, const int* a, const int* b )
{
int i = threadIdx.x;
c[i]= a[i] + b[i];
}
cudaError_t CUDA_Add( const int* a, const int* b, int* out, int size )
{
int*dev_a;
int*dev_b;
int*dev_c;
//1、设置设备
cudaError_t cudaStatus = cudaSetDevice( 0 );
switch( true )
{
default:
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "调用cudaSetDevice()函数失败!" );
return cudaStatus;
}
//2、分配显存空间
cudaStatus= cudaMalloc( (void**)&dev_a, size * sizeof(int) );
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "调用cudaMalloc()函数初始化显卡中a数组时失败!" );
break;
}
cudaStatus= cudaMalloc( (void**)&dev_b, size * sizeof(int) );
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "调用cudaMalloc()函数初始化显卡中b数组时失败!" );
break;
}
cudaStatus= cudaMalloc( (void**)&dev_c, size * sizeof(int) );
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "调用cudaMalloc()函数初始化显卡中c数组时失败!" );
break;
}
//3、将宿主程序数据复制到显存中
cudaStatus= cudaMemcpy( dev_a, a, size * sizeof( int ), cudaMemcpyHostToDevice );
if( cudaStatus != cudaSuccess )
{
fprintf( stderr, "调用cudaMemcpy()函数初始化宿主程序数据a数组到显卡时失败!");
break;
}
cudaStatus= cudaMemcpy( dev_b, b, size * sizeof( int ), cudaMemcpyHostToDevice );
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "调用cudaMemcpy()函数初始化宿主程序数据b数组到显卡时失败!" );
break;
}
//4、执行程序,宿主程序等待显卡执行完毕
addKernel<<<1,size>>>( dev_c, dev_a, dev_b );
//5、查询内核初始化的时候是否出错
cudaStatus= cudaGetLastError( );
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "显卡执行程序时失败!" );
break;
}
//6、与内核同步等待执行完毕
cudaStatus= cudaDeviceSynchronize( );
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "在与内核同步的过程中发生问题!" );
break;
}
//7、获取数据
cudaStatus= cudaMemcpy( out, dev_c, size * sizeof( int ), cudaMemcpyDeviceToHost );
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "在将结果数据从显卡复制到宿主程序中失败!" );
break;
}
}
cudaFree(dev_c );
cudaFree(dev_a );
cudaFree(dev_b );
return cudaStatus;
}
int main( int argc, char** argv )
{
const int arraySize = 5;
const int a[arraySize] = { 1, 2, 3, 4, 5 };
const int b[arraySize] = { 10, 20, 30, 40, 50 };
int c[arraySize] = { 0 };
cudaError_t cudaStatus;
cudaStatus= CUDA_Add( a, b, c, arraySize );
printf("运算结果是:\nc数组[%d, %d, %d, %d, %d]\n",
c[0],c[1], c[2], c[3], c[4] );
cudaStatus= cudaDeviceReset( );
if( cudaStatus != cudaSuccess )
{
fprintf(stderr, "调用cudaDeviceReset()函数失败!" );
return 1;
}
return 0;
}
2,一个CMakeLists.txt文件
# 设置最低cmake版本要求
cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR)
#支持 pkg-config
#include(FindPkgConfig)
#检查 libcurl cairo 等模块, 并获取它们的LIBS和INCLUDE, 结果保存到变量 LB_LIBS_XXX 中
# LB_LIBS_INCLUDE_DIRS # 保存包含路径
# LB_LIBS_LIBRARIES # 保存连接库
#pkg_check_modules(LB_LIBS REQUIRED libcurl cairo)
# 设置项目名称
project(myproject LANGUAGES CXX CUDA)
# 检查编译目录和源码是否为同一目录, 如果是则提示错误
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message(FATAL_ERROR "Do not build in-source.\nPlease remove CMakeCache.txt and the CMakeFiles/ directory.\nThen: mkdir build ; cd build ; cmake .. ; make")
endif()
if( CMAKE_BUILD_TYPE STREQUAL "Debug" )
add_definitions(-DNODEBUG)
else()
add_definitions(-DDEBUG)# 添加宏定义
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -O0 -ggdb")
#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ggdb")
# 设置CMAKE_C_FLAGS, `${CMAKE_C_FLAGS}` 可取出CMAKE_C_FLAGS的值
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -O0 -ggdb")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb")
endif()
set(CMAKE_CXX_STANDARD 11)
# 设置变量 PROJECT_NAME 并初始化, 使用 ${XXX} 引用, 如: ${PROJECT_NAME}
#set(PROJECT_NAME "xxx")
# 添加include目录(-l)
#include_directories(src)
# include_directories(src)
# 设置 XXX_TESTS 为 OFF
#set(XXX_TESTS OFF CACHE BOOL "")
# 添加子目录, 子目录要包含CMakeLists.txt文件
# add_subdirectory(src)
# 添加可执行文件 example 的生成规则, 后面接依赖文件或者文件列表
add_executable(myproject main.cu)
# 设置可执行文件 example 的链接库, 后面接的可以是系统库, 也可以是 子目录下的自定义库
# target_link_libraries(myproject lib)
# 检测是否配置了编译类型[Release|Debug], 如果没有配置则配置为Release
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING
"Choose the type of build, options are: Debug Profile Release Asan Ubsan." FORCE)
endif(NOT CMAKE_BUILD_TYPE)
3.一个tasks.json文件
{
"tasks": [
{
"type": "shell",
"label": "C/C++: g++ build active file",
"command": "mkdir build -p && cd build && cmake -DCMAKE_BUILD_TYPE=debug .. && make -j4",
//"command": "mkdir build -p && cd build && cmake -DCMAKE_BUILD_TYPE=debug .. && make -j4",
"args": [],
"options": {
"cwd": "${workspaceFolder}"
},
"detail": "Task generated by Debugger.",
"problemMatcher":{
"$nvcc"
}
}
],
"version": "2.0.0"
}
截图如下:
4 .一个launch.json文件
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "CUDA C++: Launch",
"type": "cuda-gdb",
"request": "launch",
"program": "${workspaceFolder}/build/myproject",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "cuda-gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: g++ build active file",
"miDebuggerPath": "/usr/local/cuda-10.1/doc/html/cuda-gdb"
},
]
}
截图如下:
整个工程鸟图如下:
5.远程连接服务器
点击左边的设置按钮,出现右边的选项,选择第一个文件,配置如下:
然后再点击左侧的 按钮, 此时选择你的工程所在目录即可,输入密码后就可以远程连接了
三,开始调试
1.建立目录build
2.直接F5调试即可
结果(Result)
效果如下: