本文档展示了如何使用 OpenMPI 的 --map-by rankfile:file=rankfile.txt
选项,将 MPI 进程精确绑定到指定的 CPU 核心上运行 CP2K,适用于高性能计算中避免 CPU 核心重叠的需求。
1. 系统 NUMA 架构说明
假设你通过 lscpu
或 numactl -H
得到如下 NUMA 结构:
lscpu | grep "NUMA"
NUMA 节点: 4
NUMA 节点0 CPU: 0-23
NUMA 节点1 CPU: 24-47
NUMA 节点2 CPU: 48-71
NUMA 节点3 CPU: 72-95
numactl -H
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
node 0 size: 95334 MB
node 0 free: 91202 MB
node 1 cpus: 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
node 1 size: 96755 MB
node 1 free: 85439 MB
node 2 cpus: 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
node 2 size: 96755 MB
node 2 free: 95458 MB
node 3 cpus: 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
node 3 size: 96739 MB
node 3 free: 94271 MB
node distances:
node 0 1 2 3
0: 10 21 21 21
1: 21 10 21 21
2: 21 21 10 21
3: 21 21 21 10
2. 目标任务
将 48 个 MPI 进程绑定到 CPU 核心 0 到 47(NUMA 节点1 和 节点2),避免与另一个任务冲突。
将 48 个 MPI 进程绑定到 CPU 核心 48 到 95(NUMA 节点2 和 节点3),避免与另一个任务冲突。
3. 创建 rankfile.txt
创建 rankfile 文件,手动指定每个 rank 对应的 CPU:
可通过如下脚本创建:
# 遍历0-47的rank编号,生成绑定文件,将rank i绑定到CPU核心(i),输出写入rankfile.txt
for i in $(seq 0 47); do echo "rank $i=localhost slot=$((i))"; done > rankfile0_47.txt
# 遍历0-47的rank编号,生成绑定文件,将rank i绑定到CPU核心(48+i),输出写入rankfile.txt
for i in $(seq 0 47); do echo "rank $i=localhost slot=$((48+i))"; done > rankfile48_95.txt
for i in $(seq 0 47); do ... done
这是一个循环,从0循环到47,总共48次,i
变量依次取这些数字。$(seq 0 47)
会生成一个序列0 1 2 ... 47
。echo "rank $i=localhost slot=$((48+i))"
这条命令生成一行文本,格式是:rank <rank编号>=localhost slot=<CPU核心编号>
rank $i
表示第i
个MPI进程(rank)。localhost
指当前运行程序的这台机器,也就是本地主机。(如果你要在多台机器上运行MPI进程,替换localhost
为对应远程主机的名字,例如:node01)slot=$((48+i))
是CPU核心编号,用数学表达式计算,从48开始依次加1,绑定rank 0到CPU 48,rank 1到CPU 49,依此类推直到rank 47绑定到CPU 95。
> rankfile.txt
将上面循环输出的所有行写入到文件rankfile.txt
,覆盖该文件。
rankfile0_47.txt
rank 0=localhost slot=0
rank 1=localhost slot=1
rank 2=localhost slot=2
rank 3=localhost slot=3
rank 4=localhost slot=4
rank 5=localhost slot=5
rank 6=localhost slot=6
rank 7=localhost slot=7
rank 8=localhost slot=8
rank 9=localhost slot=9
rank 10=localhost slot=10
rank 11=localhost slot=11
rank 12=localhost slot=12
rank 13=localhost slot=13
rank 14=localhost slot=14
rank 15=localhost slot=15
rank 16=localhost slot=16
rank 17=localhost slot=17
rank 18=localhost slot=18
rank 19=localhost slot=19
rank 20=localhost slot=20
rank 21=localhost slot=21
rank 22=localhost slot=22
rank 23=localhost slot=23
rank 24=localhost slot=24
rank 25=localhost slot=25
rank 26=localhost slot=26
rank 27=localhost slot=27
rank 28=localhost slot=28
rank 29=localhost slot=29
rank 30=localhost slot=30
rank 31=localhost slot=31
rank 32=localhost slot=32
rank 33=localhost slot=33
rank 34=localhost slot=34
rank 35=localhost slot=35
rank 36=localhost slot=36
rank 37=localhost slot=37
rank 38=localhost slot=38
rank 39=localhost slot=39
rank 40=localhost slot=40
rank 41=localhost slot=41
rank 42=localhost slot=42
rank 43=localhost slot=43
rank 44=localhost slot=44
rank 45=localhost slot=45
rank 46=localhost slot=46
rank 47=localhost slot=47
rankfile48_95.txt
rank 0=localhost slot=48
rank 1=localhost slot=49
rank 2=localhost slot=50
rank 3=localhost slot=51
rank 4=localhost slot=52
rank 5=localhost slot=53
rank 6=localhost slot=54
rank 7=localhost slot=55
rank 8=localhost slot=56
rank 9=localhost slot=57
rank 10=localhost slot=58
rank 11=localhost slot=59
rank 12=localhost slot=60
rank 13=localhost slot=61
rank 14=localhost slot=62
rank 15=localhost slot=63
rank 16=localhost slot=64
rank 17=localhost slot=65
rank 18=localhost slot=66
rank 19=localhost slot=67
rank 20=localhost slot=68
rank 21=localhost slot=69
rank 22=localhost slot=70
rank 23=localhost slot=71
rank 24=localhost slot=72
rank 25=localhost slot=73
rank 26=localhost slot=74
rank 27=localhost slot=75
rank 28=localhost slot=76
rank 29=localhost slot=77
rank 30=localhost slot=78
rank 31=localhost slot=79
rank 32=localhost slot=80
rank 33=localhost slot=81
rank 34=localhost slot=82
rank 35=localhost slot=83
rank 36=localhost slot=84
rank 37=localhost slot=85
rank 38=localhost slot=86
rank 39=localhost slot=87
rank 40=localhost slot=88
rank 41=localhost slot=89
rank 42=localhost slot=90
rank 43=localhost slot=91
rank 44=localhost slot=92
rank 45=localhost slot=93
rank 46=localhost slot=94
rank 47=localhost slot=95
4、使用 mpirun + rankfile 精确绑定 CPU 核心运行 CP2K
cd your_project_folder1
mpirun -np 48 --map-by rankfile:file=rankfile0_47.txt --report-bindings /your_cp2k_path/cp2k.popt -i cp2k.inp -o out.txt
cd your_project_folder2
mpirun -np 48 --map-by rankfile:file=rankfile48_95.txt --report-bindings /your_cp2k_path/cp2k.popt -i cp2k.inp -o out.txt
以下是对命令 mpirun -np 48 --map-by rankfile:file=rankfile.txt --report-bindings
的详细注释:
命令整体作用
启动一个包含 48 个 MPI 进程 的并行程序,通过 自定义进程映射文件 (rankfile.txt
) 精确控制每个进程的绑定位置,并 打印进程绑定信息 以供调试。
参数逐项解析
mpirun
- Open MPI 提供的命令,用于启动并行应用程序。
-np 48
- 目的:指定总进程数。
- 含义:启动 48 个 MPI 进程。应用程序将在这 48 个进程中并行执行。
--map-by rankfile:file=rankfile.txt
- 核心作用:通过自定义文件控制进程绑定。
- 关键点:
rankfile:file=
:指定映射规则来自文件rankfile.txt
。- 文件格式:
rankfile.txt
每行定义一个进程的绑定规则,格式如下: rank <进程ID>=<主机名> slot=<核心ID列表> - 示例:
rank 0=node1 slot=0 # 进程0绑定到 node1 的核心0
rank 1=node1 slot=1 # 进程1绑定到 node1 的核心1
rank 2=node2 slot=0-3 # 进程2绑定到 node2 的核心0~3(允许多核绑定) - 优势:精确控制每个进程的绑定位置(核心/插槽),适合复杂拓扑或特定性能需求。
--report-bindings
- 作用:在运行时 打印每个进程的实际绑定位置。
- 输出示例:
[node1:12345] Rank 0 bound to socket 0[core 0][thread 0]
[node1:12345] Rank 1 bound to socket 0[core 1][thread 0]
[node2:56789] Rank 2 bound to socket 0[cores 0-3][threads 0-3] - 用途:验证绑定是否符合预期,调试性能问题(如跨 NUMA 内存访问)。
常见问题
- 文件路径问题:
rankfile.txt
必须可被所有节点访问(建议使用绝对路径)。 - 核心冲突:同一核心被多进程绑定会导致性能下降(除非明确允许多线程)。
- 兼容性:
rankfile
是 Open MPI 特有功能,其他 MPI 实现(如 Intel MPI)需改用等效机制。
通过此命令,您可以精确控制并行程序的资源分配,最大化硬件利用率。