【问题标题】:Unexpected result from MPI isend and irecvMPI isend 和 irecv 的意外结果
【发布时间】:2017-04-24 11:20:06
【问题描述】:

我的目标是从进程 0 向进程 1 发送一个向量。然后,将它从进程 1 发送回进程 0。

我的实现有两个问题,

1- 为什么从进程 1 发送回进程 0 的时间比反之长? 第一个 send-recv 总共需要 ~1e-4 秒,第二个 send-recv 需要 ~1 秒。

2- 当我增加向量的大小时,出现以下错误。这个问题的原因是什么?


mpirun 注意到节点 server1 上 PID 为 11248 的进程等级 0 在信号 11 上退出(分段错误)。

我更新后的 C++ 代码如下

#include <mpi.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <boost/timer/timer.hpp>
#include <math.h>
using namespace std;
int main(int argc, char** argv) {
    // Initialize the MPI environment
    MPI_Init(NULL, NULL);
    MPI_Request request, request2,request3,request4;

    MPI_Status status;

    int world_size;
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);

    int world_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

    srand( world_rank );

    int n = 1e3;


    double *myvector = new double[n];
    if (world_rank==0){
        myvector[n-1] = 1;
    }
    MPI_Barrier (MPI_COMM_WORLD);

    if (world_rank==0){

        boost::timer::cpu_timer timer;

        MPI_Isend(myvector, n, MPI_DOUBLE , 1, 0, MPI_COMM_WORLD, &request);

        boost::timer::cpu_times elapsedTime1 = timer.elapsed();
        cout << "  Wallclock time on Process 1:"
                << elapsedTime1.wall / 1e9 << " (sec)" << endl;

        MPI_Irecv(myvector, n, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &request4);
        MPI_Wait(&request4, &status);

        printf("Test if data is recieved from node 1: %1.0f\n",myvector[n-1]);

        boost::timer::cpu_times elapsedTime2 = timer.elapsed();
        cout <<"  Wallclock time on Process 1:"
                << elapsedTime2.wall / 1e9 << " (sec)" << endl;

    }else{
        boost::timer::cpu_timer timer;

        MPI_Irecv(myvector, n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &request2);
        MPI_Wait(&request2, &status);

        boost::timer::cpu_times elapsedTime1 = timer.elapsed();
                cout << "  Wallclock time on Process 2:"
                        << elapsedTime1.wall / 1e9 << " (sec)" << endl;

        printf("Test if data is recieved from node 0: %1.0f\n",myvector[n-1]);
        myvector[n-1] = 2;
        MPI_Isend(myvector, n, MPI_DOUBLE , 0, 0, MPI_COMM_WORLD, &request3);
        boost::timer::cpu_times elapsedTime2 = timer.elapsed();
                cout<< "  Wallclock time on Process 2:"
                        << elapsedTime1.wall / 1e9 << " (sec)" << endl;

    }

    MPI_Finalize();

}

输出是: 进程 1:2.484e-05(秒)上的挂钟时间

进程 2 上的挂钟时间:0.000125325(秒)

测试是否从节点0收到数据:1

进程 2 上的挂钟时间:0.000125325(秒)

测试是否从节点1收到数据:2

进程 1:1.00133(秒)上的挂钟时间

【问题讨论】:

  • 鉴于只有一个计时器并且您不使用它,您是如何获得单独的计时信息的?
  • 我强烈怀疑您的真实代码使用auto_cpu_timer 而不是cpu_timer
  • 无论如何我们都需要看真正的代码来回答第一个问题。
  • 代码启动非阻塞操作并且从不等待它们。等级 0 中的 Isend 有效,因为在 request4 上的等待也会进行发送。但是 rank 1 中的 Isend 没有进行,因为没有等待请​​求。
  • @Zulan:我删除了计时部分以使代码更清晰。我用计时器更新了代码。

标签: asynchronous parallel-processing mpi nonblocking mpic++


【解决方案1】:

时间差异

首先,您不衡量发送消息的时间。这就是为什么发布用于计时的实际代码至关重要的原因。

您测量了四次,对于两次发送,您只需将呼叫计时到MPI_Isend。这是 API 调用的即时版本。顾名思义,它立即完成。时间与实际发送消息的时间无关。

对于接收操作,您测量MPI_Irecv 和相应的MPI_Wait。这是从开始接收到消息在本地可用之间的时间。这又与消息传输时间不同,因为它不考虑发布接收和相应发送之间的时间差。一般来说,您必须考虑 late senderlate receiver 的情况。此外,即使对于阻塞发送操作,本地完成也不意味着传输完成、远程完成甚至启动。

定时 MPI 传输很困难。

检查完成

仍然存在一个问题,为什么这段代码中的任何内容都可能需要一整秒。除非您的网络使用IPoAC,否则这肯定不是一个明智的时机。可能的原因是您没有检查所有消息的完成情况。 MPI 实现通常是单线程的,并且只能在各自的 API 调用期间进行通信。要使用即时消息,您必须定期调用 MPI_Test* 直到请求完成,或者使用 MPI_Wait* 完成请求。

我不知道您为什么首先选择使用即时 MPI 函数。如果您在启动MPI_Isend/MPI_Irecv 后立即拨打MPI_Wait,您不妨拨打MPI_Send/MPI_Recv。您需要用于并发通信和计算的即时函数,以允许并发不规则通信模式,并避免在某些情况下出现死锁。如果您不需要立即函数,请改用阻塞函数。

段错误

虽然我无法重现,但我怀疑这是由于两个同时运行的 MPI 操作使用相同的缓冲区 (myvector) 造成的。不要那样做。要么使用单独的缓冲区,要么确保第一个操作完成。通常 - 在将缓冲区传递给 MPI_Isend/MPI_Irecv 之后,您不得以任何方式触摸缓冲区,直到您知道请求已通过 MPI_Test*/MPI_Wait* 完成。

附言

如果您认为需要立即操作以避免在发送和接收时出现死锁,请考虑使用MPI_Sendrecv

【讨论】:

    猜你喜欢
    • 2017-07-14
    • 2012-03-31
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 2014-03-20
    • 2013-02-08
    • 2018-09-20
    • 2013-08-20
    相关资源
    最近更新 更多