1

我注意到 numactl对流基准有一些奇怪的影响

更具体地说,“numactl ./stream_c.exe”报告的内存带宽比“./stream_c.exe”低 40%。

我检查了 numactl源代码,如果我不给它任何参数,它应该没有什么特别之处。所以我天真地期望 numactl 在“numactl ./stream_c.exe”中没有性能影响,根据我的实验,这是不正确的。

这是具有高核数处理器的双插槽服务器。

使用 numastat,我可以看到 numactl 命令导致内存分配不平衡:两个 numa 节点将内存分配拆分为 80:20。

没有 numactl,内存以非常平衡的方式分配:46:54。

我还发现这不仅仅是一个 numactl 问题。如果我使用 perf 调用 stream_c.exe,内存分配甚至比使用 numactl 更不平衡。

所以这更像是一个内核问题:numactl 和 perf 如何更改子进程的内存放置策略?谢谢!

4

1 回答 1

2

TL;DR:使用的默认策略numactl可能会导致性能问题以及 OpenMP 线程绑定。numactl约束应用于所有(分叉的)子进程。

实际上,numactl默认情况下使用预定义的策略。这个策略可以是--interleaved, --preferred, --membind, --localalloc. 当第一次触摸页面完成时,此策略会更改操作系统页面分配的行为。以下是政策的含义:

  • --interleaved:内存页在节点集指定的节点之间分配,但以循环方式分配;
  • --preferred:内存是从单个首选内存节点分配的。如果没有足够的内存可用,可以从其他节点分配内存。
  • --membind: 只从节点分配内存。当这些节点上没有足够的可用内存时,分配将失败;
  • --localalloc:总是在当前节点上分配(执行第一次触摸内存页的节点)。

在您的情况下,指定一个--interleaved或一个--localalloc策略应该会提供更好的性能。我想如果线程绑定到核心,该--localalloc策略应该是最佳选择(见下文)。

此外,默认情况下,STREAM_ARRAY_SIZE宏设置为太小(10 Mo),无法实际测量 RAM 的性能。事实上,AMD EPYC 7742处理器有一个 256Mio L3 高速缓存,足以容纳其中的所有基准数据。将结果与大于 L3 缓存的值(例如 1024 Mio)进行比较可能要好得多。

最后,OpenMP 线程可能会从一个 numa 节点迁移到另一个节点。这会大大降低基准测试的性能,因为当线程移动到另一个节点时,访问的内存页面位于远程节点上,并且目标 NUMA 节点可能会饱和。您需要绑定 OpenMP 线程,以便它们无法移动,例如,使用此特定处理器的以下环境变量:OMP_NUM_THREADS=64 OMP_PROC_BIND=TRUE OMP_PLACES="{0}:64:1"假设 SMT 已禁用并且核心 ID 在 0-63 范围内。如果启用了 SMT,您应该OMP_PLACES使用以下命令调整变量:(OMP_PLACES={$(hwloc-calc --sep "},{" --intersect PU core:all.pu:0)}这需要在机器上安装软件包 hwloc)。您可以使用命令 hwloc-ps 检查线程绑定。


更新:

numactl影响所有子进程的 NUMA 属性(因为子进程是fork在 Linux 上创建的,应该复制 NUMA 属性)。您可以使用以下(bash)命令进行检查:

numactl --show
numactl --physcpubind=2 bash -c "(sleep 1; numactl --show; sleep 2;) & (sleep 2; numactl --show; sleep 1;); wait"

此命令首先显示 NUMA 属性。将其设置为并行启动 2 个进程的子 bash 进程。每个子子进程都显示 NUMA 属性。结果在我的 1 节点机器上如下:

policy: default                    # Initial NUMA attributes
preferred node: current
physcpubind: 0 1 2 3 4 5 
cpubind: 0 
nodebind: 0 
membind: 0 
policy: default                    # Sub-child 1
preferred node: current
physcpubind: 2 
cpubind: 0 
nodebind: 0 
membind: 0 
policy: default                    # Sub-child 2
preferred node: current
physcpubind: 2 
cpubind: 0 
nodebind: 0 
membind: 0 

在这里,我们可以看到--physcpubind=2约束应用于两个子子进程。它应该与--membind您的多节点计算机上的 NUMA 策略相同。因此,请注意,perf在子子进程之前调用不应对 NUMA 属性产生任何影响。

perf不应直接影响分配。但是,操作系统的默认策略可以平衡每个 NUMA 节点上分配的 RAM 量。如果是这种情况,页面分配可能是不平衡的,由于 NUMA 节点的饱和而降低了带宽。NUMA 节点之间的操作系统内存页面平衡非常敏感:例如,在两个基准测试之间写入一个巨大的文件会影响第二次运行(从而影响基准测试的性能)。这就是为什么应该始终为 HPC 基准测试(以及进程绑定)手动设置 NUMA 属性的原因。


PS:我假设流基准测试已经使用 OpenMP 支持进行了编译,并且也启用了优化(即-fopenmp -O2 -march=native在 GCC 和 Clang 上)。

于 2020-06-27T20:09:35.373 回答