MPI Broadcast and Collective Communication

本文介绍了MPI中的集合通信概念,并详细探讨了广播技术及其在并行编程中的应用。文章对比了自定义广播函数与MPI内置函数MPI_Bcast的性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下内容翻译自:MPI Broadcast and Collective Communication

到目前为止,在MPI教程中,我们已经审视了两个进程之间的点对点通信。本课是集合通信部分的开始。集合通信是一种所有通信进程都参与其中的通信方式。在本课中,我们将讨论集合通信的含义,并讨论标准集体例程——广播。

注——本网站的所有代码均位于GitHub上。本教程的代码位于tutorials/mpi-broadcast-and-collective-communication/code下。

集合通信和同步点

集合通信需要记住的一点是,它在进程间设置同步点。这意味着所有进程在它们全部开始执行之前必须到达其代码中的某个点。

在详细讨论合通信例程之前,让我们更详细地研究同步。事实证明,MPI有一个专门用于同步进程的特殊函数:

MPI_Barrier(MPI_Comm communicator)

该函数的名称非常具有描述力——该函数产生一个障碍,并且通信器中的任何进程都不能通过该障碍,直到所有这些进程调用该函数为止。这是一个例子。想象一下水平轴表示程序的执行,圆圈表示不同的过程:

MPI_Barrier

进程0首先在第一次快照(T 1)时调用MPI_Barrier。当进程0在屏障上挂起时,进程1和进程3最终到达(T 2)。当进程2最后进入屏障(T 3)时,所有进程再次开始执行(T 4)。

MPI_Barrier可以用于很多事情。主要用途之一是同步一个程序,以便并行代码部分可以准确计时。

想知道MPI_Barrier是如何实现的?当然你可以 ? 你还记得发送和接收教程中的环形程序吗?为了让你回忆起来,我们编写了一个程序,以环形方式在所有进程中传递令牌。 这种类型的程序是实现屏障的最简单方法之一,因为在所有进程一起工作之前,令牌无法完成传递。这种类型的程序是实现一个屏障最简单的方法之一,因为在所有进程协同工作之前,令牌不能完成传递。

关于同步的最后一点注意事项——请始终记住您所做的每个集合调用都进行了同步。换句话说,如果您无法成功完成MPI_Barrier,那么您也无法成功完成任何集合调用。如果您尝试调用MPI_Barrier或其他集合例程而未确保通信器中的所有进程也会调用它,则您的程序将空闲。这可能会让初学者感到困惑,所以要小心!

使用MPI_Bcast进行广播

广播是标准的集合通信技术之一。在广播过程中,一个进程向通信器中的所有进程发送相同的数据。广播的主要用途之一是将用户输入发送到并行程序,或向所有进程发送配置参数。

广播的通信模式如下所示:

broadcast_pattern

在这个例子中,进程0是根进程,它有数据的初始副本。所有其他进程都会收到数据副本。

在MPI中,可以使用MPI_Bcast完成广播。函数原型如下所示:

MPI_Bcast(
    void* data,
    int count,
    MPI_Datatype datatype,
    int root,
    MPI_Comm communicator)

尽管根进程和接收者进程执行不同的工作,但它们都调用相同的MPI_Bcast函数。当根进程(在我们的例子中,它是进程0)调用MPI_Bcast时,data变量将被发送到所有其他进程。当所有接收者进程调用MPI_Bcast时,data变量将被来自根进程的数据填充。

使用MPI_SendMPI_Recv进行广播

起初,似乎MPI_Bcast只是MPI_SendMPI_Recv的简单封装。事实上,我们现在可以制作这个功能封装。我们的函数my_bcast位于bcast.c中。它采用与MPI_Bcast相同的参数,如下所示:

void my_bcast(void* data, int count, MPI_Datatype datatype, int root,
              MPI_Comm communicator) {
  int world_rank;
  MPI_Comm_rank(communicator, &world_rank);
  int world_size;
  MPI_Comm_size(communicator, &world_size);

  if (world_rank == root) {
    // If we are the root process, send our data to everyone
    int i;
    for (i = 0; i < world_size; i++) {
      if (i != world_rank) {
        MPI_Send(data, count, datatype, i, 0, communicator);
      }
    }
  } else {
    // If we are a receiver process, receive the data from the root
    MPI_Recv(data, count, datatype, root, 0, communicator,
             MPI_STATUS_IGNORE);
  }
}

根进程将数据发送给其他进程,而其他进程则从根进程接收数据。很简单,对吧?如果您从repo的tutorials目录运行my_bcast程序,则输出应该与此类似。

>>> cd tutorials
>>> ./run.py my_bcast
mpirun -n 4 ./my_bcast
Process 0 broadcasting data 100
Process 2 received data 100 from root process
Process 3 received data 100 from root process
Process 1 received data 100 from root process

相信与否,我们的功能效率实际上非常低下!想象一下,每个进程只有一个输出/输入网络连接。我们的函数仅使用一个来自进程0的网络连接来发送所有数据。更智能的实现是基于树的通信算法,可以一次使用更多可用的网络链接。例如:

broadcast_tree

在上图中,进程0开始时将数据发送给进程1。与我们前面的例子类似,在第二阶段进程0也向进程2发送数据。这个例子的不同之处在于,进程1现在帮助完成根进程将数据转发到进程3。在第二阶段,一次使用两个网络连接。在树形通信的每个后续阶段,网络利用率都会翻倍,直到所有进程都收到数据。

你认为你可以编码吗?编写这段代码有点超出了本课的目的。如果你感觉很勇敢,那么《Parallel Programming with MPI》就是一本很好的书,它带有上述问题的完整代码例子。

MPI_BcastMPI_SendMPI_Recv的比较

MPI_Bcast实现利用类似的树广播算法来实现良好的网络利用率。我们的广播功能相比MPI_Bcast如何?我们可以运行compare_bcast,这是一个课程代码(compare_bcast.c)中包含的示例程序。在查看代码之前,我们先来看一下MPI的一个定时功能——MPI_WtimeMPI_Wtime不接受任何参数,它只是返回自过去某一时间以来的一个浮点数。与C的time函数类似,您可以在整个程序中调用多个MPI_Wtime函数,并减去它们之间的差以获取代码段的时间。

我们来看看比较my_bcastMPI_Bcast的代码。

for (i = 0; i < num_trials; i++) {
  // Time my_bcast
  // Synchronize before starting timing
  MPI_Barrier(MPI_COMM_WORLD);
  total_my_bcast_time -= MPI_Wtime();
  my_bcast(data, num_elements, MPI_INT, 0, MPI_COMM_WORLD);
  // Synchronize again before obtaining final time
  MPI_Barrier(MPI_COMM_WORLD);
  total_my_bcast_time += MPI_Wtime();

  // Time MPI_Bcast
  MPI_Barrier(MPI_COMM_WORLD);
  total_mpi_bcast_time -= MPI_Wtime();
  MPI_Bcast(data, num_elements, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Barrier(MPI_COMM_WORLD);
  total_mpi_bcast_time += MPI_Wtime();
}

在这个代码中,num_trials是一个变量,说明应该执行多少个时间实验。我们通过两个不同的变量跟踪两个函数的累计时间。在程序结束时打印平均时间。要查看完整的代码,请查看课程代码中的compare_bcast.c

如果您从repo的tutorials目录运行compare_bcast程序,则输出应与此类似。

>>> cd tutorials
>>> ./run.py compare_bcast
/home/kendall/bin/mpirun -n 16 -machinefile hosts ./compare_bcast 100000 10
Data size = 400000, Trials = 10
Avg my_bcast time = 0.510873
Avg MPI_Bcast time = 0.126835

运行脚本使用16个处理器执行代码,每个广播执行100,000个整数,并进行10次测量时间。正如你所看到的,我使用通过以太网连接的16个处理器的实验显示,我们的简单实现和MPI的实现之间存在显着的时间差异。这里是不同规模的时间结果。

Processorsmy_bcastMPI_Bcast
20.03440.0344
40.10250.0817
80.23850.1084
160.51090.1296

正如你所看到的,在两个处理器上两个实现没有区别。这是因为在使用两个处理器时,MPI_Bcast的树实现不提供任何额外的网络利用率。但是,即使只有16个处理器,差异也可以清楚地观察到。

尝试自己运行代码并在更大规模上进行实验!

结论/接下来

有点理解集合程序了?在接下来的MPI教程中,我会介绍其他重要的集合通信程序 ——gathering 和 scattering

对于所有课程,请转到MPI教程页面。

想贡献?

这个网站完全托管在 GitHub上。本网站不再由原作者(Wes Kendall)积极贡献,但它放在GitHub上,希望其他人可以编写高质量的MPI教程。点击这里获取更多关于如何贡献的信息。

此文档描述了Fortranmpi的使用。 Fortran語言 1 MPI 平行計算程式設計 1 第一章 前言 5 1.1 MPI 平行計算軟體 6 1.2 國家高速網路與計算中心的平行計算環境 7 1.3 在IBM 電腦系統上如何使用MPI 8 1.3.1 IBM 電腦系統的MPI Fortran程式編譯指令 8 1.3.2 IBM 電腦系統的 Job command file 8 1.3.3 IBM 電腦系統的平行程式的執行指令 11 1.4 在PC Cluster上如何使用MPI 13 1.4.1 PC Cluster上的MPI Fortran程式編譯指令 13 1.4.2 PC Cluster 上的 Job command file 14 1.4.3 PC Cluster 上的平行程式執行指令 15 第二章 無邊界資料交換的平行程式 16 2.1 MPI 基本指令 17 2.1.1 mpif.h include file 17 2.1.2 MPI_INIT, MPI_FINALIZE 17 2.1.3 MPI_COMM_SIZE, MPI_COMM_RANK 18 2.1.4 MPI_SEND, MPI_RECV 19 2.2 無邊界資料交換的循序程式 T2SEQ 21 2.3 資料不切割的平行程式 T2CP 23 2.4 MPI_SCATTER,MPI_GATHER,MPI_REDUCE 28 2.4.1 MPI_SCATTER,MPI_GATHER 28 2.4.2 MPI_REDUCE, MPI_ALLREDUCE 30 2.5 資料切割的平行程式 T2DCP 32 第三章 需要邊界資料交換的平行程式 36 3.1 MPI_SENDRECV, MPI_BCAST 37 3.1.1 MPI_SENDRECV 37 3.1.2 MPI_BCAST 37 3.2 邊界資料交換的循序程式 T3SEQ 39 3.3 資料不切割的邊界資料交換平行程式 T3CP 40 3.4 資料切割的邊界資料交換平行程式(一) T3DCP_1 47 3.5 資料切割的邊界資料交換平行程式 (二) T3DCP_2 52 第四章 格點數不能整除的平行程式 57 4.1 格點數不能整除的循序程式 T4SEQ 58 4.2. MPI_SCATTERV、MPI_GATHERV 60 4.3 MPI_PACK、UNPACK、BARRIER、WTIME 62 4.3.1 MPI_PACK、MPI_UNPACK 62 4.3.2 MPI_BARRIER、MPI_WTIME 65 4.4 資料切割的平行程式 T4DCP 67 第五章 多維陣列的平行程式 72 5.1 多維陣列的循序程式 T5SEQ 73 5.2 多維陣列資料不切割的平行程式 T5CP 76 5.3 多維陣列末維資料切割的平行程式 T5DCP 85 5.4 與二維切割有關的 MPI 副程式 92 5.4.1 垂直座標圖示法則 (Cartesian Topology) 92 5.4.2 界定二維切割的 MPI 副程式MPI_CART_CREATE、 93 MPI_CART_COORDS、MPI_CART_SHIFT 93 5.4.3 定義固定間隔資料的 MPI 副程式 96 MPI_TYPE_VECTOR、MPI_TYPE_COMMIT 96 5.5 多維陣列末二維切割的平行程式 T5_2D 98 第六章 MPI程式的效率提昇 112 6.1 Nonblocking資料傳送 113 6.2 資料傳送的合併 122 6.3 以邊界資料計算取代邊界資料交換 126 6.4 輸出入資料的安排 128 6.4.1 事先切割輸入資料 128 6.4.2 事後收集切割過的輸出資料 131 第七章 導出的資料類別 133 7.1 導出的資料類別 134 7.2 陣列的轉換 139 7.3 兩方迴歸與管線法 149 第八章 多方依賴及SOR解法 155 8.1 四方依賴及SOR解法 156 8.2 黑白點間隔 SOR 解法 159 8.3 斑馬線 SOR 解法 168 8.4 八方依賴與四色點間隔SOR解法 175 第九章 有限元素法程式 183 9.1 有限元素法的循序程式 184 9.2 有限元素法的平行程式 187 附錄一 撰寫C語言的MPI程式 197 參考書目 200 Parallel Processing without Partition of 1-D Arrays 201 Parallel Processing with Partition of 1-D Arrays 202 Parallel on the 2nd Dimension of 2-D Arrays without Partition 203 Parallel on the 2nd Dimension of 2-D Arrays with Partition 204 Partition on the 3rd dimension of 3-D Arrays 205
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值