混合计算:共享内存与分布式内存相结合的优势

2014年 3月 6日

在之前的混合计算系列博客中,我的同事 Pär 介绍了如何在共享内存和分布式内存平台上使用 COMSOL Multiphysics 进行并行数值模拟。今天,我将讨论这两种方法的结合:混合计算我将尝试阐明混合计算和建模的各个方面,并介绍在 COMSOL Multiphysics 中如何使用混合配置以在并行平台上获得最佳性能。

混合计算介绍

近年来,通过整合最新的多核技术,集群系统变得越来越强大。并行性已经扩展出了多个层级。大型系统必须处理跨节点、套接口、内核甚至矢量单元的并行性(这些操作是在短向量或一维数据数组上执行的,而不是在标量值或单个数据项上执行)。

此外,内存系统也被组织成几个层次。随着这些层次结构变得越来越深入、越来越复杂,编译和执行模型需要反映这些嵌套配置。事实证明,仅仅处理 单个 编译和执行模型是不够的。因此,计算变得越来越混合

核心定律和集群

如今,多核计算无处不在,人人都会遇到并行计算问题。由于时钟频率已停滞在 2-5 GHz 左右,因此只能通过添加更多的内核来满足对计算能力日益增长的需求。著名的摩尔定律 已经变成了一个必然的核心定律 ——它表明每个芯片面积的核数将继续呈指数增长。

这种发展趋势的直接结果是,每个内核的资源(例如高速缓存和内存通道的数量)将由于逻辑运算而变得更小。典型 CPU 类型的最新多核版本具有多达 16 个内核,但最多只有 4 个内存通道。

通常,几个多核 CPU 提供高性能的共享内存节点,以 GigaFLOP/s(即每秒 10 亿次浮点运算)的速度带来强大的计算能力。然后,这些共享内存节点通过高速互连将其中几个共享内存节点集成到集群中——在 (Giga/Tera/Peta)FLOP/s 和内存容量方面提供几乎无限的资源。唯一的限制是 IT 部门的预算和占地面积。

如今,一个集群系统需要拥有超过 100,000 个内核才能在最新的TOP500 榜单中获得高排名。

达到极限

集群代表一个分布式内存系统,其中通过信息传递的方式在节点之间发送信息。在这里,实际上带有多个开源和商业实现的消息传递接口(MPI) 是标准。通常,在节点内部,OpenMP用于共享内存编程。

然而,数值实验很容易揭示多核平台的局限性:“喂养这些野兽”变得越来越困难。简单地说,很难以足够快的速度将尽可能多的数据发送到内核,让它们处理数据。基本上,你可以说 FLOP/s 是免费提供的,但必须注意计算强度,即每个数据元素的 FLOP 数量——这是算法的一个特征。计算强度为可实现的 FLOP/s 速率提供了上限。

如果计算强度随问题大小线性增加,带宽就不会限制性能。但是有限元数值模拟的典型运算是稀疏矩阵-向量型运算,这种运算具有带宽限制,且带宽通常与多核系统的存储信道数成正比。一旦可用内存带宽饱和,开启更多的核就没有意义了。这就是为什么在多核 CPU 和共享内存平台上的加速经常达到饱和的原因,因为内存流量已经达到了最大水平,即使不是所有的核都参与计算。另一方面,集群系统具有增加累积带宽的额外好处,因此具有更好的性能。(关于 STREAM 基准的带宽限制的更多细节请点击这里。)

在硬件方面,已经尝试通过引入缓存层次结构来减轻带宽限制。这些缓存的范围从很小的一级缓存(仅限于单个内核,内存大小只有几百KB)到大的三级缓存(在几个内核之间共享,内存大小可达几十MB)。高速缓存的目的是让数据尽可能地靠近内核,这样要重用的数据就不需要从主内存中反复传输。这将消除内存通道中的一些压力。

即使是单个多核处理器本身也可能导致嵌套的内存层次结构。但是,将多个多核处理器封装到多个插槽中会构建一个具有非均匀内存访问(NUMA)的共享内存节点。换句话说,应用程序数据的某些部分存储在内核本地的内存中,而某些数据存储在远程内存位置。因此,可以非常快速地访问数据的某些部分,而其他访问具有更长的延迟。这意味着数据的正确放置和计算任务的合理分配对于性能至关重要。

了解基于性能的层次结构

我们已经了解到,共享内存系统建立了一个由内核和内存组成的分层系统,并且编译模型、算法和实现需要充分了解这些层次。在分布式计算中,由于共享内存节点的计算资源有限,可以通过快速互连网络将多个共享内存节点连接起来,以增加运算能力。

为了与我们之前在共享内存和分布式内存计算的博客文章类比,我们现在使用可变数量的会议位置来表示集群,其中每个位置提供一个会议室,用一个会议桌来表示一个共享内存节点。

如果要做的工作越来越多,会议管理员可以打电话给公司的其他地点寻求帮助。例如,假设会议室位于波士顿、旧金山、伦敦、巴黎和慕尼黑。这些远程位置表示分布式内存进程(MPI 进程)。管理员现在可以按需添加新的地点,如添加斯德哥尔摩——或者根据混合计算,她可以为每个共享内存节点(即每个会议室位置)设置额外的进程(即会议桌)。

每个会议室位置(进程)在会议室的桌子上都有一个电话,员工可以用它呼叫任何其他位置(另一个进程)并询问数据或信息(消息传递)。当地员工(资源有限)坐在特定地点的每张会议桌旁。会议桌旁的每个员工都代表一个线程,帮助解决会议室桌上的任务。

在桌子上,本地数据可以在报表(一级缓存)、文件夹(二级缓存)、位于文件柜内的文件夹(三级缓存)、同层库(主内存)中找到,或归档在地下室(硬盘)的档案中。几个助手(存储通道)在大楼里跑来跑去,以便从档案库或档案馆获取包含所需信息的新文件夹。助手数量是有限的,只能同时携带有限数量的文件夹(带宽)。

如果没有更多的助手可以为他们提供足够的数据来处理,那么让更多的人坐在桌子上不会增加任何价值。很明显,会议管理员需要确保工作所需的数据在桌面上是可用的,并且房间里的所有员工都可以有效地做出贡献,为给定问题找到解决方案。她还应确保将通过桌上电话打到其他会议地点的次数保持在最低限度。在数字方面,实现应该是层次感知的,数据应该保持在本地,通信量应该保持在最低限度。

各会议地点之间的电话呼叫代表进程之间的 MPI 调用。在会议室桌子上,应该采用共享内存机制。总的来说,需要分布式内存 (MPI) 和共享内存 (OpenMP) 的完美交互。

混合集群配置示例

让我们进一步研究一些可能的集群和核心配置。在下面的测试基准模型中,我们研究了一个由三个共享内存节点组成的小型集群。每个节点有两个插槽,每个插槽中有一个四核处理器。总核数是24。每个处理器都有一个本地内存组,也说明了主内存的NUMA配置。

现在,我们测试在这个集群上配置了 3、6、12 或 24 个 MPI 进程的情况。通过三个MPI进程,每个节点有一个 MPI 进程,每个 MPI 进程有八个线程,它们可以通过节点内部的共享内存 /OpenMP 和跨节点的两个套接字进行通信。如果有 6 个 MPI 进程,那么每个套接口就有一个 MPI 进程,即每个处理器有一个 MPI 进程。然后,每个 MPI 进程需要四个线程。12 个 MPI 进程的第三种可能性是为每个处理器设置两个 MPI 进程,每个进程有两个线程。最后,我们可以在系统上测试每个核一个 MPI 进程,总共 24 个 MPI 进程。这是一种非混合情况,在这种情况下,不需要共享内存并行性,所有通信都通过分布式内存进行。

你觉得哪个配置最好?

Illustration depicting different MPI configurations on a cluster with three shared memory nodes
具有三个共享内存节点的集群上的不同 MPI 配置,该节点由两个插槽组成,每个插槽具有一个四核处理器和本地内存组。

为什么混合?

为什么不使用单一的编程和执行模型而忽略分层内核和内存配置呢?首先,这是因为共享内存 (OpenMP) 机制不能在具有标准安装的标准类型系统上全局使用(就像没有可在全球范围共享数据的桌子)。

那么,为什么不像上面的例子那样,使用 24 个 MPI 进程,在所有内核间使用消息传递呢?这当然是可能的;甚至可以在同一共享内存节点上的内核之间使用消息传递。但这意味着,在我们上面的类比中,每个员工都须有自己的手机,必须给世界各地的所有其他员工打电话。这可能会出现信号被占用的问题,或者人们暂停工作,结果就无法工作。

实际上,真实的场景更加复杂,因为现代 MPI 实现很好地了解层次结构,并使用共享内存机制进行本地通信。MPI 的一个缺点是,内存资源在参与进程的数量上被二次浪费。这样做的原因是在进程之间发送实际消息之前,已在数据存储(可能是复制)的位置建立了内部缓存区。在 106 个核上,单个全局通信调用将需要 1012个缓存区(如果 MPI 实现为非层次感知)。在混合计算中,MPI 进程的数量通常低于内核计数,从而节省了内存和数据传输方面的资源。

使用混合模型的另一大优势是,许多机制(例如数据放置、线程固定或负载平衡)需要程序员的一些专门操作。混合模型提供了一种更通用的工具来表达这些细节。它具有全局和离散内存视图的优点。考虑混合 OpenMP + MPI 模型是一个很自然的选择,因为它适合内存和内核配置的混合结构,如图所示。

最重要的是,混合模型灵活且适应性强,有助于减少成本和资源需求。在有限元建模方面,数据和任务中的层次结构通常可以从物理模型、其几何结构、算法和所使用的求解器中推导出来。然后可以将这些层次结构转换为共享和分布式内存机制。

当然,混合模型也结合了共享内存和分布式计算的缺点,最终变得更加复杂。然而,最终的结果是值得的!COMSOL Multiphysics 提供了复杂的数据结构和算法,可以在很大程度上表示和利用多级并行性。它同时支持共享内存和分布式内存,用户可以通过一组参数来优化两者的交互,以获得最佳性能。

对模型和系统进行基准测试

在了解所有这些理论解释之后,现在是将概念与真实模型联系起来的时候了。我想您一定很想了解在您部门的计算服务器和集群上并行运行 COMSOL Multiphysics 时的可扩展性、加速比和产出提升。

为了得到正确的结果,关注问题的规模是非常重要的。共享内存节点上的子问题的规模必须足够大,以便每个线程获得合理的工作量,并且每个进程的计算量与进程之间通过消息交换的数据量之比必须足够大。正如 Pär 在他之前的博文中提到的,问题是否可以并行化是至关重要的。例如,如果设置模型时的主要工作是计算一个很长的时间步长序列(可能持续几个小时),但每个时间步长的问题规模并不是很大,那么在增加额外的节点和核心时,将不会带来显著的优势。

即使您仅有共享内存机,也建议您尝试混合模型的各种配置。

混合可扩展性研究

这里介绍的测试场景中,我们考虑了一个代表十辐轮辋的结构力学模型,其中模拟了轮胎压力和载荷分布。

A model and submodel of a wheel rim
轮辋模型及其相应的子模型。

我们的仿真是在上面提到的三个计算节点上运行的,其中每个节点有两个带有四核 Intel Xeon®E5-2609 处理器的插槽,每个节点有 64 GB RAM,其中 32GB 与一个处理器相关联。这些节点通过(相当慢的)千兆以太网连接互连。在这台特定的机器上,我们总共有 24 个内核可用。

在下面的图表中,我们根据混合模型配置,比较了该模型每天可以运行的模拟次数。我们考虑运行 1、2、3、6、12 和 24 个 MPI 进程的情况。这将导致 1、2、3、4、6、8、12、16 和 24 个活动内核运行,具体取决于配置。图中的每条条形图都代表一个(nn xnp)配置,其中 nn 是进程数,np 是每个进程的线程数,nn*np 是活动核数。条形图被分组成具有相同活动内核数的区域,它们的配置列在条形图的顶部。

从图中可以看出,通常情况下,随着活动内核的数量增加,计算性能会增加。我们可以看到不同配置的细微变化。当使用 24 个活动内核达到完整的系统负载时,我们发现最佳配置是为每个插槽分配一个MPI进程(即总共 6 个 MPI 进程)。使用混合进程-线程配置(6×4 的案例)的三节点系统上获得的性能和生产率是单个共享内存节点(1×8 的案例)上的两倍多。它也比完全分布式模型(24×1 的案例)高出近 30%,后者使用相同数量的内核。

当将其与邻近的全分布式配置(12×1 的案例)进行比较时,我们看到,尽管内核数量增加了一倍,但在性能上并没有真正的提高。这是因为缓慢的千兆以太网网络已经非常接近其12 个MPI进程的极限。因此,更多的MPI过程是无益的。在比较 12×1 和 12×2 配置时,情况有所不同,其中每个进程的线程数是活动内核数的两倍。这基本上意味着,在这种情况下,通过以太网的通信量没有增加。

A graph used to benchmark a structural mechanics model of a wheel rim using different configurations in a hybrid model
使用混合模型中的不同配置对轮辋的结构力学模型进行基准测试。y 轴表示通过一天中可以运行的该模型的模拟总数来提高性能和生产力。条形图表示不同配置的 nn x np,其中 nn 是 MPI 进程数,np 是每个进程的线程数。

在 COMSOL Multiphysics 中建立混合运行

在并行混合模式下运行 COMSOL Multiphysics 时,您可以通过多种方式调整所使用的进程数和线程数。在“首选项”对话框中的“多核和集群计算”部分或模型生成器中“研究”节点的“集群计算”子节点中可以找到一些设置。你可以微调 作业配置 节点的 集群计算 子节点中的设置,可以在其中指定要在集群计算中使用的(物理)节点数。使用下拉菜单设置 窗口,你还有其他选择,例如每台主机的进程数或节点粒度,这决定了是否为每个节点、套接口或核心放置一个进程。

每个进程使用的线程数是自动设置的。COMSOL Multiphysics 始终使用最大可用内核数,即每个进程的内核数设置为节点上的可用内核数除以节点上的进程数。你可以通过将首选项对话框中的多核部分中的 进程数量 设置为请求的值来覆盖每个进程的核数。

在 Linux 系统上,命令行选项 -nn 用于表示进程数,-np 用于覆盖自动确定的每个进程的线程数,以及选项 -nnhost 用于设置每个主机的进程数。nnhost 的自然选择是 1 或每个节点的套接字数。

下一步


评论 (0)

正在加载...
浏览 COMSOL 博客