将 JAX 工作负载移植到 Pathways

由于 JAX with Pathways 的分布式特性,某些操作可能因通信开销而无法很好地扩缩。虽然 Pathways 通过异步调度等功能最大限度地减少了这些开销,但在将 JAX 工作负载移植到 Pathways 或将 JAX with Pathways 工作负载扩缩到大量加速器时,您需要注意一些事项。

准备工作

请确保您已备妥:

进程索引

JAX with Pathways 会将 Pathways 集群中的所有设备视为本地设备。这简化了设备管理,并允许 JAX 利用所有可用资源。在实践中,这意味着:

  • 对于所有设备,jax.process_index() 始终为 0。
  • jax.devices() 和 jax.local_devices() 会返回整个作业中的所有 TPU 设备。

硬件类型和主机托管

为获得最佳性能,请将所有 Pathways 组件和用户作业放在同一 Google Cloud 云可用区中。使用大型 CPU,例如 IFRT 代理和资源管理器。我们建议至少使用专用 n2-standard-64,它配备 64 个 vCPU 和 256 GB 内存。

PathwaysUtils

Pathways-utils 是一个基于 Python 的 GitHub 代码库,提供必要的实用程序和工具,可让您简化在 Pathways on Cloud 架构上部署和执行 JAX 工作负载的过程。此软件包可处理云环境所需的调整,让 JAX 开发者能够专注于其核心机器学习工作流,只需进行极少的平台特定配置。具体而言,它提供:

  • “代理”JAX 后端:此自定义后端使您的 JAX 应用能够通过设置 JAX_PLATFORMS=proxy 环境变量来使用 Pathways 基础设施。
  • 集成式性能分析实用程序:性能分析功能,可让您了解应用的性能。通过使用 jax.profiler.start_trace 和 jax.profiler.start_server 等标准 JAX 性能分析 API,您不仅可以对 JAX 代码进行性能分析,还可以对底层 Pathways 组件进行性能分析,从而全面了解云环境中的执行情况。
  • 使用 Orbax 进行分布式检查点:一种自定义 Orbax 检查点处理程序,可让您在 Pathways 环境中使用 Orbax 库时使用分布式检查点并恢复检查点。此集成旨在无需对现有 Orbax 检查点代码进行任何更改即可运行,只要该代码导入 pathwaysutils 即可。
  • 弹性训练基元:提供基础弹性训练基元,您可以使用这些基元通过 Pathways 构建强大且可伸缩的训练工作流。这些基元可让您的训练作业动态适应可用资源的变化,从而提高云环境中的效率和弹性。

检查点

Orbax 已经过 Pathways 的全面测试,可用于通过 Cloud Storage 进行分布式检查点和恢复。当您 设置环境变量 ENABLE_PATHWAYS_PERSISTENCE=1 并在 train.py 中调用 import pathwaysutils; pathwaysutils.initialize() 时,系统会注册一个自定义 ArrayHandler,该 可通过 IFRT 代理高效处理检查点操作,从而允许加速器上的 Pathways 工作器直接保存和恢复数据。

共置 Python

Colocated Python 是一个开源 JAX API,可让您直接在 TPU 或 GPU 主机上运行用户指定的 Python 代码,这在多控制器 JAX 中更为直接。通过使用此方法,您可以避免在客户端和 TPU 机器之间传输数据,以执行计算密集型任务,例如数据加载和检查点。如需将 Pathways 集群配置为运行共置的 Python JAX API,请使用示例边车 Dockerfile 构建并推送边车容器映像。

数据加载

在训练期间,我们会反复从数据集中加载批量数据,以馈送给模型。为了避免加速器缺少工作,请务必使用高效的异步数据加载器,将批次分片到多个主机。使用 Pathways 运行训练时,数据加载器在 CPU 虚拟机(与多控制器设置中使用的 TPU 虚拟机不同)上运行,并将数据分派到 TPU 虚拟机。这会导致读取数据的延迟时间较长,但通过在 CPU 主机上预读 X 个批次并将读取的数据异步分派到 TPU,可以部分缓解这种情况。此解决方案足以满足中小型规模的运行需求。

在 GKE 上的头工作负载容器中运行多进程数据加载器(例如 Grain)时,该容器会与 pathways-proxy 和 pathways-rm 边车共享头节点。由于此共享架构,您必须配置以下内容:

  • 设置明确的工作器数量:在容器内,os.cpu_count() 会报告主机虚拟机的总 vCPU 数量,而不是容器的 CPU 限制。请传递明确的工作器数量(不超过容器的 CPU 限制),而不是从 os.cpu_count() 派生。
  • 装载内存支持的 /dev/shm 卷:多进程数据加载器使用 POSIX 共享内存 (/dev/shm) 进行进程间通信。确保在 /dev/shm 处装载了内存支持的 emptyDir (medium: Memory) 卷,以便预提取的批次不会超出默认容器共享内存限制。

为了在大规模运行时获得最佳性能,我们强烈建议您使用共置 Python 直接在加速器上运行数据流水线,从而共置输入数据流水线。这样可以消除 CPU 瓶颈,并利用 TPU 的快速互连进行数据传输。

您可以在 multihost_dataloading.py 的 RemoteIterator 实现中找到迁移基于 TFDS 的输入流水线的参考实现。此实现使用共置的 Python JAX API,以分布式方式在多控制器 JAX 和 Pathways 上运行。

Jax 版本控制

Pathways 版本与 JAX 版本紧密耦合,以确保兼容性和稳定性。为避免潜在问题,请验证您的 Pathways 工件和 JAX 版本是否一致。每个 Pathways 版本都会通过 jax-<version> 格式的标记明确指定兼容的 JAX 版本。

编译缓存

Pathways 永久性编译缓存是一项功能,可让 Pathways 服务器将编译后的 XLA 可执行文件存储在永久性位置(例如 Cloud Storage),以避免冗余编译。默认情况下,此功能处于启用状态。缓存的位置作为 --gcs_scratch_location 标志传递给资源管理器和 Pathways 工作器容器。为了将相关存储费用降至最低,缓存会将生命周期政策附加到 Cloud Storage 位置。每个 Cloud Storage 存储桶最多只能有 50 项政策。因此,我们建议在所有工作负载中使用一个通用的 Cloud Storage 位置。

如果您省略 --gcs_scratch_location 标志或将其留空,Pathways 会自动回退到内存中编译缓存。此缓存不会在 Pathways 部署中保留已编译的 XLA 可执行文件。

此缓存类似于 JAX 编译缓存,后者由 pathwaysutils.initialize() 针对 Pathways 工作负载停用。

编译缓存需要以下 Cloud Storage 权限:

  • storage.buckets.get:检索存储桶元数据。
  • storage.buckets.update:对于 Pathways 设置对象生命周期政策以强制执行 TTL 进行缓存逐出至关重要。
  • storage.objects.list:列出存储桶中的现有缓存对象。
  • storage.objects.create:将新编译的可执行文件写入缓存。
  • storage.objects.get:从存储桶中读取缓存的可执行文件。

分析

您可以使用 JAX 分析器生成 JAX 程序的跟踪记录。Pathways 支持两种常见方式:

  • 程序化
    • 通过 JAX 代码以编程方式捕获性能分析文件
  • 手动
    • 从 JAX 代码启动性能分析器服务器后,按需捕获性能分析文件

在这两种情况下,分析结果都会写入 Cloud Storage 存储桶。 Cloud Storage 存储桶中可能会创建多个跟踪记录文件,这些文件可能位于不同的时间戳文件夹下,例如:

  • 调用跟踪记录的主 Python 进程(通常是笔记本虚拟机):<jax-client-vm-name>.xplane.pb
  • Pathways IFRT 代理:client.<var>PATHWAYS_HEAD_NODE_NAME</var>.xplane.pb
  • Pathways 资源管理器:server.*<var>PATHWAYS_HEAD_NODE_NAME</var>.xplane.pb
  • Pathways 工作器:server.*<tpu-node-name>.xplane.pb

您可以通过运行以下命令,使用 TensorBoard 分析这些跟踪文件。如需详细了解 TensorBoard 及其所有性能分析工具,请参阅使用 Profiler 优化 TensorFlow 性能。

# verify trace files are present
gsutil ls -l -r gs://BUCKET/PREFIX

# View on tensorboard
tensorboard --logdir=gs://BUCKET/PREFIX

替换以下内容:

  • BUCKET:用于存储跟踪文件的 Cloud Storage 存储桶
  • PREFIX:Cloud Storage 存储桶中用于存储跟踪文件的路径

程序化性能剖析文件捕获

从代码内部捕获性能剖析。这些配置文件保存在 gs://<bucket>/<prefix> 内的时间戳目录下

import jax
import pathwaysutils

pathwaysutils.initialize()

jax.profiler.start_trace("gs://BUCKET/PREFIX")

# Run the operations to be profiled
key = jax.random.key(0)
x = jax.random.normal(key, (5000, 5000))
y = x @ x
y.block_until_ready()

jax.profiler.stop_trace()

手动捕获性能剖析文件

如需手动捕获性能分析信息,您必须从 Python 代码启动性能分析器服务器:

import jax
import pathwaysutils

pathwaysutils.initialize()
jax.profiler.start_server(jax_profiler_port)

# Your JAX code
jax.profiler.stop_server() # this is functionally a no-op

在性能分析器服务器运行时,您可以捕获性能剖析文件并将数据导出到目标 Cloud Storage 位置:

export DURATION_IN_SECS=6
export OUTPUT_DIR=gs://<var>BUCKET</var>/<var>PREFIX</var>

curl -d "{\"duration_ms\":\"${DURATION_IN_SECS} * 1000 }}\", \"repository_path\":\"${OUTPUT_DIR}\"}" -H "Content-Type: application/json" -X POST http://localhost:<jax_profiler_port>

您可以在程序的跟踪记录中找到 IFRT 代理客户端方法(例如 Compile 和 Execute)的时间信息。这些事件详细说明了在编译和执行期间与 IFRT 代理 gRPC 服务器的交互,它们会显示在名为 GrpcClientSessionUserFuturesWorkQueue 的线程上。通过在跟踪记录中检查此线程,您可以深入了解这些操作的性能。

XLA 标志

使用 Pathways 时,您必须在 pathways-proxy 容器中设置 XLA 标志。

使用 Cluster Toolkit(gcluster job submit 命令)时,请使用 --pathways-proxy-args 设置 XLA 标志:

--pathways-proxy-args="--xla_flag_1=value1 --xla_flag_2=value2"

HLO 转储

如需检查提供给 XLA 编译器的 HLO 输入,您可以使用 --pathways-proxy-args 配置 Pathways,将 HLO 转储到指定的 Cloud Storage 位置:

--pathways-proxy-args="--xla_dump_to=gs://BUCKET_NAME/HLO_PREFIX/"

后续步骤