分析设备内存#

注意

2023 年 5 月更新:我们建议使用 Tensorboard 分析 进行设备内存分析。在进行分析后,打开 Tensorboard 分析器的 memory_viewer 选项卡,以获取更详细和易于理解的设备内存使用情况。

JAX 设备内存分析器允许我们探索 JAX 程序如何以及为何使用 GPU 或 TPU 内存。例如,它可以用于

  • 找出在给定时间哪些数组和可执行文件位于 GPU 内存中,或

  • 跟踪内存泄漏。

安装#

JAX 设备内存分析器会发出可以用 pprof (google/pprof) 解释的输出。首先,按照其 安装说明 安装 pprof。在撰写本文时,安装 pprof 要求首先安装 Go 版本 1.16+、Graphviz,然后运行

go install github.com/google/pprof@latest

它将 pprof 安装为 $GOPATH/bin/pprof,其中 GOPATH 默认情况下为 ~/go

注意

来自 google/pprofpprof 版本与作为 gperftools 包一部分发布的同名旧工具不同。 gperftools 版本的 pprof 不适用于 JAX。

理解 JAX 程序如何使用 GPU 或 TPU 内存#

设备内存分析器的一个常见用途是找出 JAX 程序为什么使用了大量 GPU 或 TPU 内存,例如在调试内存不足问题时。

要将设备内存分析结果捕获到磁盘,请使用 jax.profiler.save_device_memory_profile()。例如,考虑以下 Python 程序

import jax
import jax.numpy as jnp
import jax.profiler

def func1(x):
  return jnp.tile(x, 10) * 0.5

def func2(x):
  y = func1(x)
  return y, jnp.tile(x, 10) + 1

x = jax.random.normal(jax.random.key(42), (1000, 1000))
y, z = func2(x)

z.block_until_ready()

jax.profiler.save_device_memory_profile("memory.prof")

如果我们首先运行上面的程序,然后执行

pprof --web memory.prof

pprof 将打开一个网页浏览器,其中包含以下设备内存分析结果的调用图格式的可视化

Device memory profiling example

调用图是每个活动缓冲区分配时 Python 堆栈的可视化。例如,在这种特定情况下,可视化显示 func2 及其被调用者负责分配 76.30MB,其中 38.15MB 是在从 func1func2 的调用中分配的。有关如何解释调用图可视化的更多信息,请参阅 pprof 文档

使用 jax.jit() 编译的函数对设备内存分析器是透明的。也就是说,在 jit 编译的函数内部分配的任何内存都将归因于该函数整体。

在示例中,对 block_until_ready() 的调用是为了确保 func2 在收集设备内存分析结果之前完成。有关更多详细信息,请参阅 异步调度

调试内存泄漏#

我们还可以使用 JAX 设备内存分析器来追踪内存泄漏,方法是使用 pprof 可视化在不同时间拍摄的两个设备内存分析结果之间的内存使用情况变化。例如,考虑以下程序,它将 JAX 数组累积到一个不断增长的 Python 列表中。

import jax
import jax.numpy as jnp
import jax.profiler

def afunction():
  return jax.random.normal(jax.random.key(77), (1000000,))

z = afunction()

def anotherfunc():
  arrays = []
  for i in range(1, 10):
    x = jax.random.normal(jax.random.key(42), (i, 10000))
    arrays.append(x)
    x.block_until_ready()
    jax.profiler.save_device_memory_profile(f"memory{i}.prof")

anotherfunc()

如果我们只是在执行结束时可视化设备内存分析结果(memory9.prof),可能不会很明显的是,anotherfunc 中循环的每次迭代都会累积更多的设备内存分配

pprof --web memory9.prof

Device memory profile at end of execution

afunction 内部的较大但固定的分配支配了分析结果,但不会随着时间的推移而增长。

通过使用 pprof--diff_base 功能 来可视化循环迭代期间内存使用情况的变化,我们可以识别出程序内存使用量随时间增长的原因

pprof --web --diff_base memory1.prof memory9.prof

Device memory profile at end of execution

可视化显示内存增长可以归因于 anotherfunc 内部的对 normal 的调用。