ubuntu22.04+cuda11.5+gcc11.4第一个cuda程序示例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

VisualStudio 2019是微软的集成开发环境(IDE),通常在Windows操作系统上使用。然而,并不直接支持在Linux上安装。如果想在Ubuntu上进行开发,可以考虑以下几个选项:
使用替代的IDE或文本编辑器: Ubuntu上有许多适用于C++等编程语言的开发工具,比如

Visual Studio Code、 Code.:Blocks、 Eclipse等。

使用虚拟机或容器: 如果你非常需要使用Visual Studio 2019,可以在Ubuntu上安装虚拟机软件(比如VirtualBox) 或容器平台(比如Docker) ,然后在虚拟机或容器中运行Windows,再在其中安装Visual Studio 2019。

使用Wine: Wine是一款允许在Linux上运行Windows应用程序的兼容层。你可以尝试使用Wine来安装和运行Visual Studio 2019,但请注意,并不是所有Windows应用程序都能在Wine下正常工作作。

使用双系统: 如果你需要长期在Linux上开发,但又需要使用VisualStudio 2019,可以考虑在计算机上设置双系统,一边运行Ubuntu,一边运行Windows

使用nano 文本编辑器创建文件,然后在终端中进行运行代码

在这里插入图片描述

gcc cuda example.cu -o cuda example/usr/bin/ld:cuda_example.cu: file format not recognized; treating as linker script/usr/bin/ld:cuda example.cu:5: syntax errorcollecto: error:ld returned i exit status

类似于上述错误信息,说明使用
~gcc尝试编译CUDA代码文件cuda_example.cu’,

~gcc无法直接编译CUDA代码。
CUDA代码需要使用NVIDIA的nvcc~编译器进行编译,因为它包含了CUDA特定的语法和功能。
编译运行CUDA代码,您需要使用NVIDIA的~nvce”编译器,而不是gcc”。

利用nvcc编译运行代码的步骤

示例代码如下

//本示例演示了如何使用 OpenMP API 为多个 GPU 编写应用程序
#include <iostream>
#include <omp.h>
#include <stdio.h>  // 使用 stdio 函数,因为 C++ 流不一定是线程安全的
#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)

inline void __checkCudaErrors(cudaError_t err, const char *file, int line) {
    if (err != cudaSuccess) {
        std::cerr << "CUDA error at " << file << ":" << line << " - " << cudaGetErrorString(err) << std::endl;
        exit(EXIT_FAILURE);
    }
}

using namespace std;

//一个简单的kernel,只需将每个数组元素递增 b
__global__ void kernelAddConstant(int *g_a, const int b)
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    g_a[idx] += b;
}

//用于检查每个数组元素是否被设置为其索引加上 b
int correctResult(int *data, const int n, const int b)
{
    for (int i = 0; i < n; i++)
        if (data[i] != i + b)
            return 0;

    return 1;
}
int main(int argc, char *argv[])
{
    int num_gpus = 0;   // CUDA GPU 数量

    printf("%s Starting...\n\n", argv[0]);

    /
   //确定支持 CUDA 的 GPU 数量
    cudaGetDeviceCount(&num_gpus);

    if (num_gpus < 1)
    {
        printf("no CUDA capable devices were detected\n");
        return 1;
    }

    /
    //显示 CPU 和 GPU 配置
    printf("number of host CPUs:\t%d\n", omp_get_num_procs());
    printf("number of CUDA devices:\t%d\n", num_gpus);

    for (int i = 0; i < num_gpus; i++)
    {
        cudaDeviceProp dprop;
        cudaGetDeviceProperties(&dprop, i);
        printf("   %d: %s\n", i, dprop.name);
    }
    printf("---------------------------\n");

    /
    // initialize data
    //
    unsigned int n = num_gpus * 8192;
    unsigned int nbytes = n * sizeof(int);
    int *a = 0;     // // 指向 CPU 上数据的指针
    int b = 3;      // 数组递增的值
    a = (int *)malloc(nbytes);

    if (0 == a)
    {
        printf("couldn't allocate CPU memory\n");
        return 1;
    }

    for (unsigned int i = 0; i < n; i++)
        a[i] = i;


    
// 运行与 CUDA 设备数量相同的 CPU 线程
    //每个 CPU 线程控制不同的设备,处理各自的数据部分。 
    // 使用的 CPU 线程数量有可能多于 CUDA 设备的数量,在这种情况下,多个 CPU 线程将在同一设备上分配资源并启动内核。
    // 例如,尝试 omp_set_num_threads(2 * num_gpus); 
    // 请注意,在 "omparallel "作用域内声明的所有变量都是 是每个 CPU 线程的局部变量
    //
    omp_set_num_threads(num_gpus);  //创建与 CUDA 设备数量相同的 CPU 线程
    //omp_set_num_threads(2*num_gpus);// 创建的 CPU 线程数量是 CUDA 设备数量的两倍
    #pragma omp parallel
    {
        unsigned int cpu_thread_id = omp_get_thread_num();
        unsigned int num_cpu_threads = omp_get_num_threads();

      
        int gpu_id = -1;
        checkCudaErrors(cudaSetDevice(cpu_thread_id % num_gpus));   
        checkCudaErrors(cudaGetDevice(&gpu_id));
        printf("CPU thread %d (of %d) uses CUDA device %d\n", cpu_thread_id, num_cpu_threads, gpu_id);

        int *d_a = 0; 
        int *sub_a = a + cpu_thread_id * n / num_cpu_threads;  
        unsigned int nbytes_per_kernel = nbytes / num_cpu_threads;
        dim3 gpu_threads(128);  // 128 threads per block
        dim3 gpu_blocks(n / (gpu_threads.x * num_cpu_threads));

        checkCudaErrors(cudaMalloc((void **)&d_a, nbytes_per_kernel));
        checkCudaErrors(cudaMemset(d_a, 0, nbytes_per_kernel));
        checkCudaErrors(cudaMemcpy(d_a, sub_a, nbytes_per_kernel, cudaMemcpyHostToDevice));
        kernelAddConstant<<<gpu_blocks, gpu_threads>>>(d_a, b);

         checkCudaErrors(cudaMemcpy(sub_a, d_a, nbytes_per_kernel, cudaMemcpyDeviceToHost));
        checkCudaErrors(cudaFree(d_a));
    }
    printf("---------------------------\n");

    if (cudaSuccess != cudaGetLastError())
        printf("%s\n", cudaGetErrorString(cudaGetLastError()));
    
    // check the result
    //
    bool bResult = correctResult(a, n, b);

    if (a)
        free(a); // free CPU memory

    exit(bResult ? EXIT_SUCCESS : EXIT_FAILURE);
}

  1. 打开终端(快捷键,ctrl+alt+t)
  2. 进入CUDA代码文件所在的目录,即 ‘/home/lcr/try’
cd /home/lcr/try

3.使用nvcc编译CUDA代码文件,并生成可执行文件。运行以下命令:

nvcc -o example example.cu

nvcc命令中的-o选项用于指定输出文件名,而不是输入文件名。 以下是编译名为 example.cu 的 CUDA 源文件并在特定目录中 生成名为 example 的可执行文件

确保 -o 和输出文件路径之间包含空格。 该命令还应包含源代码文件 (example.cu) 和所需输出可执行文件 (/home/user/lcr/example) 的正确路径。

如果是用open mp

user@user-SYS-420GP-TNR:~/lcr/try$ nvcc -o example example.cu
/usr/bin/ld: /tmp/tmpxft_0015851b_00000000-11_example.o: in function `main':
tmpxft_0015851b_00000000-6_example.cudafe1.cpp:(.text+0xfa): undefined reference to `omp_get_num_procs'
/usr/bin/ld: tmpxft_0015851b_00000000-6_example.cudafe1.cpp:(.text+0x24f): undefined reference to `omp_set_num_threads'
/usr/bin/ld: tmpxft_0015851b_00000000-6_example.cudafe1.cpp:(.text+0x254): undefined reference to `omp_get_thread_num'
/usr/bin/ld: tmpxft_0015851b_00000000-6_example.cudafe1.cpp:(.text+0x25f): undefined reference to `omp_get_num_threads'
collect2: error: ld returned 1 exit status

链接过程中找不到 omp_get_num_procs、omp_set_num_threads、omp_get_thread_num、omp_get_num_threads 这些函数的定义。这些函数属于 OpenMP 库,用于并行编程。在代码中可能使用了 OpenMP 的相关函数,但链接时找不到其定义。
解决方法是,需要在链接时显式地添加 OpenMP 库。在使用 nvcc 进行链接时,可以通过 -Xcompiler 选项将链接参数传递给底层编译器(通常是 g++),以链接 OpenMP 库。
尝试使用以下命令编译链接 CUDA C++ 代码,并添加 OpenMP 支持:

nvcc -o example example.cu -Xcompiler -fopenmp

这个命令会将 -fopenmp 选项传递给底层编译器,以链接 OpenMP 库
4. 运行生成的可执行文件

./example

运行结果如下所示
在这里插入图片描述

如果你使用 nvcc -o example example.cu -Xcompiler -fopenmp 成功编译了你的程序,但在运行时没有反应,可能有几个原因导致这种情况。以下是一些可能的解决方法:

  1. 检查代码逻辑和输出:首先,请确保你的 CUDA C++ 代码本身没有逻辑问题。你可以在代码中添加一些输出语句,以便你在运行程序时能够看到一些信息,帮助你确定程序是否在运行。

  2. 命令行运行:尝试在终端中运行你的可执行文件 example,看看是否有任何输出或错误消息。在终端中输入以下命令:
    ./example
    如果程序需要输入参数,确保你提供了正确的参数。

  3. 查看CUDA运行时错误:在运行程序时,CUDA 运行时错误可能会导致程序崩溃或没有输出。你可以在终端中运行以下命令来检查 CUDA 运行时错误:

cuda-memcheck ./example

已经退出.cu所在文件下,为了确保使用正确的可执行文件路径来运行 cuda-memcheck。如果可执行文件名是 example,那么可以使用绝对路径来运行

cuda-memcheck /home/user/lcr/try/example

./home/user/lcr/try/ .是example文件的位置路径
在这里插入图片描述

需要注意的是,cuda-memcheck 工具已经被官方标记为过时,推荐使用 compute-sanitizer 工具作为替代。

compute-sanitizer

是 NVIDIA 提供的一个工具,用于检测 CUDA 应用程序中的内存问题、数据竞争等。下面是如何在 example.cu 文件上使用 compute-sanitizer 工具的基本步骤

  1. 确保已经安装了 CUDA Toolkit 12.0 或更高版本,因为 compute-sanitizer 是在这些版本中引入的。

  2. 打开终端,并切换到包含 example.cu 文件的目录。

  3. 使用以下命令编译 example.cu 文件,并在编译选项中添加 -fsanitize=compute 标志:

nvcc -o example example.cu -Xcompiler -fopenmp -fsanitize=compute

编译完成后,运行生成的可执行文件 ./example
如果在程序中存在 CUDA 相关的问题,compute-sanitizer 会在运行时检测并报告这些问题。
需要注意的是,compute-sanitizer 的使用方式可能会因为不同的 CUDA 版本和工具包而有所不同,具体的细节可以参考 NVIDIA 的官方文档或资源。在使用前,建议查阅相关文档以了解更多详细信息。

在example 可执行文件上使用 compute-sanitizer 工具的步骤:

  1. 确保已经安装了 CUDA Toolkit 12.0 或更高版本,因为 compute-sanitizer 是在这些版本中引入的。

  2. 打开终端,并切换到包含 example 可执行文件的目录。

使用以下命令运行 compute-sanitizer 工具,将你的 example 可执行文件作为参数传递:

compute-sanitizer ./example

compute-sanitizer 将会在运行时检测可执行文件中的 CUDA 相关问题,并输出相应的报告。

一些问题

使用以下命令来查找 CUDA 的安装路径
find / -name libsanitizer-collection.so

将在整个系统中搜索 libsanitizer-collection.so 文件,一旦找到,就可以使用找到的路径来设置环境变量或者直接使用 compute-sanitizer 工具。请确保CUDA 安装已经正确完成,并且路径和文件都存在

找不到 libsanitizer-collection.so 文件。这可能是因为文件不存在或者路径不正确。

以下命令来查看 libsanitizer-collection.so 文件是否存在:

ls /usr/local/cuda/extras/CUPTI/lib64/libsanitizer-collection.so

如果上述库文件存在,尝试将其目录添加到 LD_LIBRARY_PATH 环境变量中。在终端中运行以下命令:

export LD_LIBRARY_PATH=/usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH

然后再次尝试运行 compute-sanitizer

find /usr/local/cuda -name libsanitizer-collection.so

如果文件存在,你可以将路径添加到 LD_LIBRARY_PATH 环境变量中,然后尝试运行 compute-sanitizer。如果文件确实不存在,可能需要检查 CUDA 和 CUPTI 的安装是否完整。

vscode

cuda c++(cuda-gdb),cuda c++(cuda-gdbserver),cuda c++ qnx(cuda-gdbserver),各自是做什么的,有什么作用,如何使用

CUDA C++(cuda-gdb):
CUDA C++ 是 C++ 编程语言的扩展,允许开发人员编写可在传统 CPU 和 NVIDIA GPU 上执行的代码。 CUDA C++ 提供将计算卸载到 GPU 并利用并行性的功能。 cuda-gdb 是 CUDA 程序的 NVIDIA 调试器。 它允许您通过提供断点、观察点、单步执行代码、检查变量和分析内存使用情况等功能在 GPU 上调试 CUDA C++ 程序。

CUDA C++(cuda-gdbserver):
cuda-gdbserver 是用于远程调试场景的实用程序。 它允许您从单独的主机调试目标设备(例如嵌入式系统或具有 GPU 的远程计算机)上的 CUDA 应用程序。 cuda-gdbserver 在目标设备上运行,并与主机上的 cuda-gdb 通信,使您能够远程调试 CUDA 应用程序。

CUDA C++ QNX(cuda-gdbserver):
cuda-gdbserver for QNX 是 cuda-gdbserver 实用程序的一个版本,专门设计用于与 QNX 操作系统配合使用。 QNX 常用于嵌入式系统。 与标准 cuda-gdbserver 类似,该版本允许在 QNX 目标设备上远程调试 CUDA 应用程序。

如何使用:

CUDA C++(cuda-gdb):

安装 cuda-gdb 作为 CUDA 工具包的一部分。
使用 nvcc 编译 CUDA C++ 程序。
使用 cuda-gdb 命令,后跟编译的二进制文件来启动调试器:cuda-gdb my_program。
使用 cuda-gdb 命令设置断点、运行程序、检查变量并分析内存使用情况。

CUDA C++(cuda-gdbserver):

在目标设备上,安装并运行 cuda-gdbserver。
在主机上,使用 nvcc 编译 CUDA C++ 程序。
使用带有 --remote 标志的 cuda-gdb 命令连接到远程 cuda-gdbserver:cuda-gdb --remote=target:port my_program。
调试远程 CUDA 程序,就像它在主机上运行一样。

CUDA C++ QNX(cuda-gdbserver):

请遵循与 cuda-gdbserver 相同的步骤,但请确保您拥有与 QNX 目标设备兼容的 QNX 版本的 cuda-gdbserver。
使用带有 --remote 标志的 cuda-gdb 命令,指定 QNX 目标:cuda-gdb --remote=qnx:port my_program。
请记住,使用 cuda-gdb 和 cuda-gdbserver 等调试工具需要熟悉调试概念和 CUDA 编程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值