mpi4py循环创建共享数组引发内存泄漏,求正确实现方法
问题
开发的应用需执行大量循环,每次创建共享数组覆盖之前的实例,但运行时出现内存泄漏。复现该问题的最小示例可通过mpirun -np 2 python example.py运行:
from mpi4py import MPI import numpy as np comm = MPI.COMM_WORLD for i in range(0,100000000): # 创建指定大小的double类型共享数组 size = 100000 itemsize = MPI.DOUBLE.Get_size() if comm.Get_rank() == 0: nbytes = size * itemsize else: nbytes = 0 # 进程0创建共享内存块,进程1获取其句柄(MPI中称为窗口) win = MPI.Win.Allocate_shared(nbytes, itemsize, comm=comm) # 创建指向共享内存的numpy数组 buf, itemsize = win.Shared_query(0) assert itemsize == MPI.DOUBLE.Get_size() ary = np.ndarray(buffer=buf, dtype='d', shape=(size,))
运行后内存占用持续增长,甚至可能崩溃。经排查,问题源于窗口创建操作,即使简化为仅循环创建窗口的代码,泄漏依然存在。尝试调用win.Free()后泄漏有所减轻,但在top中仍能观察到明显的内存增长。
请问实现循环创建共享数组并可完全释放内存的正确方式是什么?
解决方案
要彻底解决内存泄漏,需确保MPI窗口资源被完整回收,同时避免Python对象引用残留导致的资源无法释放,具体操作如下:
显式同步并释放MPI窗口
win.Free()是释放窗口资源的核心操作,但必须在所有进程完成对窗口的操作后执行。添加comm.Barrier()同步所有进程,避免部分进程提前释放窗口导致其他进程访问失效,同时确保所有进程都执行窗口释放操作,防止资源残留。销毁numpy数组引用
虽然numpy数组不持有共享内存的所有权,但如果数组对象被Python引用计数保留,可能间接阻碍窗口资源的回收。通过del ary显式销毁数组引用,切断对共享内存缓冲区的关联。强制触发Python垃圾回收
高频循环中Python自动垃圾回收可能存在延迟,调用gc.collect()强制回收未被引用的对象,避免临时对象堆积占用内存。
修改后的完整代码示例:
from mpi4py import MPI import numpy as np import gc comm = MPI.COMM_WORLD for i in range(0,100000000): # 初始化共享数组参数 size = 100000 itemsize = MPI.DOUBLE.Get_size() nbytes = size * itemsize if comm.Get_rank() == 0 else 0 # 创建共享内存窗口 win = MPI.Win.Allocate_shared(nbytes, itemsize, comm=comm) # 绑定numpy数组到共享内存 buf, itemsize = win.Shared_query(0) assert itemsize == MPI.DOUBLE.Get_size() ary = np.ndarray(buffer=buf, dtype='d', shape=(size,)) # --- 此处添加你的业务逻辑代码 --- # 按顺序释放资源 del ary # 销毁numpy数组引用 comm.Barrier() # 同步所有进程 win.Free() # 释放MPI窗口 gc.collect() # 强制垃圾回收
- 额外注意事项
- 必须保证所有进程都执行
win.Free(),不能仅在部分进程中调用,否则会导致MPI内部状态不一致或资源泄漏。 - 避免在循环中创建全局变量,所有临时变量需限制在循环作用域内,减少不必要的引用残留。
- 若问题仍存在,可尝试升级mpi4py及底层MPI库(如OpenMPI、MPICH)的版本,部分旧版本存在共享内存窗口的底层bug。
- 必须保证所有进程都执行
内容的提问来源于stack exchange,提问作者interatomic
相关产品推荐
相关产品推荐

