并行计算MPI(入门)——通信模式


我们已经了解了点对点通信的几乎所有重要内容。 有一件事我还没讲到,也许大家有兴趣了解一下 MPI。

如果您做过一些实验,您可能会注意到在进行屏蔽通信时出现的一些尴尬情况。 有时,阻塞通信似乎没有阻塞,而有时,却似乎阻塞了。 真奇怪。

对于那些没有遇到过这种奇怪行为的人,这里有一个小例子可以说服你们:

#include <iostream>
#include <unistd.h>
#include <mpi.h>

int main(int argc, char **argv) {
  MPI_Init(&argc, &argv);

  int size, rank;
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);

  // We have two buffers, one big and one small
  constexpr int small_count = 50;
  constexpr int big_count   = 100000;
  int buff1[small_count];
  int buff2[big_count];

  // Waiting time, play with this value if you think the difference is due to the quantity of data to send
  constexpr int waiting_time = 5; // in seconds

  if (rank == 0) {
    // We initialize both buffers
    for (int i=0; i < big_count; ++i) {
      if (i < small_count)
	buff1[i] = i;
      buff2[i] = i;
    }

    // Synchronisation 1
    MPI_Barrier(MPI_COMM_WORLD);

    // We register the current time
    double time = -MPI_Wtime();

    // We send the buffer immediately
    MPI_Send(buff1, small_count, MPI_INT, 1, 0, MPI_COMM_WORLD);

    // We print the time it took us to complete this send
    std::cout << "Time elapsed to complete blocking send 1 : " << time + MPI_Wtime() << "s" << std::endl;

    // Synchronisation 2
    MPI_Barrier(MPI_COMM_WORLD);
    time = -MPI_Wtime();

    // We send the second buffer
    MPI_Send(buff2, big_count, MPI_INT, 1, 1, MPI_COMM_WORLD);

    // We print the time again
    std::cout << "Time elapsed to complete blocking send 2 : " << time + MPI_Wtime() << "s" << std::endl;
  }
  else {
    // Synchronisation 1
    MPI_Barrier(MPI_COMM_WORLD);
    sleep(waiting_time);

    // Receiving buffer 1
    MPI_Recv(buff1, small_count, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    std::cout << "Just received buffer 1" << std::endl;

    // Synchronisation 2
    MPI_Barrier(MPI_COMM_WORLD);
    sleep(waiting_time);

    // Receiving buffer 2
    MPI_Recv(buff2, big_count, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    std::cout << "Just received buffer 2" << std::endl;
  }

  MPI_Finalize();
  
  return 0;
}

本例显示了两个阻塞通信。 一个在小缓冲区(50 个元素)上执行,另一个在大缓冲区(100000 个元素)上执行。 进程 0 只发送缓冲区,并打印发送时间和发送完成时间。 进程 1 将等待(演示阻塞特性),然后接收,并在接收完成后向用户发出警告。
您可以很容易地注意到这种奇怪的行为:第一次发送比第一次接收早完成,而对于大缓冲区来说,两个操作似乎是同时进行的。

标准模式(MPI_Send)

标准模式(MPI_Send)实际上是一种 “非模式”,它允许 MPI 实现选择哪种通信模式更可取。 这可能在很大程度上取决于实现。 在 OpenMPI 中,观察到的行为是,对于短消息,发送会自动缓冲,而对于长消息,消息的发送模式在某种程度上接近同步模式。

缓冲模式(MPI_Bsend)

缓冲模式(MPI_Bsend)将所有要发送的数据存储在临时缓冲区中,然后返回执行,就像非阻塞发送模式一样。 这样做的好处是,即使尚未调用相应的阻塞 Recv,也能立即继续执行。 另一方面,缓冲模式会将缓冲区的所有数据复制到内存的另一个区域,从而重复数据。 如果要传输大量数据,这可能会对内存造成危害。\

就绪模式(MPI_Rsend)

只有在相应的接收已被调用的情况下,就绪模式(MPI_Rsend)才能启动。 这样,您的程序就可以从信息初始化的一些额外开销中获得时间。 如果尚未调用相应的 Recv,那么将接收到的消息 //might// 将是定义不明确的,因此在调用就绪模式之前,必须确保接收进程已请求 Recv。

同步模式(MPI_Ssend)

最后,同步模式(MPI_Ssend)将等待相应的 Recv 完成。 数据传输将在这一精确时刻进行,确保两个进程都为传输做好了准备。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值