首先,确切的答案取决于:(1) 用法,即函数输入参数,(2) MPI 实现质量和细节,以及(3) 您正在使用的硬件。通常,(2) 和 (3) 是相关的,例如当硬件供应商为其网络优化 MPI 时。
一般来说,融合 MPI 集合体更适合较小的消息,因为启动成本可能很重要,并且如果调用之间的计算时间存在差异,则应该最小化阻塞集合体所需要的同步。对于较大的消息,目标应该是尽量减少发送的数据量。
比如,理论上MPI_Reduce_scatter_block
应该比MPI_Reduce
后面好MPI_Scatter
,虽然前者往往是按照后者来实现的,这样并没有真正的优势。在 MPI 的大多数实现中,实现质量和使用频率之间存在相关性,供应商显然优化了机器合同要求的那些功能。
另一方面,如果一个人在蓝色基因上,MPI_Reduce_scatter_block
使用 using MPI_Allreduce
,它比MPI_Reduce
和MPI_Scatter
组合进行更多的通信,实际上要快得多。这是我最近发现的一个有趣的违反 MPI 中的性能自洽原则(该原则在“自洽 MPI 性能指南”中有更详细的描述)。
在 scatter+gather 与 allgather 的具体情况下,请考虑在前者中,所有数据必须进出单个进程,这使其成为瓶颈,而在 allgather 中,数据可以立即流入和流出所有秩,因为所有等级都有一些数据要发送给所有其他等级。但是,在某些网络上,一次从所有节点发送数据不一定是一个好主意。
最后,回答这个问题的最好方法是在你的代码中执行以下操作并通过实验来回答这个问题。
#ifdef TWO_MPI_CALLS_ARE_BETTER_THAN_ONE
MPI_Scatter(..)
MPI_Gather(..)
#else
MPI_Allgather(..)
#endif
一个更好的选择是让您的代码在前两次迭代期间通过实验测量它,然后在剩余的迭代中使用更快的那个:
const int use_allgather = 1;
const int use_scatter_then_gather = 2;
int algorithm = 0;
double t0 = 0.0, t1 = 0.0, dt1 = 0.0, dt2 = 0.0;
while (..)
{
if ( (iteration==0 && algorithm==0) || algorithm==use_scatter_then_gather )
{
t0 = MPI_Wtime();
MPI_Scatter(..);
MPI_Gather(..);
t1 = MPI_Wtime();
dt1 = t1-t0;
}
else if ( (iteration==1 && algorithm==0) || algorithm==use_allgather)
{
t0 = MPI_Wtime();
MPI_Allgather(..);
t1 = MPI_Wtime();
dt2 = t1-t0;
}
if (iteration==1)
{
dt2<dt1 ? algorithm=use_allgather : algorithm=use_scatter_then_gather;
}
}