MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
此函数不需要 rank 参数。它如何知道每个进程的等级?
我们应该调用MPI_COMM_RANK()
之前的广播,是否有任何数据结构(如通信器)存储进程的等级?
MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
此函数不需要 rank 参数。它如何知道每个进程的等级?
我们应该调用MPI_COMM_RANK()
之前的广播,是否有任何数据结构(如通信器)存储进程的等级?
也许您认为这不可能,但 MPI 库中的函数可以在内部进行与您用来获取进程等级或通信器大小相同的 MPI 调用。这就是为什么MPI_Bcast()
不需要调用进程的等级,因为它只是调用内部实现MPI_Comm_rank()
来获取它。以下是 Open MPI 中的一个实现中的一个小示例(更具体地说,这是来自提供实现集体操作的算法的框架的模块MPI_Bcast()
中的拆分二叉树实现):tuned
coll
int
ompi_coll_tuned_bcast_intra_split_bintree ( void* buffer,
int count,
struct ompi_datatype_t* datatype,
int root,
struct ompi_communicator_t* comm,
mca_coll_base_module_t *module,
uint32_t segsize )
{
...
int rank, size;
...
size = ompi_comm_size(comm);
rank = ompi_comm_rank(comm);
...
}
如您所见,它调用了MPI_Comm_size()
and的内部实现MPI_Comm_rank()
。这些是 Open MPI 中非常便宜的调用。进程的等级存储在与通信器关联的进程组中,并在创建通信器期间复制到通信器结构中的一个字段(以节省一些 CPU 周期来取消引用组的指针)(更多信息)信息参考openmpi-source/ompi/communicator/communicator.h
和openmpi-source/ompi/group/group.h
)。
事实上,没有任何 MPI 通信原语明确地占据调用进程的等级——它总是在内部解决。您只需指定从哪里发送数据(例如 in MPI_SEND
)或从哪里接收数据(例如 in MPI_RECV
)或在具有一个的集合操作中的数据根。
考虑三种可能的实现MPI_Bcast()
:
root+1
root+2
root+3
N
转发到rank xor 2^N
. 这是对数数量级。在这些场景中的每一个中,MPI_Bcast()
函数都知道哪个进程将获得下一条消息。在第一种和第三种情况下,任何非根进程都将简单地接收数据;第二,每个进程收到数据后会继续转发过程。但是,在所有实现中,发送和接收的顺序是确定的,基于哪个进程是根。(这就是为什么所有进程都必须调用MPI_Bcast()
,无论是否为root。)
你是对的,排名存储在通信器中,并且可以在MPI_Bcast
内部实现。创建通信器时分配等级。例如,MPI_COMM_WORLD
由MPI_Init
.
MPI_Comm_rank
只需从通信器获取排名值。无需在广播前调用它。然而,知道排名通常是进行任何有意义的编程所必需的。
请注意,由于MPI_Bcast
是集体调用,因此需要由通信器中的所有进程执行。
int root
, 是广播根的等级,本质上是 MPI 广播从等级根向所有其他等级发送消息
我也认为在 MPI_Init 之后调用以下内容是“最佳实践”
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
这将为每个处理器或内核分配一个从 0 到 n-1 的 int rank 值
和
MPI_Comm_size( MPI_COMM_WORLD, &Numprocs);
这将创建一个 int,其中 Numproces 是处理器的总数