对于内存绑定程序,使用许多线程并不总是更快,比如使用与内核相同的数目,因为线程可能会争夺内存通道。通常在双套接字机器上,较少的线程更好,但我们需要设置关联策略,将线程分布在套接字上,以最大化内存带宽。
英特尔OpenMP声称,KMP_AFFINITY=scatter就是为了达到这个目的,相反的价值“紧凑”是把线程尽可能的靠近。我已经使用ICC构建了用于基准测试的Stream程序,这一说法很容易在Intel机器上得到验证。如果设置了OMP_PROC_BIND,则忽略OMP_PLACES和OMP_PROC_BIND等原生OpenMP env。你会收到这样的警告:
OMP: Warning #181: OMP_PROC_BIND: ignored because KMP_AFFINITY has been defined
然而,在我获得的最新AMD EPYC机器上的基准显示了非常奇怪的结果。KMP_AFFINITY=scatter 提供了最慢的内存带宽(可能是)。看起来,这个设置在AMD机器上做的正好相反:尽可能靠近线程,这样甚至连每个NUMA节点上的L3缓存都没有得到充分的利用。如果我显式地设置了OMP_PROC_BIND=spread,英特尔OpenMP就会忽略上面的警告。
AMD机器有两个插座,每个插座有64个物理核心。我使用了128、64和32个线程进行了测试,我希望它们能够扩展到整个系统。使用OMP_PROC_BIND=spread,Stream给我的三合一速度分别为225、290和300 GB/s。但是,一旦我设置了KMP_AFFINITY=scatter,即使OMP_PROC_BIND=spread仍然存在,流也会提供264、144和72 GB/s。
注意,对于128个核心上的128个线程,设置KMP_AFFINITY=scatter可以提供更好的性能,这进一步表明,实际上所有线程都尽可能地放置在一起,而不是分散在一起。
总之,KMP_AFFINITY=scatter在AMD上显示完全相反的行为(以坏的方式显示),它甚至会覆盖本机OpenMP环境,而不管它的品牌是什么。整个情况听起来有点可疑,因为众所周知,ICC检测CPU品牌,并使用MKL中的CPU调度器在非Intel计算机上启动较慢的代码。那么,如果国际商会检测到一个非英特尔CPU,为什么不能简单地禁用KMP_AFFINITY并恢复OMP_PROC_BIND呢?
有人知道这件事吗?或者有人能证实我的发现?
为了提供更多的上下文,我是商业计算流体力学程序的开发人员,不幸的是,我们将我们的程序与ICC OpenMP库连接起来,KMP_AFFINITY=scatter默认设置,因为在CFD中我们必须解决大规模稀疏线性系统,而这部分是非常内存限制的。我发现通过设置KMP_AFFINITY=scatter,我们的程序比程序在AMD机上所能达到的实际速度慢4X (当使用32个线程)。
更新:
现在,使用hwloc,我可以确认,KMP_AFFINITY=scatter实际上是在我的AMD线程程序3机器上执行“紧凑”操作。我已经附上了结果。我用16个线程运行我的CFD程序(由ICC2017构建)。OPM_PROC_BIND=spread可以在每个CCX中放置一个线程,以便充分利用L3缓存。Hwloc-ps -l -t提供:
在设置KMP_AFFINITY=scatter时,我得到了
我将尝试最新的ICC/Clang OpenMP运行时,看看它是如何工作的。
发布于 2020-10-18 15:35:09
TL;博士:不要使用KMP_AFFINITY
。它不是便携式的。更喜欢OMP_PROC_BIND
(它不能与KMP_AFFINITY
同时使用)。您可以将它与OMP_PLACES
混合起来,手动将线程绑定到内核。此外,应该使用numactl
来控制内存通道绑定,或者更一般地控制NUMA效应。
长答案
线程绑定:OMP_PLACES
可用于将每个线程绑定到特定的核心(减少上下文切换和NUMA问题)。理论上,OMP_PROC_BIND
和KMP_AFFINITY
应该正确地做到这一点,但在实践中,它们在某些系统上没有做到这一点。请注意,OMP_PROC_BIND
和KMP_AFFINITY
是独占的选项:它们不应该一起使用(OMP_PROC_BIND
是旧的KMP_AFFINITY
环境变量的一个新的便携替代品)。当核心的拓扑从一台机器改变到另一台机器时,您可以使用hwloc
工具获取OMP_PLACES
所需的PU ids列表。更特别的是,hwloc-calc
获得列表,hwloc-ls
检查CPU拓扑。所有线程都应该分开绑定,这样就不可能移动。您可以使用hwloc-ps
检查线程的绑定。
NUMA效应:AMD处理器是通过将多个CCX与高带宽连接(AMD )连接在一起来构建的。因此,AMD处理器是NUMA系统。如果不考虑到NUMA效应,则会导致性能显著下降。numactl
工具旨在控制/减轻NUMA效应:可以使用--membind
选项将进程绑定到内存通道,并且可以将内存分配策略设置为--interleave
(如果进程是NUMA感知的,则设置为--localalloc
)。理想情况下,进程/线程应该只对本地内存通道分配和第一次接触的数据工作。如果要在给定的CCX上测试配置,可以使用--physcpubind
和--cpunodebind
。
我的猜测是,在设置KMP_AFFINITY=scatter
时,因错误的PU映射(可能来自操作系统错误、运行时错误或用户/管理设置错误),英特尔/Clang运行时运行时不会执行良好的线程绑定。可能是由于CCX (因为包含多个NUMA节点的主流处理器非常少见)。
在AMD处理器上,访问另一个CCX内存的线程通常要支付额外的巨大代价,因为数据通过(相当慢的)无限结构互连,并且可能由于它的饱和以及内存通道的饱和。我建议您不要信任OpenMP运行时的自动线程绑定(使用OMP_PROC_BIND=TRUE
),而是手动执行线程/内存绑定,然后在需要时报告错误。
下面是一个生成命令行的示例,以便运行应用程序:numactl --localalloc OMP_PROC_BIND=TRUE OMP_PLACES="{0},{1},{2},{3},{4},{5},{6},{7}" ./app
PS:小心PU/核心ID和逻辑/物理ID。
https://stackoverflow.com/questions/64409563
复制相似问题