MPI_Isend/MPI_Recv和MPI_Send/MPI_Irecv的区别

3

对于MPI中的异步通信,以下哪种方式更好(从性能、可靠性、可读性等方面考虑):

  • 使用MPI_Isend和缓冲区,然后在接收方准备就绪时使用MPI_Iprobe和MPI_Recv进行一次接收;或者
  • 使用带有缓冲区的MPI_Irecv(以便始终有MPI_Irecv发布并具有足够的缓冲区),然后在发送方准备就绪时使用MPI_Send?

通信场景是必须异步交换数据,到达时间不重要,两个进程都有工作量。只考虑整体性能(特别是无阻塞)。

下面是一个最小化的工作示例(我没有包括工作负载,因此时间可能没有意义)。

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char const *argv[]) {
  MPI_Init(NULL, NULL);
  int world_size, world_rank;

  MPI_Comm_size(MPI_COMM_WORLD, &world_size);
  MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

  if (world_rank == 0 && world_size != 2) {
    fprintf(stderr, "This example requires two MPI processes.\n");
    MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
  }

  /* Non Blocking Send */
  int buf[100] = {0};

  MPI_Barrier(MPI_COMM_WORLD);
  double time = MPI_Wtime();
  if (world_rank == 1) {
    MPI_Request request;
    MPI_Isend(buf, 100, MPI_INT, 0, 0, MPI_COMM_WORLD, &request);
    MPI_Wait(&request, MPI_STATUS_IGNORE);
  } else {
    MPI_Recv(buf, 100, MPI_INT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
  }
  time = MPI_Wtime() - time;
  printf("rank = %d, time = %f sec\n", world_rank, time);
  MPI_Barrier(MPI_COMM_WORLD);

  usleep(100);
  if (world_rank == 0) {
    printf("---\n");
  }

  /* Non Blocking Receive */
  MPI_Barrier(MPI_COMM_WORLD);
  time = MPI_Wtime();
  if (world_rank == 1) {
    MPI_Send(buf, 100, MPI_INT, 0, 0, MPI_COMM_WORLD);
  } else {
    MPI_Request request;
    MPI_Irecv(buf, 100, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);
    MPI_Wait(&request, MPI_STATUS_IGNORE);
  }
  time = MPI_Wtime() - time;
  printf("rank = %d, time = %f sec\n", world_rank, time);
  MPI_Barrier(MPI_COMM_WORLD);

  MPI_Finalize();
  return 0;
}

在我的电脑上,这将生成:
rank = 0, time = 0.000035 sec
rank = 1, time = 0.000036 sec
---
rank = 0, time = 0.000035 sec
rank = 1, time = 0.000026 sec

感谢您已经提供的答案,祝您有愉快的一天 :)

1
后者避免了意外消息(可能导致内存使用增加)。此外,请记住,如果没有匹配的接收已发布,则MPI_Send()可能会阻塞。这些都是选择第二个选项的两个原因。 - Gilles Gouaillardet
1个回答

1

一个通用的经验法则是:

  1. 接收数据,放入缓冲区。这个过程很快,但你可能需要等待消息到达。
  2. 再次将一些数据发送到缓冲区中;这是一个长时间的步骤。

反过来做也不错,但你可以更好地重叠发送和接收。

因此,应该选择你描述的第二种情况。此外,如果你不使用非阻塞通信,请注意死锁。关于死锁,请参见MPI Send and receive questions

读者练习:

在以下片段中,假设所有缓冲区都已分配足够的大小。此外,rank和size分别表示每个进程的排名和MPI进程的总数。对于每个片段,请注意它是否会死锁,并解释原因。报告性能问题。

片段1:

int ireq = 0;
for (int p=0; p<size; p++)
if (p!=rank)
MPI_Isend(sbuffers[p],buflen,MPI_INT,p,0,comm,&(reqs[ireq++]));
for (int p=0; p<size; p++)
if (p!=rank)
MPI_Recv(rbuffer,buflen,MPI_INT,p,0,comm,MPI_STATUS_IGNORE);
MPI_Waitall(size-1,reqs,MPI_STATUSES_IGNORE);

代码片段2:

int ireq = 0;
for (int p=0; p<size; p++)
if (p!=rank)
MPI_Irecv(rbuffers[p],buflen,MPI_INT,p,0,comm,&(reqs[ireq++]));
MPI_Waitall(size-1,reqs,MPI_STATUSES_IGNORE);
for (int p=0; p<size; p++)
if (p!=rank)
MPI_Send(sbuffer,buflen,MPI_INT,p,0,comm);

解决方案:

代码片段1存在性能问题,应该将recv和send的顺序颠倒。

代码片段2由于Waitall而出现死锁。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接