一文讲清楚CUDA与PyTorch、GPU之间的关系

CUDA(Compute Unified Device Architecture)是由NVIDIA开发的一个并行计算平台和编程模型。它允许软件开发人员和研究人员利用NVIDIA的GPU(图形处理单元)进行高性能计算。CUDA提供了一系列API和工具,使得开发者能够编写和优化在GPU上运行的计算密集型任务。

CUDA与PyTorch、GPU之间的关系可以这样理解:

  1. CUDA与GPU:

GPU:是一种专门用于图像处理的微处理器,能够处理复杂的数学和图形计算。近年来,GPU也被广泛应用于通用计算,特别是深度学习和科学计算。

CUDA:允许开发者直接使用GPU进行计算。通过CUDA,开发者可以编写特定的程序(称为内核),这些程序在GPU上并行执行,从而加速计算。

  1. CUDA与PyTorch:

PyTorch:是一个开源的机器学习库,广泛用于深度学习应用。PyTorch支持动态计算图(称为autograd),这使得它在研究和开发中非常灵活和受欢迎。

CUDA与PyTorch的关系:PyTorch通过CUDA与GPU紧密集成。当PyTorch检测到系统中有可用的GPU时,它可以自动使用CUDA来加速计算。这意味着,如果你在PyTorch中定义了一个张量(tensor)并将其移动到GPU上,PyTorch将使用CUDA来执行相关的计算。这使得深度学习模型训练和推理的速度大大提高,因为GPU比CPU更适合执行并行计算任务。

总结来说,CUDA是一个编程模型,它允许开发者利用GPU的高性能进行计算。PyTorch作为一个深度学习库,通过CUDA与GPU集成,使得其能够高效地利用GPU资源进行深度学习相关的计算任务。

怎么通俗解释它们三者的用途和关系?

  1. GPU(图形处理单元):
  • 用途:GPU就像一个超级快的数学计算器。它特别擅长同时处理很多相同的计算任务,比如在视频游戏中渲染成千上万的像素点,或者在深度学习中同时更新成千上万的神经网络参数。

  • 比喻:想象一下你有一个非常擅长做加减乘除的大脑,而GPU就像是很多这样的大脑同时工作,一起解决数学问题。

  1. CUDA(计算统一设备架构):
  • 用途:CUDA是一个帮助程序员使用GPU的工具。它允许程序员编写代码,然后这些代码可以被GPU理解并执行。没有CUDA,程序员就需要用更复杂的方式来指挥GPU工作。

  • 比喻:CUDA就像是GPU的语言翻译器。程序员用一种语言写代码,CUDA把它翻译成GPU能理解的语言。

  1. PyTorch(一个深度学习库):
  • 用途:PyTorch是一个帮助人们轻松创建和训练深度学习模型的工具。深度学习模型是用于图像识别、语音识别、语言翻译等复杂任务的计算机程序。

  • 比喻:PyTorch就像是一个高级的"积木箱",里面有很多现成的积木(代码)。你可以用这些积木来搭建复杂的模型,比如一个可以识别猫和狗的模型。

它们之间的关系:

  • 当你使用PyTorch来创建一个深度学习模型时,如果你想要这个模型训练得更快,你可以让它利用GPU的计算能力。这时,CUDA就像是一个桥梁,帮助PyTorch和GPU沟通,让GPU来加速模型的训练。

总结:

  • GPU:一个超级快的数学计算器。

  • CUDA:一个让程序员能更容易使用GPU的工具。

  • Pytorch:一个帮助创建和训练深度学习模型的工具。

当PyTorch需要加速计算时,它通过CUDA来利用GPU的计算能力。

CUDA 线程层次结构

CUDA 最基本的执行单位是线程(Thread),图中每条曲线可视为单个线程,大的网格(Grid)被切分成小的网格,其中包含了很多相同线程数量的块(Block),每个块中的线程独立执行,可以通过本地数据共享实现数据交换同步。因此对于 CUDA 来讲,就可以将问题划分为独立线程块,并行解决的子问题,子问题划分为可以由块内线程并行协作解决。

CUDA 引入主机端(host)和设备(device)概念,CUDA 程序中既包含主机(host)程序也包含设备(device)程序,host 和 device 之间可以进行通信,以此来实现数据拷贝,主机负责管理数据和控制程序流程,设备负责执行并行计算任务。在 CUDA 编程中,Kernel 是在 GPU 上并行执行的函数,开发人员编写 Kernel 来描述并行计算任务,然后在主机上调用 Kernel 来在 GPU 上执行计算。

代码 cuda_host.cpp 是只使用 CPU 在 host 端实现两个矩阵的加法运算,其中在 CPU 上计算的 kernel 可看作是加法运算函数,代码中包含内存空间的分配和释放。

复制代码
#include <iostream>
#include <math.h>
#include <sys/time.h>

// function to add the elements of two arrays
void add(int n, float *x, float *y)
{
    for (int i = 0; i < n; i++)
        y[i] = x[i] + y[i];
}

int main(void)
{
    int N = 1<<25; // 30M elements

    float *x = new float[N];
    float *y = new float[N];

    // initialize x and y arrays on the host
    for (int i = 0; i < N; i++) {
        x[i] = 1.0f;
        y[i] = 2.0f;
    }

    struct timeval t1,t2;
    double timeuse;
    gettimeofday(&t1,NULL);

    // Run kernel on 30M elements on the CPU
    add(N, x, y);

    // Free memory
    delete [] x;
    delete [] y;

    return 0;
}

在 CUDA 程序架构中,host 代码部分在 CPU 上执行,是普通的 C 代码。当遇到数据并行处理的部分,CUDA 会将程序编译成 GPU 能执行的程序,并传送到 GPU,这个程序在 CUDA 里称做核(kernel)。device 代码部分在 GPU 上执行,此代码部分在 kernel 上编写(.cu 文件)。

kernel 用 __global__ 符号声明,在调用时需要用 <<<grid, block>>> 来指定 kernel 要执行及结构。代码 cuda_device.cu 是使用 CUDA 编程实现 GPU 计算,代码涉及到 host(CPU)和 device(GPU)相关计算,使用 __global__ 声明将 add 函数转变为 GPU 可执行的 kernel。

复制代码
#include <iostream>
#include <math.h>

// Kernel function to add the elements of two arrays
// __global__ 变量声明符,作用是将 add 函数变成可以在 GPU 上运行的函数
// __global__ 函数被称为 kernel
__global__
void add(int n, float *x, float *y)
{
  for (int i = 0; i < n; i++)
    y[i] = x[i] + y[i];
}

int main(void)
{
  int N = 1<<25;
  float *x, *y;

  // Allocate Unified Memory -- accessible from CPU or GPU
  // 内存分配,在 GPU 或者 CPU 上统一分配内存
  cudaMallocManaged(&x, N*sizeof(float));
  cudaMallocManaged(&y, N*sizeof(float));

  // initialize x and y arrays on the host
  for (int i = 0; i < N; i++) {
    x[i] = 1.0f;
    y[i] = 2.0f;
  }

  // Run kernel on 1M elements on the GPU
  // execution configuration, 执行配置
  add<<<1, 1>>>(N, x, y);

  // Wait for GPU to finish before accessing on host
  // CPU 需要等待 cuda 上的代码运行完毕,才能对数据进行读取
  cudaDeviceSynchronize();

  // Free memory
  cudaFree(x);
  cudaFree(y);
  
  return 0;
}

因此 CUDA 编程流程总结为:

  • 编写 Kernel 函数描述并行计算任务。
  • 在主机上配置线程块和网格,将 Kernel 发送到 GPU 执行。
  • 在主机上处理数据传输和结果处理,以及控制程序流程。

为了实现以上并行计算,对应于 GPU 硬件在进行实际计算过程时,CUDA 可以分为 Grid,Block 和 Thread 三个层次结构:

  • 线程层次结构Ⅰ-Grid:kernel 在 device 上执行时,实际上是启动很多线程,一个 kernel 所启动的所有线程称为一个网格(grid),同一个网格上的线程共享相同的全局内存空间,grid 是线程结构的第一层次。
  • 线程层次结构Ⅱ-Block:Grid 分为多个线程块(block),一个 block 里面包含很多线程,Block 之间并行执行,并且无法通信,也没有执行顺序,每个 block 包含共享内存(shared memory),可以共享里面的 Thread。
  • 线程层次结Ⅲ-Thread:CUDA 并行程序实际上会被多个 threads 执行,多个 threads 会被群组成一个线程 block,同一个 block 中 threads 可以同步,也可以通过 shared memory 通信。

因此 CUDA 和英伟达硬件架构有以下对应关系,从软件侧看到的是线程的执行,对应于硬件上的 CUDA Core,每个线程对应于 CUDA Core,软件方面线程数量是超配的,硬件上 CUDA Core 是固定数量的。Block 线程块只在一个 SM 上通过 Warp 进行调度,一旦在 SM 上调用了 Block 线程块,就会一直保留到执行完 kernel,SM 可以同时保存多个 Block 线程块,多个 SM 组成的 TPC 和 GPC 硬件实现了 GPU 并行计算。

相关推荐
qq_5298353511 分钟前
Java实现死锁
java·开发语言·python
刘大猫2625 分钟前
SpringBoot使用Kafka生产者、消费者
人工智能·设计模式·图像识别
Hi2024021726 分钟前
PyTorch多机训练Loss不一致问题排查指南:基于算子级一致性验证
人工智能·pytorch·python
孔令飞35 分钟前
01 | Go 项目开发极速入门课介绍
开发语言·人工智能·后端·云原生·golang·kubernetes
szxinmai主板定制专家44 分钟前
基于DSP+ARM+FPGA轨道交通6U机箱结构牵引控制单元(Pcle)
大数据·arm开发·人工智能·fpga开发·架构
yuanpan1 小时前
机器学习神经网络中的损失函数表达的是什么意思
人工智能·神经网络·机器学习
月亮月亮要去太阳1 小时前
python画图文字显示不全+VScode新建jupyter文件
开发语言·python
YoloMari1 小时前
Leetcode刷题-枚举右,维护左
python·算法·leetcode
邪恶的贝利亚1 小时前
prompt工程起步
开发语言·python·prompt
小青龙emmm2 小时前
机器学习(七)
人工智能·机器学习