中文领域最详细的Python版CUDA入门教程

您所在的位置:网站首页 python编程在哪里写 中文领域最详细的Python版CUDA入门教程

中文领域最详细的Python版CUDA入门教程

2024-07-03 20:35| 来源: 网络整理| 查看: 265

本系列为英伟达GPU入门介绍的第二篇,主要介绍CUDA编程的基本流程和核心概念,并使用Python Numba编写GPU并行程序。为了更好地理解GPU的硬件架构,建议读者先阅读我的第一篇文章。

GPU硬件知识和基础概念:包括CPU与GPU的区别、GPU架构、CUDA软件栈简介。

GPU编程入门:主要介绍CUDA核函数,Thread、Block和Grid概念,并使用Python Numba进行简单的并行计算。

GPU编程进阶:主要介绍一些优化方法。

GPU编程实践:使用Python Numba解决复杂问题。 公众号

针对Python的CUDA教程

Python是当前最流行的编程语言,被广泛应用在深度学习、金融建模、科学和工程计算上。作为一门解释型语言,它运行速度慢也常常被用户诟病。著名Python发行商Anaconda公司开发的Numba库为程序员提供了Python版CPU和GPU编程工具,速度比原生Python快数十倍甚至更多。使用Numba进行GPU编程,你可以享受:

Python简单易用的语法;极快的开发速度;成倍的硬件加速。

为了既保证Python语言的易用性和开发速度,又达到并行加速的目的,本系列主要从Python的角度给大家分享GPU编程方法。关于Numba的入门可以参考我的另一篇文章。更加令人兴奋的是,Numba提供了一个GPU模拟器,即使你手头暂时没有GPU机器,也可以先使用这个模拟器来学习GPU编程!

初识GPU编程

兵马未动,粮草先行。在开始GPU编程前,需要明确一些概念,并准备好相关工具。

CUDA是英伟达提供给开发者的一个GPU编程框架,程序员可以使用这个框架轻松地编写并行程序。本系列第一篇文章提到,CPU和主存被称为主机(Host),GPU和显存(显卡内存)被称为设备(Device),CPU无法直接读取显存数据,GPU无法直接读取主存数据,主机与设备必须通过总线(Bus)相互通信。 GPU和CPU架构 在进行GPU编程前,需要先确认是否安装了CUDA工具箱,可以使用echo $CUDA_HOME检查CUDA环境变量,返回值不为空说明已经安装好CUDA。也可以直接用Anaconda里的conda命令安装CUDA:

$ conda install cudatoolkit

然后可以使用nvidia-smi命令查看显卡情况,比如这台机器上几张显卡,CUDA版本,显卡上运行的进程等。我这里是一台32GB显存版的Telsa V100机器。 nvidia-smi命令返回结果 安装Numba库:

$ conda install numba

检查一下CUDA和Numba是否安装成功:

from numba import cuda print(cuda.gpus)

如果上述步骤没有问题,可以得到结果:...。如果机器上没有GPU或没安装好上述包,会有报错。CUDA程序执行时会独霸一张卡,如果你的机器上有多张GPU卡,CUDA默认会选用0号卡。如果你与其他人共用这台机器,最好协商好谁在用哪张卡。一般使用CUDA_VISIBLE_DEVICES这个环境变量来选择某张卡。如选择5号GPU卡运行你的程序。

CUDA_VISIBLE_DEVICES='5' python example.py

如果手头暂时没有GPU设备,Numba提供了一个模拟器,供用户学习和调试,只需要在命令行里添加一个环境变量。

Mac/Linux:

export NUMBA_ENABLE_CUDASIM=1

Windows:

SET NUMBA_ENABLE_CUDASIM=1

需要注意的是,模拟器只是一个调试的工具,在模拟器中使用Numba并不能加速程序,有可能速度更慢,而且在模拟器能够运行的程序,并不能保证一定能在真正的GPU上运行,最终还是要以GPU为准。

有了以上的准备工作,我们就可以开始我们的GPU编程之旅了!

GPU程序与CPU程序的区别

一个传统的CPU程序的执行顺序如下图所示: CPU程序执行流程 CPU程序是顺序执行的,一般需要:

初始化。

CPU计算。

得到计算结果。

在CUDA编程中,CPU和主存被称为主机(Host),GPU被称为设备(Device)。 GPU程序执行流程 当引入GPU后,计算流程变为:

初始化,并将必要的数据拷贝到GPU设备的显存上。CPU调用GPU函数,启动GPU多个核心同时进行计算。CPU与GPU异步计算。将GPU计算结果拷贝回主机端,得到计算结果。

一个名为gpu_print.py的GPU程序如下所示:

from numba import cuda def cpu_print(): print("print by cpu.") @cuda.jit def gpu_print(): # GPU核函数 print("print by gpu.") def main(): gpu_print[1, 2]() cuda.synchronize() cpu_print() if __name__ == "__main__": main()

使用CUDA_VISIBLE_DEVICES='0' python gpu_print.py执行这段代码,得到的结果为:

print by gpu. print by gpu. print by cpu.

与传统的Python CPU代码不同的是:

使用from numba import cuda引入cuda库在GPU函数上添加@cuda.jit装饰符,表示该函数是一个在GPU设备上运行的函数,GPU函数又被称为核函数。主函数调用GPU核函数时,需要添加如[1, 2]这样的执行配置,这个配置是在告知GPU以多大的并行粒度同时进行计算。gpu_print[1, 2]()表示同时开启2个线程并行地执行gpu_print函数,函数将被并行地执行2次。下文会深入探讨如何设置执行配置。GPU核函数的启动方式是异步的:启动GPU函数后,CPU不会等待GPU函数执行完毕才执行下一行代码。必要时,需要调用cuda.synchronize(),告知CPU等待GPU执行完核函数后,再进行CPU端后续计算。这个过程被称为同步,也就是GPU执行流程图中的红线部分。如果不调用cuda.synchronize()函数,执行结果也将改变,"print by cpu.将先被打印。虽然GPU函数在前,但是程序并没有等待GPU函数执行完,而是继续执行后面的cpu_print函数,由于CPU调用GPU有一定的延迟,反而后面的cpu_print先被执行,因此cpu_print的结果先被打印了出来。 Thread层次结构

前面的程序中,核函数被GPU并行地执行了2次。在进行GPU并行编程时需要定义执行配置来告知以怎样的方式去并行计算,比如上面打印的例子中,是并行地执行2次,还是8次,还是并行地执行20万次,或者2000万次。2000万的数字太大,远远多于GPU的核心数,如何将2000万次计算合理分配到所有GPU核心上。解决这些问题就需要弄明白CUDA的Thread层次结构。 并行执行8次的执行配置 CUDA将核函数所定义的运算称为线程(Thread),多个线程组成一个块(Block),多个块组成网格(Grid)。这样一个grid可以定义成千上万个线程,也就解决了并行执行上万次操作的问题。例如,把前面的程序改为并行执行8次:可以用2个block,每个block中有4个thread。原来的代码可以改为gpu_print[2, 4](),其中方括号中第一个数字表示整个grid有多少个block,方括号中第二个数字表示一个block有多少个thread。

实际上,线程(thread)是一个编程上的软件概念。从硬件来看,thread运行在一个CUDA核心上,多个thread组成的block运行在Streaming Multiprocessor(SM的概念详见本系列第一篇文章),多个block组成的grid运行在一个GPU显卡上。

软硬件对应关系 CUDA提供了一系列内置变量,以记录thread和block的大小及索引下标。以[2, 4]这样的配置为例:blockDim.x变量表示block的大小是4,即每个block有4个thread,threadIdx.x变量是一个从0到blockDim.x - 1(4-1=3)的索引下标,记录这是第几个thread;gridDim.x变量表示grid的大小是2,即每个grid有2个block,blockIdx.x变量是一个从0到gridDim.x - 1(2-1=1)的索引下标,记录这是第几个block。 CUDA内置变量示意图 某个thread在整个grid中的位置编号为:threadIdx.x + blockIdx.x * blockDim.x。 使用内置变量计算某个thread编号

利用上述变量,我们可以把之前的代码丰富一下:

from numba import cuda def cpu_print(N): for i in range(0, N): print(i) @cuda.jit def gpu_print(N): idx = cuda.threadIdx.x + cuda.blockIdx.x * cuda.blockDim.x if (idx


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3