You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MPI4PY跨节点Spawn子进程失败问题求助

问题描述

在HPC集群上开发了一套基于MPI的程序:主进程为Python代码,通过mpi4py的MPI.COMM_SELF.Spawn启动N个运行Fortran可执行文件child的子进程,并使用MPI广播将字符串CNTRL发送给Fortran子进程。

集群每个节点含48核,当Spawn的子进程仅在单个节点内时,代码运行完全正常;但当申请跨节点的核心资源(例如通过SLURM申请2节点共65核,Spawn64个子进程)时,代码触发MPI_ERR_SPAWN错误,提示系统可用slot不足,无法Spawn进程。

相关代码

main.py

from mpi4py import MPI
from env import myclass

# Define message to send to Python
req = b'CNTRL'
# Define class locally
C = myclass(req)
# Call class function to perform communication
C.myfunc()

print('Python done')

env.py

from mpi4py import MPI

class myclass:
  def __init__(self,req):

    # sub_comm is an MPI intercommunicator
    self.sub_comm = MPI.COMM_SELF.Spawn('./child', args=[], maxprocs=32)
    # common_comm is an intracommunicator accross the python process and the spawned process.
    self.common_comm=self.sub_comm.Merge(False)
    self.size = self.common_comm.Get_size()
    self.rank = self.common_comm.Get_rank()
    
    # define message to send to fortran
    self.req = req 

  def myfunc(self):
    # Just to try, send a message
    self.common_comm.Bcast([self.req,  MPI.CHAR],   root=0)
    print('Python sent a broadcast message: ',self.req)
    self.closeMPI()

  def closeMPI(self):
    # free the (merged) intra communicator
    self.common_comm.Free()
    # disconnect the inter communicator is required to finalize the spawned process.
    self.sub_comm.Disconnect()

Fortran child程序

program child
  !
  use mpi
  !
  implicit none
  !
  integer :: parentcomm,intracomm,group,newgroup,newcomm
  integer :: rank,rank2,size,ierr,i
  character(len=5) :: request
  integer, allocatable, dimension(:) :: ranks
  !
  call MPI_INIT(ierr)
  call MPI_COMM_GET_PARENT(parentcomm, ierr)
  call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
  call MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
  call MPI_COMM_GROUP(MPI_COMM_WORLD, group, ierr)
  allocate(ranks(size))
  do i=1,size
    ranks(i) = i-1
  end do
  call MPI_GROUP_INCL(group,size,ranks,newgroup,ierr)
  call MPI_GROUP_SIZE(newgroup, size, ierr)
  call MPI_COMM_CREATE(MPI_COMM_WORLD, newgroup, newcomm, ierr)
  call MPI_INTERCOMM_MERGE(parentcomm, .true., intracomm, ierr)
  call MPI_COMM_RANK(intracomm, rank2, ierr)
  call MPI_COMM_SIZE(intracomm, size, ierr)

  call MPI_BCAST(request,5,MPI_CHAR,0,intracomm,ierr)
  if (rank == 0) print*, 'Message received in fortran: ',request

  if (rank == 1) print*, 'Child frees intracomm'
  call MPI_COMM_FREE(intracomm, ierr)
  if (rank == 1) print*, 'Child disconnects intercomm'
  call MPI_COMM_DISCONNECT(parentcomm, ierr)
  if (rank == 1) print*, 'Child finalises'
  call MPI_FINALIZE(ierr)
end program child

已尝试方案

  • 启动MPI进程时使用use-hwthread-cpus和bind-to-core参数
  • 从OpenMPI切换至MPICH,同步编译Fortran代码并重新安装mpi4py
  • 在MPI调用中指定节点列表

当前环境:Python 3.10.12 + mpi4py 3.1.5,尝试过OpenMPI 4.1.1和MPICH 4.1.2版本。


解决方法

1. 调整Spawn的通信域策略

MPI.COMM_SELF.Spawn默认仅在主进程所在节点分配资源,跨节点场景下需改用全局通信域:
修改env.py中的Spawn调用:

# 替换原COMM_SELF.Spawn为COMM_WORLD.Spawn
self.sub_comm = MPI.COMM_WORLD.Spawn('./child', args=[], maxprocs=32)

注意:启动主进程时需通过mpirun -n 1 python main.py将其加入全局MPI通信域,而非直接用python main.py启动。

2. 利用SLURM环境变量传递跨节点资源信息

通过SLURM提供的环境变量让MPI知晓整个作业的可用节点,修改env.py的Spawn逻辑:

import os
from mpi4py import MPI

class myclass:
  def __init__(self,req):
    info = MPI.Info.Create()
    # 获取SLURM节点列表并传递给Spawn
    nodelist = os.environ.get('SLURM_JOB_NODELIST')
    if nodelist:
        # OpenMPI用host参数,MPICH用nodes参数,根据实际环境调整
        info.Set('host', nodelist)
    # Spawn时传入资源信息
    self.sub_comm = MPI.COMM_SELF.Spawn('./child', args=[], maxprocs=32, info=info)
    # 后续原有逻辑不变...

3. 允许MPI进程超配(针对OpenMPI)

在启动主进程时添加--oversubscribe参数,允许MPI在已分配的slot内Spawn子进程:

mpirun -n 1 --oversubscribe python main.py

需确保作业申请的总核数≥主进程数+Spawn子进程数(比如申请65核对应1主+64子)。

4. 简化Fortran子进程的通信域操作

原代码中MPI_GROUP_INCL和MPI_COMM_CREATE属于冗余操作,会增加通信域冲突风险,修改后的Fortran代码:

program child
  use mpi
  implicit none
  integer :: parentcomm,intracomm
  integer :: rank,size,ierr
  character(len=5) :: request

  call MPI_INIT(ierr)
  call MPI_COMM_GET_PARENT(parentcomm, ierr)
  call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
  call MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)

  call MPI_INTERCOMM_MERGE(parentcomm, .true., intracomm, ierr)
  call MPI_BCAST(request,5,MPI_CHAR,0,intracomm,ierr)
  if (rank == 0) print*, 'Message received in fortran: ',request

  call MPI_COMM_FREE(intracomm, ierr)
  call MPI_COMM_DISCONNECT(parentcomm, ierr)
  call MPI_FINALIZE(ierr)
end program child

内容的提问来源于stack exchange,提问作者Giorgio Maria Cavallazzi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 01:45:10