GPU 内存分配#
JAX 在运行第一个 JAX 操作时将预先分配 75% 的总 GPU 内存。预分配可最大限度地减少分配开销和内存碎片,但有时会导致内存不足 (OOM) 错误。如果您的 JAX 进程因 OOM 而失败,可以使用以下环境变量来覆盖默认行为:
XLA_PYTHON_CLIENT_PREALLOCATE=false这会禁用预分配行为。JAX 将按需分配 GPU 内存,从而可能减少总体内存使用量。但是,此行为更容易导致 GPU 内存碎片,这意味着使用大部分可用 GPU 内存的 JAX 程序在禁用预分配时可能会出现 OOM。
XLA_PYTHON_CLIENT_MEM_FRACTION=.XX如果启用了预分配,这将使 JAX 预分配总 GPU 内存的 XX%,而不是默认的 75%。降低预分配量可以解决 JAX 程序启动时发生的 OOM 问题。
XLA_PYTHON_CLIENT_ALLOCATOR=platform这会使 JAX 按需分配精确所需的内存,并释放不再需要的内存(请注意,这是唯一会释放 GPU 内存而不是重用它的配置)。这非常慢,因此不建议常规使用,但可能有助于以最小的 GPU 内存占用运行或调试 OOM 故障。
OOM 故障的常见原因#
- 同时运行多个 JAX 进程。
可以使用
XLA_PYTHON_CLIENT_MEM_FRACTION为每个进程分配适量的内存,或将XLA_PYTHON_CLIENT_PREALLOCATE=false设置为 false。- 同时运行 JAX 和 GPU TensorFlow。
TensorFlow 默认也进行预分配,因此这类似于同时运行多个 JAX 进程。
一种解决方案是使用仅 CPU 的 TensorFlow(例如,如果您只使用 TF 进行数据加载)。您可以使用命令
tf.config.experimental.set_visible_devices([], "GPU")来阻止 TensorFlow 使用 GPU。或者,使用
XLA_PYTHON_CLIENT_MEM_FRACTION或XLA_PYTHON_CLIENT_PREALLOCATE。还有一些类似的选项可以配置 TensorFlow 的 GPU 内存分配(TF1 中的 gpu_memory_fraction 和 allow_growth,应在传递给tf.Session的tf.ConfigProto中设置。有关 TF2 的信息,请参阅 使用 GPU:限制 GPU 内存增长)。- 在显示 GPU 上运行 JAX。
使用
XLA_PYTHON_CLIENT_MEM_FRACTION或XLA_PYTHON_CLIENT_PREALLOCATE。- 禁用重构 HLO 传递
有时禁用自动重构 HLO 传递有利于避免编译器做出糟糕的重构选择。可以通过分别设置
jax.config.update('jax_compiler_enable_remat_pass', True)或jax.config.update('jax_compiler_enable_remat_pass', False)来启用/禁用该传递。启用或禁用自动重构传递会在计算和内存之间产生不同的权衡。但请注意,该算法比较基础,您通常可以通过禁用自动重构传递并使用 jax.remat API 手动进行重构,来获得更好的计算和内存权衡。
实验性功能#
此处的功能是实验性的,必须谨慎尝试。
TF_GPU_ALLOCATOR=cuda_malloc_async这将用 cudaMallocAsync 替换 XLA 自带的 BFC 内存分配器。这将消除大的固定预分配,并使用一个不断增长的内存池。预期的好处是无需设置 XLA_PYTHON_CLIENT_MEM_FRACTION。
风险如下:
内存碎片不同,因此如果您接近限制,因碎片导致的 OOM 情况将有所不同。
分配时间不会在开始时全部支付,而是在需要增加内存池时发生。因此,您可能会在开始时经历较低的速度稳定性,对于基准测试,忽略前几次迭代将更加重要。
可以通过预先分配相当大的内存块来规避风险,同时仍能从不断增长的内存池中获益。这可以通过 TF_CUDA_MALLOC_ASYNC_SUPPORTED_PREALLOC=N 来实现。如果 N 为 -1,它将预先分配与默认分配量相同的大小。否则,它是您想要预分配的字节数。