MPI的线程安全使用发送性病创建的线程异步::(thread safety of MPI send

2019-07-19 18:48发布

根据该网站的使用MPI::COMM_WORLD.Send(...)是线程安全的。 然而,在我的应用程序经常(并不总是)陷入死锁或获得分割故障。 围的每个呼叫MPI::COMM_WORLD方法与mutex.lock()mutex.unlock()一致地去除死锁以及段错误。

这是我如何创建线程:

const auto communicator = std::make_shared<Communicator>();
std::vector<std::future<size_t>> handles;
for ( size_t i = 0; i < n; ++i )
{
   handles.push_back(std::async(std::launch::async, foo, communicator));
}
for ( size_t i = 0; i < n; ++i )
{
   handles[i].get();
}

Communicator是具有一个类std::mutex构件和排他地调用的方法,如MPI::COMM_WORLD.Send()MPI::COMM_WORLD.Recv() 我不使用发送/ MPI与接收的任何其他方法。 foo需要const std::shared_ptr<Commmunicator> &作为参数。

我的问题:是由MPI与创建的线程不兼容答应线程安全std::async

Answer 1:

在MPI线程安全不工作的开箱。 首先,你必须确保你的实现实际上支持多线程使MPI调用一次。 随着一些MPI实现,例如开放MPI,这需要与在构建时的特殊选项来配置库。 然后,你要告诉MPI在适当的线程支持级别初始化。 目前,MPI标准定义了四个级别的线程支持:

  • MPI_THREAD_SINGLE -意味着用户代码是单线程。 这是在其中,如果MPI被初始化默认级别MPI_Init()被使用;
  • MPI_THREAD_FUNNELED -意味着用户代码是多线程的,但只有主线程使MPI调用。 主线程是初始化的MPI库中的一个;
  • MPI_THREAD_SERIALIZED -意味着用户代码是多线程的,但调用的MPI库序列化;
  • MPI_THREAD_MULTIPLE -意味着用户代码是多线程的,所有线程可以使MPI在任何时间,没有任何同步调用。

为了与线程支持初始化MPI,一个必须使用MPI_Init_thread()代替MPI_Init()

int provided;

MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
if (provided < MPI_THREAD_MULTIPLE)
{
    printf("ERROR: The MPI library does not have full thread support\n");
    MPI_Abort(MPI_COMM_WORLD, 1);
}

等效代码与废弃 (以及从MPI-3处移除)C ++绑定:

int provided = MPI::Init_thread(argc, argv, MPI::THREAD_MULTIPLE);
if (provided < MPI::THREAD_MULTIPLE)
{
    printf("ERROR: The MPI library does not have full thread support\n");
    MPI::COMM_WORLD.Abort(1);
}

线程的支持水平进行排序是这样的: MPI_THREAD_SINGLE < MPI_THREAD_FUNNELED < MPI_THREAD_SERIALIZED < MPI_THREAD_MULTIPLE ,所以任何其他规定的水平,从不同MPI_THREAD_MULTIPLE将有数值低-这就是为什么if (...)上面的代码是这么写的。

MPI_Init(&argc, &argv)相当于MPI_Init_thread(&argc, &argv, MPI_THREAD_SINGLE, &provided) 。 实现不需要以所请求的电平准确地初始化-而它们可以在任何其它电平(更高或更低),这是在返回的初始化provided输出参数。

欲了解更多信息-请参见MPI标准的§12.4,免费提供在这里 。

对于大多数MPI实现,在一级线程支持MPI_THREAD_SINGLE实际上等同于在一级提供MPI_THREAD_SERIALIZED -你在你的情况下,观察什么。

既然你没有指定过您使用的MPI实现,来了一个方便的名单。

我已经说过,开放MPI有,以便能够支持具备适当的标志来编译MPI_THREAD_MULTIPLE 。 但还有另一种追赶 - 它的InfiniBand组件不是线程安全的,因此开在满线支撑位初始化MPI不会使用本地InfiniBand通信。

英特尔MPI有两种不同的口味 - 一种带有一个不完全多线程的支持。 多线程的支持是通过将启用-mt_mpi选项将MPI编译器的包装,其能够与MT版本的链接。 如果支持OpenMP或autoparalleliser启用该选项也在暗示。 我不知道如何当启用全螺纹支持IMPI采用InfiniBand驱动程序工作。

MPICH(2)不支持InfiniBand的,因此它是线程安全的,可能是最新版本提供MPI_THREAD_MULTIPLE支持开箱即用。

MVAPICH是英特尔MPI建立的基础,它支持的InfiniBand。 我不知道用InfiniBand的一台机器上使用时,它在全螺纹支撑位的行为。

因为很多计算集群的时下使用InfiniBand结构有关多线程支持InfiniBand的音符是很重要的。 用(在IB部件openib开放MPI BTL)禁用,最MPI实现切换到另一个协议,例如TCP / IP( tcp开放MPI BTL),这导致更慢和更潜的通信。



Answer 2:

有四个级别MPI线程安全的,不是所有的人每隔实现支持:MPI_THREAD_SINGLE,MPI_THREAD_FUNNELED,MPI_THREAD_SERIALIZED和MPI_THREAD_MULTIPLE。 最后一个,它允许一个进程有多个线程可以同时调用MPI函数,可能是一个你感兴趣的。所以,首先,你需要确保你的实现支持MPI_THREAD_SERIALIZED。

线程安全的要求的水平必须要在通话中指定MPI_Init_thread 。 你叫后MPI_Init_thread你应该能够安全地调用MPI函数升压(POSIX)线程在自己创建的。



文章来源: thread safety of MPI send using threads created with std::async