如何通过MPI实现集群节点与计算核心的解耦?
现在需要把这个任务跑4次,但单节点负载太高影响性能,打算用mpi4py实现跨节点并发。期望: - `script.py`中的`num_proc`对应节点数 - `function`内`Parallel`的`n_jobs`对应每节点核心数(ppn) 拟定的`script.py`框架: ```python from mpi4py import MPI comm = MPI.COMM_WORLD my_rank = comm.Get_rank() # 节点编号 # num_proc设为5:节点0接收数据,节点1-4执行计算 num_proc = comm.Get_size() if my_rank != 0: comm.send(function(), dest=0) else: for proc_id in range(1, num_proc): result = comm.recv(source=proc_id) save(result)
TORQUE提交脚本示例:
#PBS -l nodes=5:ppn=28 # ppn为每节点核心数 ... mpirun -np $np python script.py
但通常$np是nodes*ppn,我希望num_proc对应节点数、n_jobs对应ppn,有几个疑问:
- 怎么让MPI实现节点与核心的解耦?
- 如果提交
mpirun -np 5 python script.py,joblib会不会在每个节点分配28核,mpi4py把任务分发到4个节点? - mpirun的绑定选项该怎么选?
解决方案
1. 节点与核心的解耦实现
要让num_proc对应节点数,核心是每个节点只启动1个MPI进程,而非每个核心启动1个。具体操作:
- 在TORQUE提交脚本中,指定
nodes=5:ppn=28后,设置mpirun的-np参数为节点数(即5),同时通过--map-by node参数强制MPI按节点分配进程,确保每个节点仅运行1个MPI进程:
#PBS -l nodes=5:ppn=28 # 自动提取节点数(也可以直接写5) num_nodes=$(cat $PBS_NODEFILE | uniq | wc -l) mpirun -np $num_nodes --map-by node python script.py
- 此时Python代码中
comm.Get_size()会返回5(节点总数),my_rank对应每个节点的唯一编号,实现了节点级的MPI并发,每个MPI进程再通过joblib调用该节点的28核做计算。
2. joblib核分配的有效性验证
当你用mpirun -np 5 python script.py且加上--map-by node时:
- 每个节点上只会运行1个Python(MPI)进程
- 该进程内的
Parallel(n_jobs=28)会调用当前节点的全部28核执行并行计算 - mpi4py会把计算任务分发到4个工作节点(rank1-4),rank0负责接收结果并保存,完全符合你的需求。
⚠️ 注意:如果省略--map-by node,MPI可能会把5个进程集中到少数节点上,导致部分节点空闲、部分节点过载,所以这个参数是实现节点级并发的关键。
3. mpirun绑定选项的选择
绑定选项的核心是避免joblib多进程与MPI进程抢占核心,推荐两种设置:
--bind-to none:让MPI不绑定进程到特定核心,完全交给joblib管理节点内的核心分配。这种方式最简单,适配你的场景(每个节点仅1个MPI进程),joblib可以自由调度全部28核。--bind-to socket:如果你的服务器是NUMA架构,把MPI进程绑定到节点的socket(NUMA节点),能减少跨socket的内存访问开销,提升计算效率。比如28核可能对应2个socket,绑定后joblib在socket内调度核心,性能更优。
最终的TORQUE提交脚本示例:
#PBS -l nodes=5:ppn=28 #PBS -N multi_node_job #PBS -q your_queue_name num_nodes=$(cat $PBS_NODEFILE | uniq | wc -l) mpirun -np $num_nodes --map-by node --bind-to none python script.py
内容的提问来源于stack exchange,提问作者Brian Barry
相关产品推荐
相关产品推荐

