什么是 GPU 编程?
GPU 计算是使用图形处理单元 (graphics processing unit) 来执行曾经由中央处理单元 (central processing unit) 处理的高度并行的独立计算。
GPU 编程是一种在 GPU 加速器上运行高度并行的通用计算的方法。
虽然过去的 GPU 专为计算机图形设计,但如今它们也被广泛用于通用计算。除了图形渲染,GPU 驱动的并行计算现在还用于科学建模、机器学习和其他需要并行化的工作。
CPU 与 GPU 计算差异
中央处理器 (CPU) 是延迟优化的通用处理器,旨在按顺序处理各种不同的任务,而图形处理单元 (GPU) 是吞吐量优化的专用处理器,专为高端并行设计计算。
您是否需要 GPU 加速器的问题归结为您要解决的问题的具体情况。CPU 和 GPU都有不同的优势领域,了解它们的局限性会让你在决定是否为你的项目使用 GPU 编程时做得更好。
GPU 编程 API
GPU 根据图元理解计算问题。如今,有多种可用的编程框架可以在幕后为您操作这些原语,因此您可以专注于更高级别的计算概念。
CUDA
计算统一设备架构 (CUDA) 是 Nvidia 于 2006 年创建的并行计算平台和应用程序编程接口 (API),可直接访问 GPU 的虚拟指令集以执行计算内核。
内核是在 GPU 上运行的函数。当我们启动内核时,它做为一组线程执行。每个线程都映射到 GPU 上的单个 CUDA 内核,并对数据子集执行相同的操作。根据Flynn 的分类法,它是一种单指令多数据 (SIMD) 计算。
线程被分组为块,当内核启动时,它们被映射到一组相应的 CUDA 内核。块进一步分组为网格,每次内核启动都会创建一个网格。
CUDA 编程模型允许软件工程师使用支持 CUDA 的 GPU 在 C/C++ 和 Fortran 中进行通用处理,第三方包装器也可用于 Python、Java、R 和其他几种编程语言。CUDA 兼容从 G8x 系列开始的所有 Nvidia GPU,以及大多数标准操作系统。
OpenCL
虽然 CUDA 是专有框架,但 OpenCL 是由 Khronos Group 创建的跨异构平台并行编程的开放标准。OpenCL 与中央处理器 (CPU)、图形处理单元 (GPU)、数字信号处理器、现场可编程门阵列 (FPGA) 和其他处理器或硬件加速器协同工作。
OpenCL 用途极为广泛,已被科技行业巨头成功采用,包括 AMD、Apple、IBM、Intel、Nvidia、Qualcomm、Samsung 等。它基于 C/C++ 语言,第三方包装器也可用于 Python、Java、R、GO、JavaScript 等。
OpenACC
OpenACC 是一种用户驱动的基于指令的并行编程标准,专为有兴趣将其代码移植到各种异构高性能计算 (HPC) 硬件平台的科学家和工程师而设计。该标准是由用户为用户设计的。
OpenACC 旨在简化异构 CPU/GPU 硬件平台和架构的并行编程,其编程工作量比低级模型所需的工作量少得多。它支持 C/C++ 和 Fortran 编程语言。
使用 CUDA 和 Python 进行 GPU 编程
开始构建 GPU 加速程序有多种标准和多种编程语言,但我们选择了 CUDA 和 Python 来说明我们的示例。CUDA 是最容易上手的框架,Python 在科学、工程、数据分析和深度学习领域非常流行——所有这些都严重依赖并行计算。
即使在 Python 中,您也可以在不同的抽象层上进行 GPU 编程。如果您不需要较低抽象层可以提供的额外自定义和控制功能,那么从满足您的应用程序的最高抽象层开始是合理的。
让我们回顾一下在 Python 中使用 CUDA 的方法,从最高抽象级别开始一直到最低级别。
CUDA 用于专业库
如果您只想使用神经网络或任何其他深度学习算法,那么 Tensorflow 或 PyTorch 等专业深度学习库可能是您的正确选择。这些库可以在后台为您自动在 CPU 和 GPU 处理之间切换。
CUDA 做为直接替代品
如果您是使用 NumPy 和 SciPy 的科学家,优化 GPU 计算代码的最简单方法是使用 CuPy。它模仿大多数 NumPy 函数,并允许您简单地插入 NumPy 代码并将其替换为在 GPU 而不是 CPU 上处理的 CuPy 函数。
CUDA 用于自定义算法。
当您需要使用自定义算法时,您不可避免地需要深入抽象层次结构并使用 NUMBA。它绑定到 CUDA 并允许您在 Python 中编写自己的 CUDA 内核。通过这种方式,您可以仅使用 Python 非常接近 CUDA C/C++,而无需自己分配内存。
CUDA 做为 C/C++ 扩展
如果您想要对硬件进行最高级别的控制,例如手动内存分配、动态并行性或纹理内存管理,那么使用 C/C++ 是没有办法的。对于 Python 应用程序,最方便的方法是使用 PyCUDA 扩展,它允许您在 Python 字符串中编写 CUDA C/C++ 代码。
如何在 Ubuntu 20.04 上开始使用 CUDA for Python?
在 Ubuntu 20.04 上安装 CUDA
首先,您需要在具有支持 CUDA 的 GPU 的机器上安装 CUDA。要使用本地安装程序在 Ubuntu 20.04 上安装 CUDA 驱动程序,请按照以下说明操作:
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin
sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/11.4.2/local_installers/cuda-repo-ubuntu2004-11-4-local_11.4.2-470.57.02-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu2004-11-4-local_11.2-470.57.02-1_amd64.deb
sudo apt-key add /var/cuda-repo-ubuntu2004-11-4-local/7fa2af80.pub
sudo apt-get update
sudo apt-get -y install cuda
安装 CuPy 库
接下来,我们需要安装一个 Python 库来使用 CUDA。如上所述,有许多方法可以在不同的抽象级别在 Python 中使用 CUDA。由于 NumPy 是 Python 数据科学生态系统的骨干库,我们将选择对其进行加速以进行本次演示。
使用 NumPy 的最简单方法是使用名为 CuPy 的插入式替换库,它在 GPU 上复制 NumPy 函数。您可以通过 pip 安装稳定版的 CuPy 源码包:
pip install cupy
编写脚本来比较 CPU 和 GPU
最后,您要确保 CuPy 在您的系统上运行良好,以及它能在多大程度上提高您的性能。为此,让我们编写一个简单的脚本来实现这一目的。
让我们导入 NumPy 和 CuPy 库,以及我们将用于基准处理单元的时间库。
import numpy as np
import cupy as cp
from time import time
接下来,让我们定义一个将用于基准测试的函数。
def benchmark_processor(arr, func, argument):
start_time = time()
func(arr, argument) # your argument will be broadcasted into a matrix automatically
finish_time = time()
elapsed_time = finish_time – start_time
return elapsed_time
然后需要实例化两个矩阵:一个用于 CPU,一个用于 GPU。我们将为我们的矩阵选择 9999 x 9999 的形状。
# load a matrix to global memory
array_cpu = np.random.randint(0, 255, size=(9999, 9999))
# load the same matrix to GPU memory
array_gpu = cp.asarray(array_cpu)
最后,我们要运行一个简单的加法函数来确定 CPU 与 GPU 处理器的性能差异。
# benchmark matrix addition on CPU by using a NumPy addition function
cpu_time = benchmark_processor(array_cpu, np.add, 999)
# you need to run a pilot iteration on a GPU first to compile and cache the function kernel on a GPU
benchmark_processor(array_gpu, cp.add, 1)
# benchmark matrix addition on GPU by using CuPy addition function
gpu_time = benchmark_processor(array_gpu, cp.add, 999)
# determine how much is GPU faster
faster_processor = (gpu_time - cpu_time) / gpu_time * 100
并将结果打印到控制台。
print(f"CPU time: {cpu_time} seconds\nGPU time: {gpu_time} seconds.\nGPU was {faster_processor} percent faster")
在配备来自Cherry Servers GPU Cloud的 Nvidia Geforce GT1030 GPU 加速器的 Intel Xeon 1240v3 机器上运行此脚本后,我们确认整数加法在 GPU 上的运行速度快了许多倍。例如,当使用 10000x10000 矩阵时,GPU 运行整数加法的速度要快 1294 倍。
事实上,矩阵越大,您期望的性能提升就越高。
cuda-gpu-基准测试
图 1 – GPU 性能提升。
我们通过对 100x100、500x500、1000x1000、7500x7500 和 10000x10000 二维矩阵使用整数加法,比较了 CPU 与 GPU 的性能(以秒为单位)。当使用足够大的矩阵时,GPU 开始明显超过 CPU。
总结
NVIDIA官方网站。https://developer./language-solutions
Aliyun Servers GPU 服务器。https://www.aliyun.com/minisite/goods?userCode=69hiviin
Cherry Servers GPU 服务器。https://www./?affiliate=1TO67IVM
如果您正在处理可以并行处理的大量数据,那么深入研究 GPU 编程可能是值得的。如您所见,使用 GPU 计算处理大型矩阵时性能显着提高。到一天结束时,如果您的应用程序可以利用并行计算,它可能会为您节省宝贵的时间和资源。
半熟不熟,慢慢就熟了。