模拟/建模/设计

使用 cuNumeric 和 Legate 加速 Python 应用程序

cuNumeric 是一个库,旨在为支持所有 indexing view 功能(如就地更新、 NumPy 和完整 indexing view 语义)的 NumPy API 提供分布式和加速插入替换。这意味着,当切换到使用 cuNumeric 时,任何使用 NumPy 在大型数据集上操作的 Python 代码都可以自动并行化,以利用 CPU 和 GPU 的大型集群的功能。

NumPy 是科学计算中的基础 Python 库,用于执行基于阵列的数值计算。大多数程序员使用的 NumPy 的规范实现在单个 CPU 内核上运行,只有少数操作在内核之间并行化。对单个节点 CPU 执行的这种限制限制了可以处理的数据的大小和解决问题的速度。

到目前为止, NumPy 的几个替换库加速下降(例如 CuPy 和 NumS )。然而,它们中没有一个在具有许多 CPU 和 GPU 的多节点机器上提供透明的分布式加速,同时仍然支持 NumPy 的所有重要功能。

在 cuNumeric 之前, NumPy 代码必须进行显著更改,才能在多个节点/ GPU 上执行。这些修改通常包括手动代码并行化和分发逻辑,这些逻辑通常容易出错,并且不总是性能良好,可能会导致功能损失。

cuNumeric 的创建旨在为开发人员提供 NumPy 的生产力以及加速和分布式 GPU 计算的性能,而不受影响。使用 cuNumeric ,计算和数据科学家可以在本地机器上的中等大小数据集上开发和测试程序。然后,可以使用相同的代码立即扩展到部署在超级计算机上的多个节点上的更大数据集。

cuNumeric 于 GTC 2022 年首次发布。有关详细信息,请参见 NVIDIA Announces Availability for cuNumeric Public Alpha 。此后的更新包括将 NumPy API 的 API 覆盖率从 20% 提高到 60% ,支持 Jupyter 笔记本,并改进了性能。

cuNumeric 已证明其可扩展至数千 GPU 。例如,从 GitHub 上的 CFDPython 摘录的 CFD 代码在切换到使用 cuNumeric 时显示了良好的缩放结果(图 1 )。

A graph showing weak scaling results for the CFD code example in a graphical representation. The X axis represents the number of GPUs from 1 to 2048, the Y axis represents computational throughput. On the top of the graph there is an orange dot at 162 million points per second representing a throughput for CuPy on a single GPU. There is a blue line going from 1 to 2048 GPUs for cuNumeric code with the throughput changing from 153 for the single GPU to 104 when executed on 2048 GPUs.
图 1 。 cuNumeric 中 CFD 代码示例的弱缩放结果

cuNumeric 中的隐式数据分布和并行化通过 Legate 实现。
Legate 是一个生产力层,使得在 Legion 运行时之上构建可组合的层更容易,以便在异构集群上执行。 cuNumeric 是 Legate 生态系统的一部分,这意味着 cuNumeric 程序可以透明地向生态系统中的其他库传递对象,而不会导致不必要的同步或数据移动,即使在分布式环境中也是如此。

Figure shows an image of the Legate software stack in four colors consisting of Legion/Realm at the bottom, Legate  layered above, Legate libraries (like cuNumeric) layered above, and final User applications at the top. It is surrounded by different GPU-based devices and computers to show that Legate-based code will work on almost any kind of architecture.
图 2 :合法的软件堆栈和生态系统

使用 cuNumeric

使用 cuNumeric 只需要在 NumPy 代码中用import cunumeric as np替换import numpy as np,并使用 Legate 驱动程序脚本来执行程序。

cuNumeric 代码的一个简单示例如下:

import cunumeric as np
a = np.arange(10000, dtype =int)
a = a.reshape((100,100,))
b = np.arange(10000, dtype =int)
b = b.reshape((100,100,))
c = np.multiply(a, b)
print(c)
print(type(c))

只有第一个import更改需要从 NumPy 迁移到 cuNumeric 。该代码现在在多个 GPU 上执行。阵列abc在 GPU 上进行分区,以便在import的不同碎片上异步执行arangereshapemultiply操作。有关详细信息,请参阅下面的 cuNumeric 自动数据分区部分。

cuNumeric 自动数据分区

cuNumeric 隐式地划分其数据对象,考虑将访问数据的计算、不同处理器类型所消耗的理想数据大小以及可用处理器数量。无论机器的规模如何,子分区的一致性都由 Legion 自动管理。

图 3 显示了四个进程之间 cuNumeric 2D 阵列的相等划分的可视化。当执行数据并行操作(如add)时,不同颜色的区块将由不同的任务异步处理。

A 4-colored image representing a 10 x 10 matrix that has been equally broken apart into four smaller 5 x 5 matrices to illustrate how cuNumeric partitions data to be processed in parallel.
图 3 。 cuNumeric 隐式数据划分的可视化

请注意,不同的 cuNumeric API 可以重用现有分区或请求不同的分区以满足特定需求。允许多个分区共存,并自动保持同步。 Legion 将仅在需要时复制和重新格式化数据,并将尽可能以最有效的方式执行此操作。

使用 cuNumeric 异步执行

除了在每个任务的不同分区数组上异步执行计算之外,如果资源可用, cuNumeric 还可以执行异步任务和/或操作执行。底层运行时将创建依赖关系图,然后以分布式无序方式执行操作,同时保留数据依赖关系。

图 4 可视化了在四个 GPU (单个节点)上执行的第一个示例的依赖关系图。这里,阵列arangereshapecopy任务和a操作可以与阵列b的操作并行执行。请注意,阵列范围内的每个操作也分为四个子操作。

Figure shows some Python code with data being partitioned into separate boxes representing a dependency tree graph to show asynchronous execution of independent API calls.
图 4 。 cuNumeric 中的异步执行

单节点安装和执行

cuNumeric 在 Anaconda 上可用。要安装 cuNumeric ,请执行以下脚本:

conda install -c nvidia -c conda-forge -c legate cunumeric

conda 包与 CUDA >= 11.4 ( CUDA 驱动程序版本>= r470 )和 NVIDIA Volta 或更高版本 GPU 架构兼容。

请注意, cuNumeric conda 包目前不支持 Mac OS 。如果您是在 Mac OS 上安装,请参阅下面的多节点安装和执行部分,了解有关手动安装 cuNumeric 的说明。 cuNumeric 程序使用 Legate 文档中描述的 Legate 驱动程序脚本运行:

legate cunumeric_program.py

以下运行时选项可用于控制设备的数量:

  --cpus CPUS                Number of CPUs to use per rank
  --gpus GPUS                Number of GPUs to use per rank
  --omps OPENMP              Number of OpenMP groups to use per rank
  --ompthreads               Number of threads per OpenMP group

您可以熟悉 Legate 文档中描述的这些资源标志,也可以将--help传递给 Legate 驱动程序脚本。对基于 Legate 的代码使用legate驱动程序脚本的要求应在不久的将来解除。( cuNumeric 代码将与标准python解释器一起使用。)

Jupyter 笔记本和 cuNumeric

cuNumeric 安装在系统上时,也可以与 Jupyter 笔记本一起使用。应按如下方式配置和安装特定的 Jupyter 内核:

legate-jupyter --name legate_cpus_2 --cpus 2

使用– help 命令行选项可以查看内核的其他配置选项。

应使用以下脚本启动 Jupyter 服务器:

jupyter notebook --port=888 --no-browser

然后,应使用 Legion wiki 页上提供的说明在浏览器中打开 Jupyter 笔记本。

多节点安装和执行

为了支持多节点执行,必须手动安装 cuNumeric 。手动执行包括以下步骤:

1 ) 使用以下代码从 GitHub 克隆 Legate :

git clone git@github.com:nv-legate/legate.core.git
cd legate.core

2 ) 安装 Legate 和 cuNumeric 依赖项。

检索依赖项的主要方法是通过 conda 。使用 Legate 的 scripts/generate-conda-envs.py 脚本创建一个 conda 环境文件,列出构建、运行和测试 Legate Core 所需的所有包以及目标系统上的所有下游库。例如:

$ ./scripts/generate-conda-envs.py --python 3.10 --ctk 11.7 --os linux --compilers --openmpi 
--- generating: environment-test-linux-py310-cuda-11.7-compilers-openmpi.yaml

生成环境文件后,通过使用以下脚本创建新的 conda 环境来安装所需的软件包:

conda env create -n legate -f <env-file>.yaml

3 ) 安装 Legate

Legate Core 存储库在顶级目录中附带了一个助手install.py脚本,该脚本将构建库的 C ++部分,并在当前活动的 Python 环境下安装 C ++和 Python 组件。

要添加 GPU 支持,请使用--cuda标志:

./install.py --cuda

如果在安装过程中未找到 CUDA ,请将CUDA_PATH变量设置为正确的位置。例如:

CUDA_PATH=/usr/local/cuda-11.6/lib64/stubs ./install.py --cuda

对于多节点执行, Legate 使用 GASNet ,可以使用--network标志请求。使用 GASNet 时,还需要使用--conduit标志指定目标计算机的互连网络。例如,以下代码将是 DGX SuperPOD 的安装:

./install.py -–network gasnet1 --conduit ibv –cuda

4 ) 使用以下调用克隆并安装 cuNumeric :

git clone git@github.com:nv-legate/cunumeric.git
cd cunumeric
./install.py 

有关 cuNumeric 安装选项(包括多节点配置设置)的更多详细信息,请访问 GitHub 上的 nv-legate/cunumeri#build

如果 Legate 是使用允许多节点执行的网络支持进行编译的,则可以使用--nodes选项和要使用的节点数并行运行 Legate 。每当使用--nodes选项时, Legate 将使用mpirun启动,即使使用--nodes 1。如果没有--nodes选项,将不会使用启动器。

Legate 目前支持 mpirun 、 srun 和 jsrun 作为启动器,可能会添加其他启动器类型。您可以使用– launcher 选择启动器的目标类型。例如,以下命令将在 64 个节点上执行 cuNumeric 代码,每个节点执行 8 GPU :

legate cunumeric_program.py --nodes 64 --gpus 8

cuNumeric 示例

cuNumeric 的存储库中有几个 example codes ,可以用来熟悉库。为了简单起见,本文从 Stencil 示例开始。

使用 cuNumeric 进行模具计算

Stencil 代码演示了如何以不同的比例编写和执行 cuNumeric 代码。模板代码是许多数值求解器和物理模拟代码的核心,因此对科学计算研究特别感兴趣。本节介绍 cuNumeric 中一个简单模板代码示例的实现。

首先,使用以下脚本创建并初始化网格:

import cunumeric as np

N = 1000 # number of elements in one dimension
I = 100 # number of iterations

def initialize(N):
	print("Initializing stencil grid...")
	grid = np.zeros((N + 2, N + 2))
	grid[:, 0] = -273.15
	grid[:, -1] = -273.15
	grid[-1, :] = -273.15
	grid[0, :] = 40.0
	return grid

此“初始化”函数将分配( N + 2 ) x ( N + 2 个)二维零矩阵并填充边界元素。

接下来,执行模具计算:

def run_stencil():
	grid = initialize(N)

	center = grid[1:-1, 1:-1]
	north = grid[0:-2, 1:-1]
	east = grid[1:-1, 2:]
	west = grid[1:-1, 0:-2]
	south = grid[2:, 1:-1]

	for i in range(I):
    	average = center + north + east + west + south
    	average = 0.2 * average
    	center[:] = average

run_stencil()

该代码通过 cuNumeric 在所有可用资源上完全并行化。它可以使用以下调用在单个节点上执行:

legate examples/stencil.py --gpus 8

它可以使用以下调用在多个节点上执行:

legate examples/stencil.py --nodes 128 --gpus 8

要查看此示例的原始代码,请访问 GitHub 上的 nv-legate/cunumeric

模具示例性能结果

图 5 显示了 Stencil 代码的弱缩放结果。每个 GPU 的网格点数保持不变(每个 GPU 12264004 点),从而增加了问题的总大小。如图所示,在没有程序员帮助的情况下,示例在大型系统上几乎可以完美缩放。

Graph shows weak scaling results for the Stencil code example in a graphical representation. The X axis represents the number of GPUs from 1 to 1024, the Y axis represents computational throughput. On the top of the graph there is a flat horizontal line at about 11.3 on the Y axis, which means perfect weak scaling.
图 5 。 cuNumeric 中模具代码示例的弱缩放结果

剖析 cuNumeric 代码

开发功能 cuNumeric 应用程序后,通常需要对性能进行分析和调整。本节介绍分析 cuNumeric 代码的不同选项。

Legate 教授

要获得 Legion 级别的评测输出,请在执行 cuNumeric 代码时传递– profile 标志。在执行结束时,将创建 legate _ prof 目录。此目录包含一个网页,可在显示程序执行时间线的任何 web 浏览器中查看。请注意,如果您正在本地计算机上查看页面(取决于浏览器),则可能需要执行 enable local JavaScript

图 6 显示了执行 Stencil 示例的概要输出。

A screen capture of the output of a Legate profiler which shows multiple green and orange blocks representing the parallel execution of each GPU process as well as several more graphs representing Utility processor, Python code execution, memory allocation.
图 6 。使用 Legate 探查器输出模板代码配置文件

NVIDIA Nsight 系统

运行时标志用于获取模具--nsys代码 cuNumeric 的 NVIDIA Nsight Systems 探查器输出。传递此标志后,将生成一个输出文件,可将其加载到 Nsight Systems UI 中。 cuNumeric 生成的nsys文件的可视化如图 7 所示。

A screen capture of the output of a Nsight System profiler including time spent per each kernel and some of the CUDA APIs.
图 7 。使用 NVIDIA Nsight Systems 输出模板代码配置文件

调试 cuNumeric 代码

Legate 提供了在 cuNumeric 应用程序运行期间检查 Legion 构建的数据流和事件图的工具。构建这些图需要在计算机上安装 GraphViz

要为程序生成数据流图,请将--dataflow标志传递给 legate脚本。运行完成后,库将生成一个dataflow_legate PDF 文件,其中包含程序的数据流图。要生成相应的事件图,请将--event标志传递给legate.py脚本以生成event_graph_legate PDF 文件。

图 6 和图 7 显示了在四个 GPUS 上执行使用 cuNumeric 一节中的简单示例时生成的数据流和事件图。

A chart representing data dependency for different APIs used in cuNumeric Python code.
图 8 。 cuNumeric 数据流图示例
A data flow chart representing step-by-step execution of partitioned cuNumeric Python code and its interdependencies.
图 9 。事件图格式的简单 cuNumeric 代码执行的探查器输出

cuNumeric 现状和未来计划

cuNumeric 目前正在进行中。正在逐步增加对未实施 NumPy 运营商的支持。在 cuNumeric 的 alpha 版本和最新版本( v23.01 )之间, API 覆盖率从 25% 增加到 60% 。对于当前不支持的 API ,将提供警告并调用规范 NumPy 。 API reference 中提供了可用功能的完整列表。

虽然 cuNumeric v23.01 为许多 cuNumeric 应用程序提供了良好的弱缩放结果,但众所周知,某些改进将导致某些 API /用例的性能达到峰值。接下来的几个版本将专注于提高性能,力争在 2023 年实现 API 的全面覆盖。

总结

本文介绍了 cuNumeric ,这是一个基于 Legion 编程系统的 NumPy 的替代品。它透明地加速 NumPy 程序并将其分发给任何规模和能力的机器,通常通过更改单个模块导入语句。 cuNumeric 通过将 NumPy 应用程序接口转换为 Legate 编程模型并利用 Legion 运行时的性能和可扩展性来实现这一点。

 

Tags