Python multiprocessing Pool.map指定进程却占用全部核心问题
核心问题现象
在单12核机器上使用multiprocessing.Pool(processes=8)时,htop显示8个目标进程,符合预期;但在双20核(共40核)机器上,即使指定8个进程,htop却显示数百个python EA_launch.py条目,占满所有40核,导致性能骤降。
可能原因及分析
evaluate函数内部触发了额外并行
检查evaluate函数是否在内部调用了多进程/多线程逻辑,或是使用了默认自动并行的第三方库(如numpy的BLAS后端、pandas的某些操作、机器学习框架)。这类库通常会根据机器核心数自动分配资源,当Pool的8个进程各自都启动全核心并行时,总线程/进程数会达到8×40=320,远超机器承载能力,引发大量上下文切换,拖慢整体速度。进程启动方式导致的资源继承问题
Unix系统下multiprocessing默认用fork方式启动子进程,若主进程在创建Pool前已初始化了线程池、全局状态等资源,子进程会继承这些资源,进而触发意外的并行逻辑。单CPU机器核心数少,这类副作用表现不明显;双CPU机器核心充足,问题会被放大。htop显示的是线程而非进程
htop默认会显示线程(按H键可切换进程/线程视图),如果evaluate内部使用多线程,htop会把每个线程当作独立条目展示,看起来进程数远超8个,但实际是线程。双CPU核心多,线程拉满后会占满所有核心,导致CPU调度效率急剧下降。
验证与解决方法
确认进程/线程数量
在evaluate函数开头加入print(os.getpid()),运行后统计输出的PID数量:import os def evaluate(obj): print(f"当前进程ID: {os.getpid()}") # 业务逻辑 ...- 若PID数量为8,说明是线程导致的htop显示“进程数过多”;若PID远超8,则是
evaluate内部创建了额外进程。
- 若PID数量为8,说明是线程导致的htop显示“进程数过多”;若PID远超8,则是
限制子进程的并行资源
针对自动并行的第三方库,在evaluate内部设置环境变量限制核心数:def evaluate(obj): import os # 限制OpenBLAS/MKL使用1核心 os.environ['OPENBLAS_NUM_THREADS'] = '1' os.environ['MKL_NUM_THREADS'] = '1' # 业务逻辑 ...更换进程启动方式
使用spawn方式创建Pool,避免继承主进程的线程或全局资源:import multiprocessing as mp if __name__ == '__main__': mp.set_start_method('spawn') pool = mp.Pool(processes=8) pool.map(evaluate, object_list)调整htop显示设置
按H键切换到进程视图,确认实际进程数量是否为8,区分进程与线程的显示差异。
内容的提问来源于stack exchange,提问作者TOP1RM

