【问题标题】:MPI_Test isn't picking up a completed MPI_SendMPI_Test 没有收到已完成的 MPI_Send
【发布时间】:2015-01-08 08:24:27
【问题描述】:

我已经花了三天时间寻找答案,所以如果这个问题已经得到解决,我希望你能容忍我,而我很不幸地找到了解决方案。 我正在使用 Fortran(哎呀!),但这是一个通用的 MPI 查询。

场景(本示例已简化):

  • 进程 0 和 1 与进程 2 通信(但彼此不通信)
  • 0 和 1 进行大量发送/接收
  • 2 执行大量接收/处理/发送(但每对都执行两次,以便 拿起 0 和 1)
  • 0 & 1 最终会停止 - 我不知道什么时候! - 所以我在适当的时候使用第三个进程的等级(filter_rank_id = 2)和一个特殊标签(c_tag_open_rcv = 200)从每个执行一个MPI_Send,缓冲区中的逻辑为TRUE(end_of_run)。像这样:

CALL MPI_SEND(end_of_run, 1, MPI_LOGICAL, filter_rank_id, c_tag_open_rcv, mpi_coupling_comms, mpi_err)

问题出现在流程 2 中...它正忙于执行其 MPI_Recv/MPI_Send 对,我无法摆脱它。我已经为其他两个进程中的每一个都设置了一个非阻塞接收并存储了请求句柄:

    DO model_rank_id= 0, 1
        !Set up a non-blocking receive to get notification of end of model run for each model
        end_run = end_model_runs(model_rank_id) !this is an array of booleans initialised to FALSE
        CALL MPI_IRECV(end_run, 1, MPI_LOGICAL, model_rank_id, &
                       c_tag_open_rcv, coupling_comms, mpi_request_handle, mpi_err)
        !store the handle in an array
        request_handles(model_rank_id) = mpi_request_handle                
    END DO

其中 model_rank_id 是 MPI 通信器中的进程号,即 0 或 1。

后来,忙于处理所有这些接收/发送对,我总是检查缓冲区是否有任何东西到达:

    DO model_rank_id= 0, 1
        IF (end_model_runs(model_rank_id) .EQV. .FALSE.) THEN
            CALL MPI_TEST(request_handles(model_rank_id), run_complete, mpi_status, mpi_err)
            IF (run_complete .eqv. .FALSE.) THEN
                !do stuff... receive/process/send
            ELSE
                !run is complete
                !___________removed this as I realised it was incorrect__________
                !get the stop flag for the specific process
                CALL MPI_RECV(end_run, 1, MPI_LOGICAL, model_rank_id, &
                                    c_tag_open_rcv, coupling_comms, mpi_err)
                !____________end_________________________________________________
                !store the stop flag so I can do a logical 'AND' on it and break out when
                !both processes have sent their message
                end_model_runs(model_rank_id) = end_run
            END IF
        END IF
    END DO

请注意,这个 sn-p 包含在一个循环中,该循环一直持续到所有停止标志为 TRUE。

我知道这相当复杂,但这不会那么难吧?如果有人能看到错误,那就太好了,甚至可以提出更好的方法。

提前非常感谢。

【问题讨论】:

    标签: interrupt openmpi


    【解决方案1】:

    经过大量的实验,我最终解决了这个问题,实际上很简单(不是总是这样吗?)

    问题是由于进程 0 和 1 可以结束并发布他们的“我完成了”消息 OK,但进程 2 处于如此紧密的循环中,执行测试和接收/发送对(外部循环为清楚起见在原始过去省略了两组发送/接收),测试将失败并且进程将停留在阻塞MPI_RECV

    首先我尝试了一个 sleep(3) 使它工作,但它不能在每个循环上都睡眠而不会对性能产生非常糟糕的影响,然后我尝试了一个 MPI_IPROBE 但遇到了与测试相同的问题。最后,MPI_IPROBE 周围的超时起到了作用,因此:

            DO iter1 = 1, num_models
    
                !Test each model in turn and ensure we do the comms until it has finished
                IF (end_model_runs(iter1) .EQV. .FALSE.) THEN                        
                    model_rank_id= models(iter1)
                    now = TIME()
                    DO WHILE (TIME() .LT. now + timeout)
                        msg_flag = .FALSE.
                        CALL MPI_IPROBE(model_rank_id, c_tag, coupling_comms, &
                                    msg_flag, empi_status, empi_err)
                        IF (msg_flag .EQV. .TRUE.) THEN
                            !Message waiting
                            EXIT
                        END IF
                    END DO
    
                    IF (msg_flag .EQV. .TRUE.) THEN
                        CALL MPI_RECV(state_vector, num_state_params, MPI_DOUBLE_PRECISION, &
                                      model_rank_id, c_tag, coupling_comms, empi_status, empi_err)
                    ELSE !no message waiting, flag should be False, i.e. the run *has* finished
                        end_model_runs(iter1) = .NOT. msg_flag
                    END IF
                END IF
            END DO
    

    这个代码在一个循环中,一旦end_model_runs 的所有成员都是TRUE,就会中断。

    我希望这对其他人有所帮助 - 并为他们节省三天的精力!

    【讨论】:

      【解决方案2】:

      您的程序可能卡在MPI_RECV 调用中。原因是MPI_TEST 返回的肯定完成标志意味着MPI_IRECV收到消息。除非发件人发送具有相同标签的另一条消息,否则MPI_RECV 将简单地阻塞和等待,在您的情况下可能是无限期的。除此之外,您使用相同的接收缓冲区发出两个MPI_IRECV 调用,这可能不是您真正想要做的,因为end_run = end_model_runs(model_rank_id) 不会将数组元素的地址复制到end_run,而是它的值。

      您的代码应如下所示:

      DO model_rank_id= 0, 1
          !Set up a non-blocking receive to get notification of end of model run for each model
          CALL MPI_IRECV(end_model_runs(model_rank_id), 1, MPI_LOGICAL, model_rank_id, &
                         c_tag_open_rcv, coupling_comms, request_handle, ierr)
          !store the handle in an array
          request_handles(model_rank_id) = request_handle                
      END DO
      
      ...
      
      DO model_rank_id= 0, 1
          IF (end_model_runs(model_rank_id) .EQV. .FALSE.) THEN
              CALL MPI_TEST(request_handles(model_rank_id), run_complete, status, ierr)
              IF (run_complete .eqv. .FALSE.) THEN
                  !do stuff... receive/process/send
              ELSE
                  !run is complete
                  !the stop flag is ALREADY in end_model_runs(model_rank_id)
                  !do a logical 'AND' on it and break out when
              END IF
          END IF
      END DO
      

      附带说明,使用您自己的以mpi_ 开头的标识符是一个糟糕的主意,因为这些标识符可能与 MPI 库提供的符号冲突。您真的应该将mpi_ 视为保留前缀,并且在命名您自己的变量、子例程等时不要使用它。我已经在上面的代码中为您解决了这个问题。

      【讨论】:

      • 我已经删除了多余的MPI_RECV 调用(见编辑),因为我意识到它与另一个发送不匹配。但是,代码一开始就没有涉及到!我的打印输出显示 MPI_TEST 总是返回 False,即使我可以看到其他进程已发送到正确的目的地。重复使用 mpi_ 是 Fortran 的东西...不区分大小写?
      • 请注意,此类由单个逻辑值组成的短消息很可能在发送方缓冲或急切发送。测试失败的原因可能在未显示的代码中。至于前缀,不,它不是 Fortran 的东西。在 C/C++ 中使用 MPI_ 也是一种不好的做法。
      • 我最终找到了它 - 这是一个时间问题 - 如果我将 sleep 插入,测试会接收完成的发送。无论如何感谢您的帮助 :) 重新使用 mpi_:我正在更改大小写以将其与 MPI_ 区分开来,在 C/C++ 中这些将被区别对待。
      • MPI_TEST 旨在在循环中连续调用 - 在许多情况下,单个调用不会做太多事情。如果您只是想等到任何进程有消息,请发出MPI_RECV,源等级为MPI_ANY_SOURCE。如果您想定期检查消息可用性,请使用MPI_IPROBE,再次使用MPI_ANY_SOURCE。后者需要一个连续的MPI_RECV 才能接收消息。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-02
      • 1970-01-01
      • 1970-01-01
      • 2014-06-21
      • 2013-07-24
      • 2012-01-08
      • 1970-01-01
      相关资源
      最近更新 更多