自学内容网 自学内容网

【C++】CUDA核函数

1. 从C++到CUDA编程

cuda中的Hello World程序

nvcc:

  • 安装CUDA即可使用nvcc
  • nvcc支持纯C++代码的编译
  • 编译扩展名为.cu的CUDA文件
  • 编译CUDA文件命令:nvcc hello.cu -o hello

使用nvcc编译程序时,会将纯粹的C++代码交给C++编译器,自己只处理属于CUDA的代码

// hello.cu (此代码并没有调用GPU设备)
#include <stdio.h>
int main(){
    printf("Hello World\n");
    return 0;
}

注意:在cuda编程中,不能使用cout进行输出,只能使用printf

2. 核函数

核函数简介:

  • 核函数是运行在GPU上的函数,可以并行执行,核函数通过特殊的语法和关键字定义,并且是 GPU 编程的核心。

定义核函数:

  • 必须使用限定词__global__修饰
  • 核函数返回值必须是void
  • 核函数通常以<<<blockNum, threadNum>>>形式来配置执行线程和块

核函数注意事项:

  • 核函数只能访问GPU内存
  • 核函数不能使用变长参数
  • 核函数不能使用静态变量
  • 核函数不能使用函数指针
  • 核函数具有异步性

核函数编写流程:

int main(){
主机代码
核函数调用
主机代码
return 0;
}

代码示例:

#include <stdio.h>

__global__ void hello_from_gpu(){
    printf("Hello World from GPU \n");
}

int main(){
    hello_from_gpu<<<1,6>>>();
    // 同步主机与设备,促使缓冲区刷新,打印信息到终端。该函数会等待GPU设备执行完毕
    cudaDeviceSynchronize();
    return 0;
}

代码解析:

1.核函数hello_from_gpu

  • 核函数通过__global__限定符定义
  • 函数内使用printf 打印 Hello World from GPU

2.核函数启动

  • 在main函数中,hello_from_gpu<<<1,6>>>();启动了核函数
  • 这里的<<<1,6>>>表示启动1个线程块,每个块包含6个线程

3.同步

  • 使用cudaDeviceSynchronize();来同步主机与设备,确保 GPU 上的所有线程执行完毕后,主机程序才继续执行

代码输出:

Hello World from GPU 
Hello World from GPU 
Hello World from GPU 
Hello World from GPU 
Hello World from GPU 
Hello World from GPU 

注意:

由于核函数的执行是异步的,cudaDeviceSynchronize() 负责等待 GPU 执行完毕并确保所有输出都能被打印出来。如果没有这个同步步骤,可能会导致输出内容丢失,或者程序提前结束。

总结:

  1. 通过 __global__修饰符定义的核函数在 GPU 上并行执行。通过 <<<blocks, threads>>> 语法配置并行计算的块和线程数。
  2. 核函数执行是异步的,主机程序不会等待设备代码执行完毕,通常使用 cudaDeviceSynchronize() 来等待 GPU 完成操作。
  3. 在 CUDA 中,printf 用于设备端输出,主机端的 cout 无法使用。

3. 问题与思考

3.1 CUDA编程中.cu文件作用是什么

在CUDA编程中,.cu文件是CUDA源码文件的标准扩展名。CUDA代码分为两部分:一部分运行在主机CPU上,另一部分运行在GPU上。.cu文件用于包含这些代码,并且他们的内容会被nvcc编译器处理

  • 主机代码(C++代码)使用标准C++编译器进行编译
  • 设备代码(CUDA核函数)由nvcc进行编译,生成与CUDA设备兼容的代码

3.2 核函数为什么不能使用变长参数

在 CUDA 编程中,核函数__global__不能使用变长参数,其原因主要与并行计算模型、线程独立性、内存访问模式以及性能优化相关。以下是一些关键点:

  • 线程的独立性:CUDA 的核心设计思想是每个线程应当独立运行,变长参数的使用可能导致每个线程执行的代码路径不同,这破坏了并行计算的规则,使得程序的执行变得不可预测。每个线程的执行应该尽可能相同且高效。
  • 性能问题:使用变长参数会增加运行时的解析和内存分配开销,这可能导致计算瓶颈,影响 CUDA 核函数的高效性。变长参数通常涉及动态内存分配、类型推导等操作,这些都是在编译时无法确定的,会增加 GPU 上并行计算的额外负担。
  • 内存访问冲突: 变长参数的动态性质可能导致线程间不一致的内存访问模式,从而引发内存访问冲突和性能下降。CUDA 要求内存访问尽量是局部和一致的,而变长参数增加了不确定性。
  • 硬件资源的限制: GPU 计算中的每个线程都有其固定的资源(如寄存器、内存等),变长参数的使用可能会导致这些资源的分配不均,影响到计算资源的最大化利用。

3.3 核函数为什么不能使用静态变量

在 CUDA 编程中,核函数不能使用静态变量,原因如下:

  • 线程之间的资源共享问题:
    静态变量通常在程序运行过程中只有一个实例,而核函数的每个线程都会独立执行。如果在核函数中使用静态变量,那么多个线程可能会并发访问同一份数据,这可能导致线程间的同步问题或数据竞争。CUDA 设计要求每个线程都应该独立,不能有共享的状态(除非通过显式的同步机制)。
  • 线程的独立性:
    在 CUDA 中,每个线程都应该是独立的,能够并行执行而不依赖于其他线程的执行状态。静态变量的生命周期通常会跨越多个函数调用,因此可能会导致线程在执行过程中产生意外的相互依赖,从而降低并行执行的效率。
  • 内存管理问题:
    静态变量的内存分配通常是在程序启动时完成的,而 CUDA 程序中,内存管理依赖于 GPU 的内存模型,且不同的线程可能需要独立的数据。如果使用静态变量,会导致内存分配和访问的不一致,进而影响内存的使用效率和程序的稳定性。

3.4 核函数为什么不能使用函数指针

在 CUDA 中,核函数不能使用函数指针,这一限制主要源于以下几个原因:

  • 线程执行路径的不可预测性:
    核函数的每个线程都是独立执行的,并且执行路径应该是固定和一致的。函数指针通常会导致执行路径的动态决定,使得每个线程的执行路径可能不同。这种不确定性会影响到 GPU 资源的调度和优化,特别是在高并发的情况下。
  • 编译时优化问题:
    使用函数指针会使得编译器无法在编译时确定函数的调用目标,导致无法进行高效的优化。CUDA 核函数通常依赖于编译时的优化(如内联、循环展开等),而使用函数指针会增加编译时的复杂性,影响最终的代码生成和性能。
  • 硬件执行模型的限制:
    在 GPU 上,线程的执行通常是基于硬件的执行模型进行的,线程之间的执行应当是简单、快速且高效的。函数指针的使用可能导致线程执行时需要额外的跳转和计算,增加了硬件执行路径的复杂性,进而影响性能。
  • 内存管理问题:
    函数指针的使用可能会导致内存访问的不一致性,特别是在多个线程中。由于函数指针可能指向不同的代码路径,可能会出现不同线程间对内存访问模式的竞争,这不仅会降低程序效率,还可能导致并发访问的错误。

3.5 如何理解核函数具有异步性

核函数的异步性 是 CUDA 编程中的一个重要概念。它指的是在 GPU 上启动的核函数的执行与主机(CPU)程序的执行是异步的,也就是说,核函数的调用并不会立即阻塞主机程序的执行。以下是异步性的一些关键点:

  • 核函数调用的异步执行:
    当你调用一个核函数(例如 kernel<<<blocks, threads>>>()),这并不会立即阻塞主机程序。相反,主机程序会继续执行下去,直到显式的同步操作发生,或者核函数执行完成后,CUDA 运行时会同步主机和设备的执行。
  • 显式同步:
    核函数的异步性要求在需要时显式地进行同步。例如,使用 cudaDeviceSynchronize() 可以确保主机程序等待 GPU 上的核函数执行完毕。没有这种同步机制,主机程序可能会在核函数执行完成之前就退出,导致错误或不一致的结果。
  • CUDA 内核调度与执行:
    GPU 会根据资源的可用性调度核函数的执行。这意味着,核函数的执行可能在后台启动,而主机程序不会等待它完成。这种异步模型最大化了 CPU 和 GPU 的并行性,使得主机程序可以在核函数执行期间执行其他任务,提高整体性能。
  • 硬件调度的异步性:
    GPU 中的多个 SM(流式多处理器)能够同时执行多个核函数调用,从而实现并行计算。核函数的执行在硬件级别是异步的,不同线程和块之间可能会并行执行。GPU 内部的线程调度与主机的调度相互独立,增加了灵活性和高效性。
  • 性能优化:
    异步性使得 GPU 可以在等待计算完成时进行其他工作,如处理更多的线程或执行其他核函数。在高性能计算场景中,这种异步特性有助于提升 GPU 的利用率和整体性能。通过合理安排同步操作,程序员可以最大化 CPU 和 GPU 的并行计算。

本文参考:
https://www.bilibili.com/video/BV1sM4y1x7of/?spm_id_from=333.788.videopod.episodes&vd_source=cf0b4c9c919d381324e8f3466e714d7a&p=4


原文地址:https://blog.csdn.net/qq_42761751/article/details/144297840

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!