openmp - 英特尔 OpenMP 库通过设置 KMP_AFFINITY=scatter 显着降低 AMD 平台上的内存带宽

标签 openmp amd hpc icc affinity

对于内存受限的程序,使用多个线程并不总是更快,比如与内核数量相同的线程,因为线程可能会竞争内存 channel 。通常在双插槽机器上,线程越少越好,但我们需要设置关联策略,将线程分布在插槽之间以最大化内存带宽。
Intel OpenMP 声称 KMP_AFFINITY=scatter 就是为了达到这个目的,相反的值“compact”是将线程尽可能的靠近。我已经使用 ICC 构建了 Stream 程序以进行基准测试,并且这种说法很容易在 Intel 机器上得到验证。如果设置了 OMP_PROC_BIND,则会忽略 OMP_PLACES 和 OMP_PROC_BIND 等原生 OpenMP 环境变量。你会得到这样的警告:

        OMP: Warning #181: OMP_PROC_BIND: ignored because KMP_AFFINITY has been defined
然而,我获得的最新 AMD EPYC 机器上的基准测试显示出非常奇怪的结果。 KMP_AFFINITY=分散提供最慢的内存带宽 .似乎这个设置在 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 仍然存在,Streams 也会提供 264、144 和 72 GB/s。
请注意,对于 128 个内核上的 128 个线程,设置 KMP_AFFINITY=scatter 可以提供更好的性能,这甚至进一步表明实际上所有线程都尽可能靠近,但根本没有分散。
总之,KMP_AFFINITY=scatter 在 AMD 机器上显示完全相反的 ( 以错误的方式 ) 行为,它甚至会覆盖原生 OpenMP 环境,无论 CPU 品牌如何。整个情况听起来有点可疑,因为众所周知,ICC 会检测 CPU 品牌并使用 MKL 中的 CPU 调度程序在非 Intel 机器上启动较慢的代码。那么,如果 ICC 检测到非 Intel CPU,为什么不能简单地禁用 KMP_AFFINITY 并恢复 OMP_PROC_BIND 呢?
这对某人来说是一个已知问题吗?或者有人可以验证我的发现?
为了提供更多背景信息,我是商业计算流体动力学程序的开发人员,不幸的是,我们将我们的程序与 ICC OpenMP 库链接,并且默认设置 KMP_AFFINITY=scatter,因为在 CFD 中我们必须解决大规模稀疏线性系统,这部分非常内存绑定(bind)。我发现设置 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 混合使用手动将线程绑定(bind)到核心。此外,numactl应该用于控制内存 channel 绑定(bind)或更一般的 NUMA 效果。
长答案 :
线程绑定(bind) : OMP_PLACES可用于将每个线程绑定(bind)到特定内核(减少上下文切换和 NUMA 问题)。 OMP_PROC_BINDKMP_AFFINITY理论上应该正确地做到这一点,但在实践中,它们在某些系统上无法做到这一点。请注意 OMP_PROC_BINDKMP_AFFINITY是独占选项:它们不应一起使用(OMP_PROC_BIND 是旧的 KMP_AFFINITY 环境变量的新可移植替代品)。随着核心拓扑从一台机器更改为另一台机器,您可以使用 hwloc获取 OMP_PLACES 所需的 PU id 列表的工具.尤其是 hwloc-calc获取列表和hwloc-ls检查 CPU 拓扑。所有线程应单独绑定(bind),以免移动。您可以使用 hwloc-ps 检查线程的绑定(bind)。 .
NUMA 效果 :AMD 处理器是通过将多个 CCX 与高带宽连接(AMD Infinity Fabric)连接在一起而构建的。因此,AMD 处理器是 NUMA 系统。如果不考虑,NUMA 效果可能会导致性能显着下降。 numactl工具旨在控制/减轻 NUMA 影响:进程可以使用 --membind 绑定(bind)到内存 channel 选项和内存分配策略可以设置为--interleave (或 --localalloc 如果进程支持 NUMA)。理想情况下,进程/线程应该只处理在其本地内存 channel 上分配和首次接触的数据。如果您想在给定的 CCX 上测试配置,您可以使用 --physcpubind--cpunodebind .
我的猜测是 Intel/Clang 运行时在 KMP_AFFINITY=scatter 时没有执行良好的线程绑定(bind)。由于错误的 PU 映射(可能来自操作系统错误、运行时错误或错误的用户/管理员设置)而设置。可能是由于 CCX(因为包含多个 NUMA 节点的主流处理器非常罕见)。
在 AMD 处理器上,访问另一个 CCX 内存的线程通常会支付额外的大量成本,因为数据通过(相当慢的)Infinity Fabric 互连移动,并且可能由于其饱和以及内存 channel 之一。我建议您不要相信 OpenMP 运行时的自动线程绑定(bind)(使用 OMP_PROC_BIND=TRUE),而是手动执行线程/内存绑定(bind),然后在需要时报告错误。
以下是生成的命令行示例,以便运行您的应用程序:numactl --localalloc OMP_PROC_BIND=TRUE OMP_PLACES="{0},{1},{2},{3},{4},{5},{6},{7}" ./app PS :注意 PU/核心 ID 和逻辑/物理 ID。

关于openmp - 英特尔 OpenMP 库通过设置 KMP_AFFINITY=scatter 显着降低 AMD 平台上的内存带宽,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64409563/

相关文章:

javascript - 如何使用 RequireJS AMD 让函数公开其对象和成员方法

javascript - 使用 requireJS 的模块定义的区别

c - 需要一种快速方法将大量 double 转换为字符串

java - 在 COMPAS 中定义的任务中找不到文件

javascript - config.js 的 requirejs 加载超时(优化)

powershell - MS Compute Cluster 2003 命令行 - Powershell WMI

concurrency - 使用 OpenMP 的数组中的最大值

multithreading - 在混合 MPI/OpenMP 中进行 MPI 调用的线程

C++ NUMA 优化

将 OpenMP 代码编译为 C 代码