Ray集群VMEM占用过高致任务失败问题排查
问题描述
我在测试Python多进程框架Ray实现代码并行化:
- 笔记本(Core-i7-13800H、32GB内存)上原代码运行正常,使用
num_cpus=18 - 本地集群申请了50核CPU+200GB内存,但当
num_cpus>40时任务失败,日志显示疑似OOM错误:
^[[36m(func_parallel pid=70267)^[[0m [2024-11-01 10:33:28,278 E 70267 75787] logging.cc:108: Unhandled exception: N5boost10wrapexceptINS_6system12system_errorEEE. what(): thread: Resource temporarily unavailable [system:11] ^[[36m(func_parallel pid=70115)^[[0m OpenBLAS blas_thread_init: pthread_create failed for thread 1 of 50: Resource temporarily unavailable ^[[36m(func_parallel pid=70115)^[[0m OpenBLAS blas_thread_init: RLIMIT_NPROC 16384 current, 16384 max ^[[36m(func_parallel pid=69306)^[[0m 2024-11-01 10:33:29.811669: F external/local_tsl/tsl/platform/default/env.cc:74] Check failed: ret == 0 (11 vs. 0)Thread tf_numa_-1_Eigen creation via pthread_create() failed. ^[[36m(func_parallel pid=69306)^[[0m *** SIGABRT received at time=1730482409 on cpu 55 *** ^[[36m(func_parallel pid=69306)^[[0m PC: @ 0x14a9fbfa8cbb (unknown) raise ^[[36m(func_parallel pid=69306)^[[0m @ 0x14a9fcabc8c0 186786352 (unknown) ^[[33m(raylet)^[[0m A worker died or was killed while executing a task by an unexpected system error. To troubleshoot the problem, check the logs for the dead worker. RayTask ID: 4e9c3bd9b738a771af2ce9c4f66013ce9dbb521201000000 Worker ID: 52b96fe61ede380cde1d513fc65c222ba65a0100a2a1a8b00ad8f310 Node ID: ea8ebb2f21de741cf396d92301075881372f10000e39b7518e080cbb Worker IP address: Worker port: Worker PID: Worker exit type: SYSTEM_ERROR Worker exit detail: Worker unexpectedly exits with a connection error code 2. End of file. There are some potential root causes. (1) The process is killed by SIGKILL by OOM killer due to high memory usage. (2) ray stop --force is called. (3) The worker is crashed unexpectedly due to SIGSEGV or other unexpected errors.
- 通过
top观察:每个核RES仅1.5GB,但VMEM高达92GB,总VMEM超2TB,远超代码实际需求 - 编写简化复现代码(处理10000×10000矩阵的乘法、求和),测试Python Multiprocessing、Ray Core、Ray Multiprocessing Pool三种方式:
import os os.environ['RAY_DEDUP_LOGS'] = '0' import numpy as np import ray from ray.util.multiprocessing import Pool # Used by Ray Core @ray.remote def func_parallel(db): a = 0 for _ in range(5): a = a + np.sum(np.matmul(db, db)) print(a) return a # Used by Ray Multiprocessing Pool # def func_parallel(db): # a = 0 # for _ in range(5): # a = a + np.sum(np.matmul(db, db)) # print(a) # return a nloop = 50 mat = np.ones((10000, 10000)) # Tested with Ray Core ray.init(num_cpus=40, object_store_memory=80*1024*1024) db_ref = ray.put(mat) task = [func_parallel.remote(db_ref) for _ in range(nloop)] val = ray.get(task) # Tested with Ray Multiprocessing Pool # with Pool(ray_remote_args={'num_cpus': 40}) as pool: # val = pool.map(func_parallel, [mat for _ in range(nloop)], chunksize=1) print(val)
- 简化代码在笔记本正常运行,但集群中每个核VMEM仍超60GB,
num_cpus超阈值易失败 - 仅缓解方法:将
object_store_memory设为最小值(约80MB),可将VMEM降至每个核9GB(RES为1GB,符合矩阵大小);原代码设置后VMEM在5GB-27GB波动(疑似Ray重试),但num_cpus超阈值仍失败
核心问题
- 任务失败是否由高VMEM占用导致?
- 为何VMEM占用如此之高?
- 集群与笔记本运行Ray的差异是什么?笔记本无足够空间却能正常运行。
问题解答
1. 任务失败是否由高VMEM占用导致?
是,但并非直接物理内存耗尽,而是虚拟内存超限触发系统资源限制。从日志看,出现了pthread_create failed: Resource temporarily unavailable和RLIMIT_NPROC提示,说明系统进程/线程数达到上限;同时Ray提示OOM killer可能介入,高VMEM会导致系统页表膨胀,消耗额外内核内存,间接触发OOM或资源限制,最终导致worker进程崩溃。
2. 为何VMEM占用如此之高?
主要有三个原因:
- Ray对象存储的默认预分配:默认情况下Ray会为对象存储分配较大的虚拟内存缓冲区,即使实际使用的物理内存(RES)不高,虚拟内存也会被预占。当
object_store_memory未指定或设置过大时,每个worker进程都会预留大量虚拟内存空间,叠加50个任务后总VMEM急剧膨胀。 - 线性代数库的线程预创建:OpenBLAS、Eigen等库会根据CPU核心数自动创建线程池,集群核数多,这些库会尝试创建大量线程,每个线程会占用虚拟内存栈空间(默认几MB到几十MB),累加后推高VMEM。
- Ray集群模式的内存隔离策略:集群模式下Ray的worker进程隔离更彻底,每个worker会占用独立的虚拟内存空间,而笔记本环境下内存压力小,系统对虚拟内存的限制更宽松。
3. 集群与笔记本运行Ray的差异是什么?笔记本无足够空间却能正常运行。
- 系统资源限制不同:集群通常设置更严格的
RLIMIT_NPROC(进程/线程数上限)、虚拟内存配额,而笔记本默认限制宽松。笔记本即使虚拟内存超物理内存,会用磁盘交换空间兜底,但集群可能禁用或限制交换空间,且对单进程VMEM有隐性限制。 - Ray初始化配置差异:笔记本上Ray默认基于物理内存比例自动计算
object_store_memory,而集群中如果未显式设置,可能误判可用内存,预分配过大的虚拟内存缓冲区。另外,集群模式下Ray的worker进程隔离更彻底,每个worker会占用独立的虚拟内存空间,而笔记本可能共享部分内存页。 - 线性代数库行为差异:OpenBLAS等库会根据当前环境CPU核心数调整线程数,笔记本18核会创建对应线程池,而集群50核会尝试创建50个线程,线程栈的虚拟内存累加后差异巨大。
内容的提问来源于stack exchange,提问作者Bawb
相关产品推荐
相关产品推荐

