【问题标题】:shared memory, MPI and queuing systems共享内存、MPI 和排队系统
【发布时间】:2010-12-30 03:45:47
【问题描述】:

我的 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)排队系统中执行,在进程不干净退出的情况下,我想知道这些是否会清理节点特定的共享内存。

【问题讨论】:

  • 在到目前为止的答案、测试和进一步阅读之后,内存映射文件可能是最简单的选择: - 只有主 MPI 进程需要“准备”内存文件,该文件将被所有过程。 - 由于文件将是只读的,因此无需担心内容一致性。 - 不知道性能如何......也许只有实验会告诉我们。
  • 性能完全取决于您的平台。您的详细信息很少,但考虑到您可用的 CPU 和 RAM,您应该不会有大问题。映射文件失败的唯一地方是如果您需要更改共享内存(您的分布式数据),不需要共享内存的内容是持久的,只需要共享 RAM。在这种情况下,您的系统将浪费大量时间将所有内存更改写入磁盘。
  • 离开了,无法选择最终答案,得票最多的人得到了它:) 但无论如何,周围有很多好的答案,但没有什么能准确回答我想要的,所以我想没有广泛标准的方法可以做到这一点!

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


【解决方案1】:

我对unix了解不多,也不知道MPI是什么。但在 Windows 中,您所描述的是文件映射对象的完全匹配。

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

但是,您可以将数据放入文件中,然后将CreateFileMappingMapViewOfFile 放在上面。映射可以是只读的,您可以将文件的全部或部分映射到内存中。所有进程将共享映射相同底层 CreateFileMapping 对象的页面。关闭取消映射视图并关闭句柄是一种很好的做法,但如果您不这样做,操作系统会在拆卸时为您执行此操作。

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

【讨论】:

  • 从文档中,我推断 boost.interprocess 允许以跨平台方式(无需#ifdef)和“干净”代码执行此操作。并且有一个特定于 Windows 的选项可以完全按照您的描述进行。但是这里的问题的核心不是共享内存系统的技术实现,而是当你有 128 个应用程序实例分布在 8 核机器上时如何干净地做到这一点:-)
  • 我不知道为什么会有问题。您是说要在多台机器之间共享。我很确定每台机器只会看到它自己的 RAM,并且一台机器上的所有内核共享该机器 RAM 的视图。
【解决方案2】:

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

顺便说一句,当调用 malloc 请求多页数据时,glibc 会使用 mmap。

【讨论】:

  • glibc malloc mmap 阈值默认为 128 kB,与页面大小不同。
【解决方案3】:

我没有使用过 MPI,但如果它像我见过的其他 IPC 库一样隐藏其他线程/进程/无论是在同一台还是不同的机器上,那么它将无法保证共享内存.是的,它可以处理同一台机器上两个节点之间的共享内存,如果那台机器自己提供共享内存的话。但是由于引发了复杂的一致性问题,试图在不同机器上的节点之间共享内存充其量是非常困难的。我希望它只是未实现。

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

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

至于清理...我认为它不会对共享内存进行任何清理,但是应该清理mmaped 文件,因为在清理进程时文件已关闭(这应该释放它们的内存映射)向上。我不知道CreateFileMapping 等有什么警告。

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

【讨论】:

    【解决方案4】:

    我在 SHUT 有一些使用 MPI 的项目。

    据我所知,有很多方法可以使用 MPI 分配问题,也许您可​​以找到另一个不需要共享内存的解决方案, 我的项目是求解一个 7,000,000 方程和 7,000,000 个变量

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

    【讨论】:

    • 当然,问题的“静态”部分可以更好地并行化,但开发时间会很长。 “满”问题的大部分内存都可能在每个计算节点上加载一次。所以,我的目标是共享内存,并以最好的技术为目标!
    • 我想知道的是你解决了什么样的问题,有 7*10^6 个变量。
    【解决方案5】:

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

    【讨论】:

    • 这不会极大地增加访问共享内存的延迟吗?还是 MPI_Get 只是跨内存总线直接获取的别名?
    • @Crashworks 是的,MPI-2 RMA 并不比传统的发送/接收快。在许多情况下速度较慢,因为需要注册内存窗口。原则上,未来有特殊的网络硬件支持可能会变得更快,但今天几乎没有理由使用它。
    • 确实如此。但也许使用 MPI2 RMA 的一个原因是在 MPI 范式中进行共享内存编程,而不必求助于内存映射文件或 IPC 库等较低级别的功能。稍微好一点的执行性能的代价很可能是低得多的开发性能。我想知道 OP 对这一切做了什么。
    【解决方案6】:

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

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

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

    { 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 线程库、英特尔 TBB 库或直接 pthread 或 windows 线程。

    【讨论】:

    • 如果您将代码更改为在每个共享内存多处理器节点上进行多线程处理,请务必仔细编写线程调度以考虑缓存局部性和其他内存架构。
    • 我不确定混合方法是否越来越普遍。这是一个证据示例,它可能不值得采用 - pdc.kth.se/education/historical/2008/PRACE-P2S2/coursework/… 是的,这是一个不错的概念,但与修改应用程序所需的努力相比,它在实践中的价值值得怀疑。
    • 这个答案没有解决问题中的任何问题
    【解决方案7】:

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

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

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

    【讨论】:

    • 是的,数据只属于一个 CPU,并且是只读的。这里没有一致性问题。因此,内存映射文件可能是一个简单的选择。
    • 同意。但这将取决于您的架构。 memmapped 文件最适合共享内存架构。我不确定如何使用 beowulf 集群。
    【解决方案8】:

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

    【讨论】:

    • 阅读其他答案很有趣,所有答案都可以追溯到 2009 年,看看人们在 2012 年 MPI 3 之前必须跳过哪些障碍。
    猜你喜欢
    • 1970-01-01
    • 2020-06-29
    • 2012-07-05
    • 2020-05-10
    • 1970-01-01
    • 2019-10-22
    • 2016-04-05
    • 2016-10-09
    • 1970-01-01
    相关资源
    最近更新 更多