如何在并行程序中指定哪个进程运行在哪个节点上
How to specify which processes run on which node in a parallel program
我正在 运行在 Intel Sandy Bridge 集群上的 16 节点分区上安装我的 MPI 程序。每个节点有两个处理器,每个处理器有 8 个内核。我用 "mpirun -n 256 ./myprogram" 开始了 运行。现在我需要每个节点上的一个代表性进程报告该节点的两个处理器消耗的功率(使用 RAPL)。我的问题是如何 select 这个过程。例如,如果保证进程将分配给节点 1-16、17-32、33-48 等,那么我可以只检查进程的 MPI 等级并决定它是否应该报告功率。 numactl 可以用来跨多个节点绑定大量进程吗?
MPI 标准未涵盖 (AFAIK) 在特定节点上放置特定等级的问题。但是,每个实现 and/or 机器 can/is 可能会提出此功能。例如,这可以通过 MPI 启动器的选项(mpirun
、mpiexec
、srun
、prun
、ortrun
、[在此处添加您首选的 mpi 启动器], ...),或通过批处理调度程序(如果适用)。因此,要获取此特定信息,我鼓励您参考 MPI 库文档、批处理调度程序文档或机器文档。
但是,您想要的功能独立于实际的 MPI 进程放置。无论进程如何放置在您的计算节点上,您都可以非常轻松地实现它来工作。这可以通过这种方式实现:
- 通过
MPI_Get_processor_name()
、gethostname()
或任何其他您认为足够的方式查询当前进程 运行 所在的节点的名称。 MPI_Get_processor_name()
作为 MPI 标准,出于可移植性的原因我会推荐它。
- 通过
MPI_Allgather()
为每个进程收集值以了解彼此的节点名称。
- 对于每个节点上排名最小的进程 运行ning,执行报告功率测量所需的任何操作。
这可能是这样的:
#include <mpi.h>
#include <iostream>
#include <cstring>
bool amIFirstOnNode() {
int rank, size;
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &size );
char names[size][MPI_MAX_PROCESSOR_NAME];
int len;
MPI_Get_processor_name( names[rank], &len );
MPI_Allgather( MPI_IN_PLACE, 0, 0, names[0], MPI_MAX_PROCESSOR_NAME, MPI_CHAR, MPI_COMM_WORLD );
int lower = 0;
while ( std::strncmp( names[rank], names[lower], MPI_MAX_PROCESSOR_NAME ) != 0 ) {
lower++;
}
return lower == rank;
}
int main( int argc, char *argv[] ) {
MPI_Init( &argc, &argv );
int rank;
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
bool nodeMaster = amIFirstOnNode();
if ( nodeMaster ) {
std::cout << "[" << rank << "] master process on the node\n";
}
else {
std::cout << "[" << rank << "] not master process on the node\n";
}
MPI_Finalize();
return 0;
}
关于跨节点使用 numactl
,这也是可行的,但高度依赖于您的环境。例如,在我自己的双路节点环境中 运行,每个 socket/NUMA 节点有一个 MPI 进程,我有时会使用我的这个 numa_bind.sh
脚本:
#!/bin/bash
PPN=2
numactl --cpunodebind=$(( $PMI_ID % $PPN )) --membind=$(( $PMI_ID % $PPN )) "$@"
这样称呼:
mpirun -ppn 2 numa_bind.sh my_mpi_binary [my_mpi_binary options]
当然,这假设您的环境设置 PMI_ID
或等效...
如果您使用 MPI 3.x 实现,您可以使用 MPI_Comm_split_type
[1] 和 MPI_COMM_TYPE_SHARED
作为 split_type
参数。这会将通信器(在您的情况下,当然 MPI_COMM_WORLD
)拆分为子通信器,这些子通信器正是集群的共享内存区域。然后每个子通信器都有一个本地根,它可以是您的代表进程。
[1] 第 247-248 页,MPI:消息传递接口标准(版本 3.0 或 3.1)
我正在 运行在 Intel Sandy Bridge 集群上的 16 节点分区上安装我的 MPI 程序。每个节点有两个处理器,每个处理器有 8 个内核。我用 "mpirun -n 256 ./myprogram" 开始了 运行。现在我需要每个节点上的一个代表性进程报告该节点的两个处理器消耗的功率(使用 RAPL)。我的问题是如何 select 这个过程。例如,如果保证进程将分配给节点 1-16、17-32、33-48 等,那么我可以只检查进程的 MPI 等级并决定它是否应该报告功率。 numactl 可以用来跨多个节点绑定大量进程吗?
MPI 标准未涵盖 (AFAIK) 在特定节点上放置特定等级的问题。但是,每个实现 and/or 机器 can/is 可能会提出此功能。例如,这可以通过 MPI 启动器的选项(mpirun
、mpiexec
、srun
、prun
、ortrun
、[在此处添加您首选的 mpi 启动器], ...),或通过批处理调度程序(如果适用)。因此,要获取此特定信息,我鼓励您参考 MPI 库文档、批处理调度程序文档或机器文档。
但是,您想要的功能独立于实际的 MPI 进程放置。无论进程如何放置在您的计算节点上,您都可以非常轻松地实现它来工作。这可以通过这种方式实现:
- 通过
MPI_Get_processor_name()
、gethostname()
或任何其他您认为足够的方式查询当前进程 运行 所在的节点的名称。MPI_Get_processor_name()
作为 MPI 标准,出于可移植性的原因我会推荐它。 - 通过
MPI_Allgather()
为每个进程收集值以了解彼此的节点名称。 - 对于每个节点上排名最小的进程 运行ning,执行报告功率测量所需的任何操作。
这可能是这样的:
#include <mpi.h>
#include <iostream>
#include <cstring>
bool amIFirstOnNode() {
int rank, size;
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &size );
char names[size][MPI_MAX_PROCESSOR_NAME];
int len;
MPI_Get_processor_name( names[rank], &len );
MPI_Allgather( MPI_IN_PLACE, 0, 0, names[0], MPI_MAX_PROCESSOR_NAME, MPI_CHAR, MPI_COMM_WORLD );
int lower = 0;
while ( std::strncmp( names[rank], names[lower], MPI_MAX_PROCESSOR_NAME ) != 0 ) {
lower++;
}
return lower == rank;
}
int main( int argc, char *argv[] ) {
MPI_Init( &argc, &argv );
int rank;
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
bool nodeMaster = amIFirstOnNode();
if ( nodeMaster ) {
std::cout << "[" << rank << "] master process on the node\n";
}
else {
std::cout << "[" << rank << "] not master process on the node\n";
}
MPI_Finalize();
return 0;
}
关于跨节点使用 numactl
,这也是可行的,但高度依赖于您的环境。例如,在我自己的双路节点环境中 运行,每个 socket/NUMA 节点有一个 MPI 进程,我有时会使用我的这个 numa_bind.sh
脚本:
#!/bin/bash
PPN=2
numactl --cpunodebind=$(( $PMI_ID % $PPN )) --membind=$(( $PMI_ID % $PPN )) "$@"
这样称呼:
mpirun -ppn 2 numa_bind.sh my_mpi_binary [my_mpi_binary options]
当然,这假设您的环境设置 PMI_ID
或等效...
如果您使用 MPI 3.x 实现,您可以使用 MPI_Comm_split_type
[1] 和 MPI_COMM_TYPE_SHARED
作为 split_type
参数。这会将通信器(在您的情况下,当然 MPI_COMM_WORLD
)拆分为子通信器,这些子通信器正是集群的共享内存区域。然后每个子通信器都有一个本地根,它可以是您的代表进程。
[1] 第 247-248 页,MPI:消息传递接口标准(版本 3.0 或 3.1)