对于内存受限的程序,使用多个线程并不总是更快,比如与内核数量相同,因为线程可能会竞争内存通道。通常在双插槽机器上,线程越少越好,但我们需要设置关联策略,将线程分布在插槽之间以最大化内存带宽。
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=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 仍然存在,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 中我们必须解决大规模稀疏线性系统,这部分非常内存绑定。我发现设置 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 给出:
在设置 KMP_AFFINITY=scatter 时,我得到了
我将尝试最新的 ICC/Clang OpenMP 运行时,看看它是如何工作的。