共享内存,MPI和排队系统

时间:2009-12-26 18:29:00

标签: c++ shared-memory mpi boost-interprocess pbs

我的unix / windows C ++应用程序已经使用MPI进行了并行化:作业被分割为N cpus,并且每个块都是并行执行,效率非常高,速度非常好,工作正确完成。

但是在每个过程中都会重复一些数据,并且由于技术原因,这些数据不能轻易地通过MPI分割(...)。 例如:

  • 5 Gb的静态数据,为每个进程加载的内容完全相同
  • 可以在MPI中分发的4 Gb数据,使用的CPU越多,每个CPU的RAM就越小。

在一个4 CPU的工作中,这意味着至少有20Gb的RAM负载,大部分内存“浪费”,这很糟糕。

我在考虑使用共享内存来减少总体负载,每台计算机只会加载一次“静态”块。

所以,主要问题是:

  • 是否有任何标准的MPI方式在节点上共享内存?某种现成的+免费库?

    • 如果没有,我会使用boost.interprocess并使用MPI调用来分发本地共享内存标识符。
    • 共享内存将由每个节点上的“本地主服务器”读取,并以只读方式共享。不需要任何形式的信号量/同步,因为它不会改变。
  • 是否有任何性能损失或特别需要注意的问题?

    • (不会有任何“字符串”或过于奇怪的数据结构,一切都可以归结为数组和结构指针)
  • 作业将在PBS(或SGE)排队系统中执行,如果进程不干净退出,我想知道这些是否会清理特定于节点的共享内存。

8 个答案:

答案 0 :(得分:8)

高性能计算(HPC)中一种越来越常见的方法是混合MPI / OpenMP程序。即你有N个MPI进程,每个MPI进程有M个线程。此方法很好地映射到由共享内存多处理器节点组成的集群。

更改为这样的分层并行化方案显然需要一些或多或少的侵入性更改,如果正确完成OTOH,除了减少复制数据的内存消耗外,它还可以提高代码的性能和可伸缩性。

根据MPI实现,您可能会也可能无法从所有线程进行MPI调用。这由MPI_Init_Thread()函数的requiredprovided参数指定,您必须调用它而不是MPI_Init()。可能的值是

{ MPI_THREAD_SINGLE}
    Only one thread will execute. 
{ MPI_THREAD_FUNNELED}
    The process may be multi-threaded, but only the main thread will make MPI calls (all MPI calls are ``funneled'' to the main thread). 
{ MPI_THREAD_SERIALIZED}
    The process may be multi-threaded, and multiple threads may make MPI calls, but only one at a time: MPI calls are not made concurrently from two distinct threads (all MPI calls are ``serialized''). 
{ MPI_THREAD_MULTIPLE}
    Multiple threads may call MPI, with no restrictions. 

根据我的经验,像Open MPI这样的现代MPI实现支持最​​灵活的MPI_THREAD_MULTIPLE。如果您使用较旧的MPI库或某些专用架构,则可能会更糟糕。

当然,您不需要使用OpenMP进行线程化,这只是HPC中最受欢迎的选项。你可以用例如Boost线程库,英特尔TBB库,或直接pthreads或Windows线程。

答案 1 :(得分:7)

我没有使用MPI,但如果它像其他IPC库一样,我看到隐藏其他线程/进程/无论是在相同还是不同的机器上,那么它将无法保证共享内存。是的,如果该机器本身提供共享内存,它可以处理同一台机器上两个节点之间的共享内存。但是,由于提出了复杂的一致性问题,尝试在不同机器上的节点之间共享内存将是非常困难的。我希望它只是没有实现。

在所有实际情况中,如果您需要在节点之间共享内存,最好的办法是在MPI之外执行此操作。我认为您不需要使用boost.interprocess样式的共享内存,因为您没有描述不同节点对共享内存进行细粒度更改的情况;它是只读的或分区的。

John和deus的答案涵盖了如何在文件中映射,这绝对是你想要为5 Gb(giga bit ?)静态数据做的。每个CPU的数据听起来都是一样的,你只需要向每个节点发送一条消息,告诉它应该抓取哪个文件部分。操作系统应该将虚拟内存映射到物理内存到文件。

至于清理......我认为它不会对共享内存进行任何清理,但是当一个进程关闭文件(应该释放它们的内存映射)时,应该清除mmap个ed文件被清理干净了。我不知道CreateFileMapping等有什么警告。

当进程终止时,不会清除实际的“共享内存”(即boost.interprocess)。如果可能的话,我建议您尝试杀死一个进程并查看遗留的内容。

答案 2 :(得分:2)

使用MPI-2,您可以通过MPI_Put和MPI_Get等功能获得RMA(远程内存访问)。使用这些功能,如果您的MPI安装支持它们,肯定会帮助您减少程序的总内存消耗。成本增加了编码的复杂性,但这是并行编程乐趣的一部分。然后,它确实让你进入MPI领域。

答案 3 :(得分:0)

我对unix知之甚少,我不知道MPI是什么。但在Windows中,您所描述的内容与文件映射对象完全匹配。

如果此数据嵌入在您加载的.EXE或.DLL中,则它将自动在所有进程之间共享。即使由于崩溃,您的进程的拆除也不会导致任何泄漏或未发布的数据锁定。然而,一个9Gb .dll听起来有点不确定。所以这可能不适合你。

但是,您可以将数据放入文件中,然后在其上CreateFileMappingMapViewOfFile。映射可以是只读的,您可以将全部或部分文件映射到内存中。所有进程将共享映射到相同底层CreateFileMapping对象的页面。关闭unmap视图和关闭句柄是一个好习惯,但是如果你不这样做,操作系统会在拆卸时为你完成。

请注意,除非您运行x64,否则您将无法将5Gb文件映射到单个视图(甚至是2Gb文件,1Gb可能会起作用)。但鉴于你说的已经有了这个,我猜你已经只有x64了。

答案 4 :(得分:0)

如果将静态数据存储在文件中,则可以在unix上使用mmap来随机访问数据。当您需要访问特定的数据位时,将分页数据。您需要做的就是覆盖文件数据上的任何二进制结构。这是上面提到的CreateFileMapping和MapViewOfFile的unix等价物。

顺便说一下,当一个人调用malloc来请求超过一页数据时,glibc会使用mmap。

答案 5 :(得分:0)

我在SHUT中有一些MPI项目。

据我所知,有很多方法可以使用MPI分发问题,也许你可以找到另一种不需要共享内存的解决方案, 我的项目正在解决一个 7,000,000个等式和7,000,000个变量

如果你能解释你的问题,我会尽力帮助你

答案 6 :(得分:0)

几年前,当我使用MPI时,我遇到了这个小问题。

我不确定SGE是否了解内存映射文件。如果你要分发beowulf群集,我怀疑你会遇到一致性问题。你能谈谈你的多处理器架构吗?

我的草案方法是建立一个架构,其中数据的每个部分都由定义的CPU拥有。将有两个线程:一个线程是MPI双向讲话者,一个线程用于计算结果。请注意,MPI和线程并不总能很好地协同工作。

答案 7 :(得分:0)

MPI-3提供共享内存窗口(参见例如MPI_Win_allocate_shared()),它允许使用节点上的共享内存,而不需要任何其他依赖。