CUDA(Compute Unified Device Architecture)是由NVIDIA开发的一个并行计算平台和编程模型。它允许软件开发人员和研究人员利用NVIDIA的GPU(图形处理单元)进行高性能计算。CUDA提供了一系列API和工具,使得开发者能够编写和优化在GPU上运行的计算密集型任务。
CUDA与PyTorch、GPU之间的关系可以这样理解:
1. CUDA与GPU:
GPU:是一种专门用于图像处理的微处理器,能够处理复杂的数学和图形计算。近年来,GPU也被广泛应用于通用计算,特别是深度学习和科学计算。
CUDA:允许开发者直接使用GPU进行计算。通过CUDA,开发者可以编写特定的程序(称为内核),这些程序在GPU上并行执行,从而加速计算。
2. 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就像是很多这样的大脑同时工作,一起解决数学问题。
2. CUDA(计算统一设备架构):
- 用途:CUDA是一个帮助程序员使用GPU的工具。它允许程序员编写代码,然后这些代码可以被GPU理解并执行。没有CUDA,程序员就需要用更复杂的方式来指挥GPU工作。
- 比喻:CUDA就像是GPU的语言翻译器。程序员用一种语言写代码,CUDA把它翻译成GPU能理解的语言。
3. PyTorch(一个深度学习库):
- 用途:PyTorch是一个帮助人们轻松创建和训练深度学习模型的工具。深度学习模型是用于图像识别、语音识别、语言翻译等复杂任务的计算机程序。
- 比喻:PyTorch就像是一个高级的“积木箱”,里面有很多现成的积木(代码)。你可以用这些积木来搭建复杂的模型,比如一个可以识别猫和狗的模型。
它们之间的关系:
- 当你使用PyTorch来创建一个深度学习模型时,如果你想要这个模型训练得更快,你可以让它利用GPU的计算能力。这时,CUDA就像是一个桥梁,帮助PyTorch和GPU沟通,让GPU来加速模型的训练。
总结:
- GPU:一个超级快的数学计算器。
- CUDA:一个让程序员能更容易使用GPU的工具。
- Pytorch:一个帮助创建和训练深度学习模型的工具。
当PyTorch需要加速计算时,它通过CUDA来利用GPU的计算能力。
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
#include
#include
// 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__
符号声明,在调用时需要用 <<
来指定 kernel 要执行及结构。代码 cuda_device.cu
是使用 CUDA 编程实现 GPU 计算,代码涉及到 host(CPU)和 device(GPU)相关计算,使用 __global__
声明将 add 函数转变为 GPU 可执行的 kernel。
#include
#include
// 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 编程流程总结为:
为了实现以上并行计算,对应于 GPU 硬件在进行实际计算过程时,CUDA 可以分为 Grid,Block 和 Thread 三个层次结构:
因此 CUDA 和英伟达硬件架构有以下对应关系,从软件侧看到的是线程的执行,对应于硬件上的 CUDA Core,每个线程对应于 CUDA Core,软件方面线程数量是超配的,硬件上 CUDA Core 是固定数量的。Block 线程块只在一个 SM 上通过 Warp 进行调度,一旦在 SM 上调用了 Block 线程块,就会一直保留到执行完 kernel,SM 可以同时保存多个 Block 线程块,多个 SM 组成的 TPC 和 GPC 硬件实现了 GPU 并行计算。