【问题标题】:MPI - Equivalent of MPI_SENDRCV with asynchronous functionsMPI - 等效于具有异步功能的 MPI_SENDRCV
【发布时间】:2015-11-10 18:18:47
【问题描述】:

我知道MPI_SENDRECV 可以解决死锁问题(当我们使用经典的MPI_SENDMPI_RECV 函数时)。

我想知道MPI_SENDRECV(sent_to_process_1, receive_from_process_0)是否等同于:

MPI_ISEND(sent_to_process_1, request1)
MPI_IRECV(receive_from_process_0, request2)
MPI_WAIT(request1)
MPI_WAIT(request2)

具有异步 MPI_ISENDMPI_RECV 函数?

据我所知,MPI_ISENDMPI_RECV 创建了一个 fork(即 2 个进程)。所以如果我按照这个逻辑,MPI_ISEND 的第一次调用会生成 2 个进程。一个负责通信,另一个调用 MPI_RECV,它自己分叉 2 个进程。

但是一旦第一个MPI_ISEND的通信结束,第二个进程是否又调用MPI_IRECV?有了这个逻辑,上面的等价物似乎是无效的......

也许我应该改成这样:

MPI_ISEND(sent_to_process_1, request1)
MPI_WAIT(request1)
MPI_IRECV(receive_from_process_0, request2)
MPI_WAIT(request2)

但我认为这也可能造成死锁。

任何人都可以使用MPI_ISENDMPI_IRECVMPI_WAIT 给我另一个解决方案来获得与MPI_SEND_RECV 相同的行为吗?

【问题讨论】:

    标签: mpi openmpi inter-process-communicat


    【解决方案1】:

    问题和其他答案中有一些危险的思路。当您启动非阻塞 MPI 操作时,MPI 库不会创建新的进程/线程/等。我相信您正在考虑更像 OpenMP 的并行区域,在其中创建新线程/任务来完成某些工作。

    在 MPI 中,启动非阻塞操作就像告诉 MPI 库您有一些事情想要完成,只要 MPI 有机会执行它们。在实际完成时,有许多同样有效的选项:

    1. 当您调用阻塞完成函数(如MPI_WAITMPI_WAITALL)时,它们可能都会在稍后完成。这些函数保证在完成阻塞完成调用时,您作为参数传入的所有请求都已完成(在您的情况下,MPI_ISENDMPI_IRECV)。无论操作何时实际发生(请参阅接下来的几个项目符号),您作为应用程序在它们被MPI_WAITMPI_TEST 等函数实际标记为已完成之前都不能认为它们已完成。
    2. 这些操作可以在另一个 MPI 操作期间“在后台”完成。例如,如果您执行以下代码:

      MPI_Isend(..., MPI_COMM_WORLD, &req[0]);
      MPI_Irecv(..., MPI_COMM_WORLD, &req[1]);
      MPI_Barrier(MPI_COMM_WORLD);
      MPI_Waitall(2, req);
      

      MPI_ISENDMPI_IRECV 可能实际上会在MPI_BARRIER 期间在后台进行数据传输。这是因为作为一个应用程序,您在 MPI_BARRIER 调用期间将应用程序的“控制”转移到 MPI 库。这使库可以在它想要的任何正在进行的 MPI 操作上取得进展。最有可能的是,当 MPI_BARRIER 完成时,大多数其他最先完成的事情也是如此。

    3. 某些 MPI 库允许您指定您想要一个“进度线程”。这告诉 MPI 库在后台启动另一个线程(不是那个线程!= 进程),当您的应用程序在主线程中继续运行时,该线程实际上会为您执行 MPI 操作。

    请记住,所有这些最终都需要您实际调用 MPI_WAITMPI_TEST 或其他类似函数以确保您的操作实际完成,但这些都不会产生新的线程或进程来执行当您调用非阻塞函数时为您工作。这些实际上就像您将它们放在要做的事情列表中一样(实际上,这是大多数 MPI 库实现它们的方式)。

    考虑如何实现MPI_SENDRECV 的最佳方法是使用一个完成函数进行两次非阻塞调用:

    MPI_Isend(..., MPI_COMM_WORLD, &req[0]);
    MPI_Irecv(..., MPI_COMM_WORLD, &req[1]);
    MPI_Waitall(2, req);
    

    【讨论】:

      【解决方案2】:

      我通常如何在节点 i 上与节点 i+1 通信:

      mpi_isend(send_to_process_iPlus1, requests(1))
      mpi_irecv(recv_from_process_iPlus1, requests(2))
      ...
      mpi_waitall(2, requests)
      

      您可以看到通过非阻塞通信以这种方式排序命令如何让您(在上述...期间)执行任何不依赖于在通信期间完成的发送/接收缓冲区的计算。将计算与通信重叠通常对于最大化性能至关重要。

      mpi_send_recv 另一方面(同时避免任何死锁问题)仍然是一个阻塞操作。因此,您的程序必须在整个发送/接收过程中保持在该例程中。

      最后一点:您可以初始化 2 个以上的请求并等待所有请求,使用上述结构的方式与处理 2 个请求相同。例如,与节点 i-1 开始通信并等待所有 4 个请求非常容易。使用mpi_send_recv,您必须始终有配对的发送和接收;如果你只想发送呢?

      【讨论】:

      • 好的,谢谢。我认为在第一个 MPI_ISEND 之后有 2 个进程,一个处理通信(在我上面的示例中发送到进程 1),另一个传递给 MPI_IRECV 函数。这样,我认为 MPI_IRECV 应该被调用两次(在 fork 之后和第一次调用完成时)。在我的示例中,MPI_WAIT 只是阻塞直到 request_1 被实现:我们不能说每个 MPI_ISEND 和 MPI_IRECV 都执行一个分叉(从 POSIX pthread 的角度创建 2 个进程),对吗? MPI_WAIT 的底层实现是什么(是 POSIX "pthread_join" 吗?)?
      • 将它们称为进程是不好的,因为您已经使用 MPI 进程(或等级)来分解问题。但是,如果您真的想这样想,那么是的,有一个分叉,其中 1 个“进程”处理通信,而 1 个“进程”在代码中继续进行。请注意,通信“过程”在通信完成后立即终止。因此,您的 waitall 仅适用于每个 MPI 进程——而不适用于从 fork 创建的每个“进程”。
      • mpi_wait 的底层实现取决于您使用的 MPI 实现。 MPI 标准没有规定如何实现功能,而只是规定了符合 MPI 的实现的结果。最后,pthreads 和 MPI 是两种不同的思想流派,所以我不会比较它们。这就像试图比较 OpenMP 和 MPI……它们只是创建并行代码的根本不同方式。
      • 不应该是recv_from_process_i吗?
      • @mort 否:您正在处理 i。为什么进程我会从自己接收进程?
      猜你喜欢
      • 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
      相关资源
      最近更新 更多