【问题标题】:Safety guarantee for interleaved MPI Isend / Recv交错 MPI Isend / Recv 的安全保证
【发布时间】:2014-04-06 03:31:07
【问题描述】:

related question 中,我了解到执行request = Isend(...); Recv(...); request.Wait();保证工作,因为Isendrequest.Wait() 之前可能不会做任何事情,因此在Recv(...) 处死锁(见原文详情提问)。

但是如果Isend() / Wait()Recv 之外的另一个线程上执行呢?我现在对标准的安全保证不直接感兴趣。这是因为标准只断言线程安全,如果适当的Init_thread 方法被调用并返回正确的级别。对于我的 openMPI 配置,情况并非如此。但是,我看不出一个实现实际上将调用限制为仅来自调用 Init_thread 的线程的原因(需要对线程 ID 进行实际比较)。我的理由是:如果我序列化所有发送和所有接收,mpi 永远不会注意到我正在使用多个线程。

所以我的简化代码是这样的:

#include <cassert>
#include <thread>
#include "mpi.h"

void send(int rank, int& item)
{
   MPI::Request request = MPI::COMM_WORLD.Isend(&item, sizeof(int), MPI::BYTE, rank, 0);
   request.Wait();
}

void recv(int rank, int& item)
{
   MPI::COMM_WORLD.Recv(&item, sizeof(int), MPI::BYTE, rank, 0);
}

int main()
{
   MPI::Init();
   int ns[] = {-1, -1};
   int rank = MPI::COMM_WORLD.Get_rank();
   ns[rank] = rank;
   auto t_0 = std::thread(send, 1 - rank, std::ref(ns[rank])); // send rank to partner (i.e. 1 - rank)
   auto t_1 = std::thread(recv, 1 - rank, std::ref(ns[1 - rank])); // receive partner rank from partner
   t_0.join();
   t_1.join();
   assert( ns[0] == 0 );
   assert( ns[1] == 1 );
   MPI::Finalize();
}

代码解释:每个处理器上执行两个线程。一个尝试向合作伙伴Isend一些数据并等待直到完成,另一个从合作伙伴接收一些数据。

问题:我可以安全地假设大多数 MPI 实现不会因这段代码而窒息吗?

(免责声明:这段代码并非设计为异常安全或特别漂亮。仅用于演示目的)

【问题讨论】:

  • 您在相关问题中学到的内容是错误的。
  • @HristoIliev 感谢您在那里发布答案:) 这是个好消息(嗯,部分。这意味着其他地方有一个错误-.-),因为我不必抛出完全离开我的概念

标签: c++ thread-safety mpi communication openmpi


【解决方案1】:

问题:我可以安全地假设大多数 MPI 实现不会因这段代码而窒息吗?

在实践中 - 是的,如果您添加同步(您的代码缺少同步);理论上 - 没有。虽然某些实现可能允许在 MPI_THREAD_SINGLE 级别从不同线程进行序列化调用(Open MPI 就是这样一个 - 请参阅 here),但 MPI 标准要求库必须在 MPI_THREAD_SERIALIZED 级别初始化.如果您希望您的软件具有可移植性并且能够与其他 MPI 实现一起正确编译和运行,则不应依赖某些特定的 Open MPI 行为。

也就是说,Open MPI 可以配置为在构建库时支持多线程 (MPI_THREAD_MULTIPLE)。默认情况下,出于性能原因,MT 支持未启用。您可以使用 ompi_info 检查您的特定安装的状态:

$ ompi_info | grep MPI_THREAD_MULTIPLE
     Thread support: poxis (MPI_THREAD_MULTIPLE: no, progress: no)
                            ^^^^^^^^^^^^^^^^^^^^^^^

该特定构建不支持多线程,并且将始终在providedprovided 输出参数中返回MPI_THREAD_SINGLE

【讨论】:

  • 请问需要同步哪些部分的代码?用相同的互斥体包装IsendRecv 就足够了吗?
  • 所有 MPI 调用都必须与临界区同步,无论它是如何实现的。
  • 谢谢,我想我现在看到了解决问题的方法 :)
  • 请注意,您的问题存在单行解决方案:MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, ns, 1, MPI_INT, MPI_COMM_WORLD);(无论 C++ 等价物是什么)。
  • 这是一个相当简化的示例代码。在我的实际应用程序中,我需要在主节点上收集结果(相当随机地进入)并将作业分配给节点。请问你看看下面的代码,告诉我它是否安全? pastie.org/8861734
猜你喜欢
  • 2016-12-27
  • 1970-01-01
  • 2017-07-14
  • 2012-03-31
  • 2017-04-24
  • 2018-09-20
  • 1970-01-01
  • 2013-10-05
  • 2013-08-20
相关资源
最近更新 更多