共享内存,MPI和排队系统

22

我的Unix/Windows C++应用程序已经使用MPI实现了并行化:作业被分割成N个CPU,每个块都以并行方式执行,效率很高,速度扩展性非常好,工作完成得很好。

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

  • 5 GB的静态数据,每个进程加载的确切内容都相同
  • 4 GB的数据可以在MPI中分布,使用的CPU越多,每个CPU的RAM就越小。

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

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

因此,主要问题是:

  • 是否存在任何标准MPI共享节点内存的方法? 有一些现成且免费的库吗?

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

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


经过目前的答案、测试和进一步阅读,内存映射文件可能是最简单的选择:
  • 只有主MPI进程需要“准备”内存文件,所有进程都将映射该文件。
  • 由于文件是只读的,无需担心内容一致性问题。
  • 不过关于性能没有明确的想法...或许只有实验才能说明。
- Blklight
性能完全取决于你的平台。虽然你提供的细节很少,但考虑到你可用的 CPU 和 RAM,你不应该遇到太大的问题。唯一一个 mmapped 文件不能满足你需求的地方是如果你需要更改共享内存(你的分布式数据),并且不需要共享内存的内容持久存在,只需要共享 RAM。在这种情况下,系统将浪费大量时间将所有内存更改写入磁盘。 - Mike DeSimone
我离开了一段时间,无法选择最终答案,所以得票最多的那个获胜了 :) 但是,无论如何,周围有很多好答案,但没有一个确切回答我所寻找的内容,所以我想这似乎没有广泛标准的解决方法! - Blklight
8个回答

10
在高性能计算(HPC)中,越来越常见的方法是混合MPI/OpenMP程序。也就是说,你有N个MPI进程,每个MPI进程有M个线程。这种方法适用于由共享内存多处理器节点组成的集群。
将其改变为这种分层并行化方案显然需要一些更或多或少的侵入性改变。如果正确地执行,它可以增加代码的性能和可扩展性,并减少重复数据的内存消耗。
根据MPI实现的不同,您可能无法从所有线程中进行MPI调用。这由必须调用MPI_Init_Thread()函数而不是MPI_Init()函数的“required”和“provided”参数指定。可能的值为:
{ MPI_THREAD_SINGLE } 只有一个线程将被执行。 { MPI_THREAD_FUNNELED } 进程可以是多线程的,但只有主线程将进行MPI调用(所有MPI调用都被“汇入”到主线程中)。 { MPI_THREAD_SERIALIZED } 进程可以是多线程的,多个线程可以进行MPI调用,但是一次只能有一个:MPI调用不能同时从两个不同的线程中进行(所有MPI调用都被“串行化”)。 { MPI_THREAD_MULTIPLE } 多个线程可以调用MPI,没有限制。
据我所知,像Open MPI这样的现代MPI实现支持最灵活的MPI_THREAD_MULTIPLE。如果您使用旧的MPI库或某些专门的体系结构,则可能效果较差。
当然,您不需要使用OpenMP进行线程处理,这只是HPC中最流行的选项。您也可以使用例如Boost线程库、Intel TBB库、直接使用pthread或Windows线程。

如果您将代码更改为在每个共享内存多处理器节点上进行多线程处理,请确保仔细编写线程调度以考虑缓存本地性和其他内存架构。 - stephan
2
我不确定混合方法是否越来越普遍。以下是一个证据,表明这可能不是一种值得采用的方法--http://www.pdc.kth.se/education/historical/2008/PRACE-P2S2/coursework/handouts.html#hybrid是的,这是一个不错的概念,但实际上与修改应用程序所需的努力相比,其价值存疑。 - High Performance Mark
这个答案没有解决问题中的任何问题。 - lurscher

8
我没有使用过MPI,但如果它像我见过的其他IPC库一样隐藏了其他线程/进程/设备是否在同一台机器上,那么它将无法保证共享内存。是的,它可以处理在同一台机器上的两个节点之间的共享内存,如果该机器本身提供了共享内存。但是,试图在不同机器的节点之间共享内存最多也会非常困难,因为涉及到复杂的一致性问题。我期望它只是未实现。
实际上,如果您需要在节点之间共享内存,最好在MPI之外进行。我认为您不需要使用类似于boost.interprocess的共享内存,因为您没有描述不同节点对共享内存进行细粒度更改的情况;它要么是只读的,要么是分区的。
John和deus的答案涵盖了如何映射文件,这绝对是您想要为5 GB(千兆字节?)静态数据执行的操作。每个CPU的数据听起来像是相同的事情,您只需要向每个节点发送消息,告诉它应该获取文件的哪个部分即可。操作系统应该负责将虚拟内存映射到物理内存和文件中。
至于清理...我会假设它不会清理共享内存,但是mmap的文件应该会被清理,因为当进程被清理时,文件关闭(这应该释放它们的内存映射)。我不知道CreateFileMapping等有什么注意事项。
实际的“共享内存”(即boost.interprocess)在进程死亡时不会被清除。如果可能的话,我建议尝试杀死一个进程并查看留下了什么。

2

使用MPI-2,您可以通过MPI_Put和MPI_Get等函数进行远程内存访问(RMA)。如果您的MPI安装支持这些功能,则使用它们将有助于减少程序的总内存消耗。但代价是编码复杂度增加,但这也是并行编程的乐趣之一。另外,这使您保持在MPI领域。


这难道不会极大地增加对共享内存访问的延迟吗?还是说MPI_Get只是直接通过内存总线获取的别名? - Crashworks
@Crashworks 是的,MPI-2 RMA并不比传统的Send/Recv更快。在许多情况下,由于需要注册内存窗口而更慢。原则上,在未来有了特殊的网络硬件支持后,它可能会变得更快,但今天几乎没有使用它的理由。 - janneb
确实如此。但使用MPI2 RMA的原因可能是在MPI范式内进行共享内存编程,而无需诉诸于更低级别的功能,例如内存映射文件或IPC库。稍微更好的执行性能的成本可能会大大降低开发性能。我想知道OP对所有这些的看法。 - High Performance Mark

2

MPI-3提供了共享内存窗口(例如MPI_Win_allocate_shared()),允许使用本地节点上的共享内存而无需任何其他依赖项。


阅读其他答案很有趣,这些答案都可以追溯到2009年,看看人们在MPI 3于2012年发布之前必须跳过哪些障碍。 - Victor Eijkhout

0

我对Unix不是很了解,也不知道MPI是什么。但在Windows中,你所描述的正好与文件映射对象相匹配。

如果这些数据嵌入在你的.EXE或.DLL中,那么它将自动在所有进程之间共享。即使由于崩溃而导致进程关闭,也不会导致任何数据泄漏或未释放的锁定。然而,一个9GB的.dll听起来有点靠不住。所以这可能对你不起作用。

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

请注意,除非你运行的是x64,否则你将无法将5GB的文件映射到单个视图中(甚至2GB的文件也不行,1GB可能可以)。但考虑到你已经谈论过这个已经工作的问题,我猜你已经是x64了。


从文档中我推断出,boost.interprocess允许以跨平台的方式(无需#ifdef)并使用“干净”的代码来完成此操作。并且有一个面向Windows的选项可以完全实现您所描述的内容。 但是这里问题的关键不是共享内存系统的技术实现,而是当您在分布在8核机器上的128个应用程序实例时如何干净地进行此操作 :-) - Blklight
1
我不确定为什么会有问题。你是说你想在多台机器之间共享吗?我很确定每台机器只能看到自己的RAM,而且一台机器上的所有核心共享该机器的RAM视图。 - John Knoeller

0
如果您将静态数据存储在文件中,您可以在Unix上使用mmap来随机访问数据。当您需要访问特定数据位时,数据将被分页加载。您所需做的就是将任何二进制结构覆盖在文件数据上。这相当于上面提到的CreateFileMapping和MapViewOfFile的Unix等效方法。
顺便说一下,当调用malloc请求超过一页的数据时,glibc会使用mmap。

glibc的malloc mmap阈值默认为128 kB,这与页面大小不同。 - janneb

0

我在SHUT做了一些MPI相关的项目。

据我所知,使用MPI可以有很多方法来分配问题的处理,也许你可以找到另一种不需要共享内存的解决方案。我的项目是解决一个700万个方程和700万个变量的问题。

如果你能够解释你的问题,我会尝试帮助你。


当然,问题中的“静态”部分可以更好地并行化,但开发时间将会非常长。大部分“完整”问题的内存可以在每个计算节点上一次性加载。因此,我的目标是共享内存,并瞄准最佳技术来实现! - Blklight
我想知道的是,你解决了一个有700万个变量的问题。 - Matthieu N.

0

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

我不确定SGE是否理解内存映射文件。如果您正在针对Beowulf集群进行分发,我怀疑您将会遇到一些一致性问题。您能否谈谈您的多处理器架构?

我的初步方法是建立一个架构,其中数据的每个部分都由一个定义好的CPU拥有。将有两个线程:一个线程是MPI双向通信器,另一个线程用于计算结果。请注意,MPI和线程并不总是兼容的。


是的,数据仅由一个CPU拥有,并且只读。这里没有一致性问题。因此,内存映射文件可能是一个简单的选择。 - Blklight
同意。但这将取决于您的架构。在共享内存架构中,memmapped文件是最好的选择。我不确定如何在Beowulf集群中实现它。 - Paul Nathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接