通过设置KMP_AFFINITY = scatter

问题描述

对于受内存限制的程序,使用许多线程(与内核数量相同)并不总是更快,因为线程可能竞争内存通道。通常在两路套接字的计算机上,线程越少越好,但是我们需要设置相似性策略,以便将线程跨套接字分配以最大化内存带宽。

Intel OpenMP声称KMP_AFFINITY = scatter是为了实现此目的,相反的值“ compact”是将线程尽可能地靠近。我已经使用ICC来构建Stream程序进行基准测试,并且可以轻松地在Intel机器上验证此声明。而且,如果设置了OMP_PROC_BIND,则将忽略本机OpenMP环境变量,例如OMP_PLACES和OMP_PROC_BIND。您会收到这样的警告:

        OMP: Warning #181: OMP_PROC_BIND: ignored because KMP_AFFINITY has been defined

但是,我获得的最新AMD EPYC机器的基准测试显示出了非常奇怪的结果。 KMP_AFFINITY =分散提供可能的最慢内存带宽。在AMD机器上,此设置似乎正好相反:将线程放置得尽可能近,以至于每个NUMA节点上的L3缓存甚至都没有被充分利用。而且,如果我明确设置OMP_PROC_BIND = spread,则如上述警告所述,它会被Intel OpenMP忽略。

AMD机器有两个插槽,每个插槽64个物理核心。我已经使用128、64和32个线程进行了测试,我希望它们可以分布在整个系统中。使用OMP_PROC_BIND = spread,Stream的三合一速度分别为225、290和300 GB / s。但是,一旦我设置KMP_AFFINITY = scatter,即使OMP_PROC_BIND = spread仍然存在,Streams也会提供264、144和72 GB / s的速度。

请注意,对于128个内核上的128个线程,将KMP_AFFINITY = scatter设置为可提供更好的性能,这甚至进一步表明实际上所有线程都尽可能靠近放置,而根本不会分散。

总而言之,KMP_AFFINITY = scatter在AMD机器上显示完全相反的(错误的方式)行为,并且无论cpu品牌如何,它甚至会覆盖本机OpenMP环境。整个情况听起来有些混乱,因为众所周知,ICC检测到cpu品牌并使用MKL中的cpu调度程序在非Intel计算机上启动较慢的代码。那么,如果ICC检测到非Intel cpu,为什么不能简单地禁用KMP_AFFINITY并恢复OMP_PROC_BIND?

这是某人的已知问题吗?还是有人可以验证我的发现?

要提供更多背景信息,我是一家商业计算流体力学程序的开发人员,很遗憾,我们将该程序与ICC OpenMP库链接,并且KMP_AFFINITY = scatter是认设置的,因为在CFD中,我们必须解决大型稀疏线性系统,因此部分是非常受内存限制的。我发现通过设置KMP_AFFINITY = scatter,我们的程序(使用32个线程时)变得比程序在AMD机器上可以达到的实际速度慢4倍。

更新:

现在使用hwloc-ps,我可以确认KMP_AFFINITY = scatter在我的AMD threadripper 3计算机上确实在“压缩”。我附上了lstopo的结果。我运行带有16个线程的CFD程序(由ICC2017构建)。 OPM_PROC_BIND = spread可以在每个CCX中放置一个线程,以便充分利用L3缓存。 Hwloc-ps -l -t给出:

enter image description here

设置KMP_AFFINITY = scatter时,我得到了

enter image description here

我将尝试最新的ICC / Clang OpenMP运行时,并查看其工作原理。

enter image description here

解决方法

TL; DR :请勿使用KMP_AFFINITY。它不是便携式的。首选OMP_PROC_BIND(不能同时与KMP_AFFINITY一起使用)。您可以将其与OMP_PLACES混合使用,以将线程手动绑定到核心。此外,numactl应该用于控制内存通道绑定或更普遍的NUMA效果。

好答案

线程绑定OMP_PLACES可用于将每个线程绑定到特定核心(减少上下文切换和NUMA问题)。 OMP_PROC_BINDKMP_AFFINITY在理论上应该正确执行此操作,但实际上,它们在某些系统上无法执行此操作。请注意,OMP_PROC_BINDKMP_AFFINITY是排他性选择:它们不能一起使用(OMP_PROC_BIND是对旧KMP_AFFINITY环境的新的可移植替代品变量)。随着核心拓扑从一台计算机更改为另一台计算机,您可以使用hwloc工具获取OMP_PLACES所需的PU ID列表。更具体地说,hwloc-calc用于获取列表,hwloc-ls用于检查CPU拓扑。所有线程应单独绑定,以便无法移动。您可以使用hwloc-ps检查线程的绑定。

NUMA效果:AMD处理器是通过将多个通过高带宽连接(AMD Infinity Fabric)连接在一起的 CCX 组装而成的。因此,AMD处理器是 NUMA系统。如果不考虑这一点,NUMA效果可能会导致性能显着下降。 numactl工具旨在控制/减轻NUMA效果:可以使用--membind选项将进程绑定到内存通道,并且可以将内存分配策略设置为--interleave(或{{1 }}(如果该过程支持NUMA)。理想情况下,进程/线程仅应在其本地内存通道上分配并初次接触的数据上工作。如果要在给定CCX上测试配置,则可以使用--localalloc--physcpubind

我的猜测是,设置--cpunodebind时,由于不良的PU映射(可能来自操作系统错误,运行时错误或错误的用户/管理员设置)。可能是由于CCX(因为包含多个NUMA节点的主流处理器非常少见)。
在AMD处理器上,访问另一CCX内存的线程通常会付出额外的重大费用,这是由于数据通过(非常慢的) Infinity Fabric互连移动,并且可能是由于其 saturation 以及存储通道之一。我建议您不要信任OpenMP运行时的自动线程绑定(使用KMP_AFFINITY=scatter),而是手动执行线程/内存绑定,然后在需要时报告错误。

以下是生成的命令行示例,以运行您的应用程序: OMP_PROC_BIND=TRUE

PS :请注意PU /核心ID和逻辑/物理ID。