【问题标题】:MPI_Bcast: Efficiency advantages?MPI_Bcast:效率优势?
【发布时间】:2011-10-21 00:56:58
【问题描述】:

在 MPI 中,MPI_Bcast 是纯粹的便利函数,还是使用它而不是循环遍历所有等级并向所有等级发送相同的消息有效率优势?

理由:MPI_Bcast 将消息发送给所有人(包括 root)的行为对我来说很不方便,所以我宁愿不使用它,除非有充分的理由,或者可以不发送发给 root 的消息。

【问题讨论】:

  • 为什么让 MPI_Bcast 的根参与问题?

标签: c performance parallel-processing mpi message-passing


【解决方案1】:

使用 MPI_Bcast 肯定会比自己滚动更有效。在所有 MPI 实现中都做了很多工作,以根据消息大小和通信架构等因素优化集体操作。

例如,MPI_Bcast in MPICH2 would use a different algorithm depending on the size of the message。对于短消息,使用二叉树来最小化处理负载和延迟。对于长消息,它被实现为一个二叉树散点图,然后是一个集合。

此外,HPC 供应商通常会提供 MPI 实施,以有效利用底层互连,尤其是对于集体操作。例如,可以使用hardware supported multicast schemeuse bespoke algorithms that can take advantage of the existing interconnects

【讨论】:

    【解决方案2】:

    集体沟通可能比你自己的沟通快很多。所有 MPI 实现都花费大量时间来处理这些例程以提高速度。

    如果您经常想做集体类型的事情,但只针对一部分任务,那么您可能想要创建自己的子通信器并在这些通信器上使用 BCAST 等。

    【讨论】:

    • 即使有备用通信,该通信的根仍将参与集体操作。 OP 特别提到让 root 获取消息是一个问题。
    • 根“发送”消息给自己是一个空操作,甚至不是内存拷贝。
    【解决方案3】:

    MPI_Bcast 根据定义将消息从一个进程(“根”)发送到所有其他。它也可能比循环遍历所有进程要快一些。例如,mpich2 实现使用二叉树来分发消息。

    如果你不想广播到 MPI_COMM_WORLD,但你想定义子组,你可以这样做:

    #include <stdio.h>
    #include "mpi.h"
    
    #define NPROCS 8
    
    int main(int argc, char **argv)
    {
        int rank, new_rank, sendbuf, recvbuf,
        ranks1[4]={0,1,2,3}, ranks2[4]={4,5,6,7};
    
        MPI_Group orig_group, new_group;
        MPI_Comm new_comm;
    
        MPI_Init(&argc,&argv);
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        sendbuf = rank;
    
        /* Extract the original group handle */
        MPI_Comm_group(MPI_COMM_WORLD, &orig_group);
    
        /* Divide tasks into two groups based on rank */
        if (rank < NPROCS/2) {
            MPI_Group_incl(orig_group, NPROCS/2, ranks1, &new_group);
        } else {
            MPI_Group_incl(orig_group, NPROCS/2, ranks2, &new_group);
        }
    
        /* Create new communicator and then perform some comm
         * Here, MPI_Allreduce, but you can MPI_Bcast at will
         */
        MPI_Comm_create(MPI_COMM_WORLD, new_group, &new_comm);
        MPI_Allreduce(&sendbuf, &recvbuf, 1, MPI_INT, MPI_SUM, new_comm);
        MPI_Group_rank (new_group, &new_rank);
    
        printf("rank= %d newrank= %d recvbuf= %d\n", rank, new_rank, recvbuf); 
    
        MPI_Finalize();
    } 
    

    这可能会产生类似以下的输出:

    rank= 7 newrank= 3 recvbuf= 22
    rank= 0 newrank= 0 recvbuf= 6 
    rank= 1 newrank= 1 recvbuf= 6 
    rank= 2 newrank= 2 recvbuf= 6 
    rank= 6 newrank= 2 recvbuf= 22
    rank= 3 newrank= 3 recvbuf= 6
    rank= 4 newrank= 0 recvbuf= 22
    rank= 5 newrank= 1 recvbuf= 22
    

    【讨论】:

      【解决方案4】:

      答案是,在一般情况下,MPI_Bcast 可能比循环更快。通常,MPI 集合针对各种消息大小、通信大小和特定等级布局进行了优化。

      也就是说,有可能在特定的消息大小、通信大小和排名布局上击败集体。例如,在非阻塞点对点调用(例如 ISend 和 Recv/IRecv)上的循环可能会更快……但可能仅限于一些特定的消息大小、通信大小和排名布局。

      如果您正在编码的特定算法需要 Bcast 模式(例如,所有等级从根获取相同的数据有效负载),则使用 Bcast 集合。一般来说,通过滚动你自己的“集体替代品”来增加复杂性是不值得的。

      如果算法需要一些其他消息模式,并且 Bcast 仅部分适合...那么可能值得您自己滚动...但我个人将这个标准设置得相当高。

      【讨论】:

      • 我同意。简单地调整集合体的参数(手动通过命令行参数/环境变量,或通过诸如 IntelMPI 的 mpitune 之类的东西自动)然后重写您自己的高性能集合体会更容易。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多