上一篇 下一篇 分享链接 返回 返回顶部

日本AMD服务器多核性能为何受NUMA影响:EPYC内存访问机制怎么工作

发布人:Minchunlin 发布时间:2 天前 阅读量:16
日本AMD服务器多核性能为何受NUMA影响:EPYC内存访问机制怎么工作

参数不等于体验。在日本AMD服务器上,EPYC的核心数量、频率和标称内存容量只能说明计算资源的上限,不能直接推导出多核业务的实际吞吐。真正决定结果的,往往是线程运行在哪个NUMA节点、数据首次分配到哪个节点,以及多个线程是否同时争用同一组内存控制器。

NUMA影响多核性能的核心原因是:每个CPU节点都有更近的本地内存,也可能需要通过Infinity Fabric访问远端内存。线程访问本地内存时路径更短、带宽竞争更容易控制;访问远端内存时,需要经过片间互连或处理器间互连,延迟、链路占用和缓存一致性开销都会增加。测试时应先确认服务器实际暴露的NUMA拓扑,再通过本地、远端和交错分配三组结果判断影响,而不是只看CPU总核心数。

先区分几个容易混淆的参数

NUMA节点、Socket与CCD不是同一层级

在操作系统看来,NUMA节点是可以分别进行CPU和内存绑定的资源区域。双路EPYC服务器通常至少会体现出与Socket相关的NUMA距离;在支持NPS模式的单路或双路平台上,一个Socket还可能被划分为多个NUMA节点。

EPYC内部的CCD、CCX、IOD和内存控制器属于更底层的硬件结构:

  • CCD或CCX主要描述核心与缓存的组织方式,不等于一个独立的NUMA节点。
  • IOD负责处理器内部的I/O、内存访问和互连请求,具体结构随EPYC代际变化。
  • 内存控制器和内存通道决定数据如何进入内存,通道是否均衡使用会影响可达到的带宽。
  • NUMA节点是固件和操作系统向调度器呈现的访问域,实际划分由处理器、BIOS设置和平台实现共同决定。

因此,不能看到EPYC采用多芯粒设计,就直接把每个CCD当作一个NUMA节点。判断依据应以操作系统报告的节点、CPU列表和内存归属为准。

NPS影响的是拓扑粒度,不是凭空增加带宽

部分EPYC平台提供NPS(NUMA Per Socket)选项。在平台和固件支持的前提下,常见的NPS1、NPS2或NPS4会改变一个Socket被划分为几个NUMA域。

其影响可以这样理解:

  • 较少的NUMA节点:操作系统看到的拓扑更简单,线程和内存不容易因为节点过多而错配,但应用对局部性的控制粒度较粗。
  • 较多的NUMA节点:可以更精细地把线程和内存分开,适合能够按节点划分任务的应用;如果应用没有做好绑定,也更容易出现线程在一个节点、数据在另一个节点的情况。
  • 节点容量变小:节点划分后,单个节点可用的本地内存容量也可能减少。一个进程若需要大量连续内存,可能不得不跨节点访问。
  • 总资源不变:NPS改变的是访问组织和可见拓扑,不代表处理器因此获得了额外的内存通道或更高的物理带宽。

NPS的具体可选项和行为取决于EPYC型号、主板固件和内存配置。选择日本AMD服务器时,不能只询问“是否为EPYC”,还应确认实例或裸机实际向操作系统暴露了几个NUMA节点。

内存通道与NUMA节点解决的是不同问题

内存通道决定同一个内存节点能同时处理多少数据请求。多线程程序即使全部运行在本地节点,如果线程数量继续增加,仍然可能先达到内存控制器或通道的带宽上限。

这意味着:

  • 核心数增加,计算能力可能增加;
  • 内存通道数量和实际均衡插入情况,决定内存带宽上限;
  • NUMA绑定决定线程访问哪一组内存资源;
  • 多个节点之间是否均衡使用,决定整机能否接近聚合带宽。

BIOS中的通道交错、Socket级内存交错和NPS配置不能混为一谈。通道交错通常用于提高一个节点内部的带宽利用率;跨节点交错则可能让数据分布更平均,但也会让单次访问不再始终落在本地内存。

EPYC的内存访问路径如何产生性能差异

一个核心执行加载或存储指令时,通常先查询缓存。只有在目标数据不在相应缓存层级,或者数据被其他核心修改后需要重新获取时,访问才会继续向内存系统发出请求。

在简化情况下,访问路径可以分为两类:

  1. 线程所在核心访问其NUMA节点对应的内存控制器;
  2. 线程所在核心访问其他NUMA节点的内存,需要经过处理器内部互连,双路场景还可能经过Socket之间的互连。

第一种情况称为本地内存访问。第二种情况称为远端内存访问。远端不一定意味着跨物理服务器,也可能只是同一个Socket内部的另一个NUMA域。

远端访问产生影响的原因主要有三点:

  • 路径更长:请求需要经过额外的互连和路由;
  • 共享链路竞争:多个核心同时读取远端内存时,会共同占用互连带宽;
  • 数据一致性流量增加:多个核心频繁修改同一数据时,缓存行需要在不同核心或节点之间转移,延迟可能高于独立读取。

在单线程、缓存命中率很高的任务中,这些差异可能不明显;在大数据集、随机访问或多线程内存带宽测试中,差异会被放大。

Linux的“首次触碰”决定了很多内存归属

Linux通常采用first-touch策略:匿名内存页在首次被某个线程实际写入时,倾向于分配到该线程所在的NUMA节点。

这会造成一个常见现象:

  • 主线程在节点0上初始化全部数组;
  • 工作线程随后分布到节点0和节点1;
  • 节点1上的线程访问的数据仍主要在节点0;
  • 多核扩展后,部分访问变成远端访问。

因此,单纯给进程增加线程数并不能保证内存访问自动均衡。初始化线程的位置、并行初始化方式、线程亲和性和内核的自动NUMA平衡策略,都会影响最终结果。

文件映射、页缓存、透明大页和内存回收也可能改变页面位置。测试时如果没有记录这些条件,同一台日本AMD服务器的两次结果也可能出现偏差。

参数对业务表现分别意味着什么

参数或现象主要影响不能直接说明什么建议验证方式
CPU核心数并行计算能力和可运行线程数不代表内存带宽按比例增加观察不同线程数下的吞吐曲线
NUMA节点数量线程与内存的绑定粒度不代表节点越多性能越高使用numactl --hardware确认
NPS模式Socket内部的拓扑呈现方式不代表处理器频率或总内存自动提升记录固件配置并对照操作系统节点
内存通道使用情况单节点带宽上限不代表访问延迟一定更低做本地带宽测试并核对内存配置
本地/远端访问比例延迟、互连流量和有效带宽不代表所有业务都会同比下降结合numastat和业务延迟观察
线程亲和性核心与节点的固定关系不代表数据已经在对应节点同时检查CPU绑定和内存归属
内存交错负载在多个节点或通道间的分布不代表每次访问都是本地访问对比本地绑定和交错分配结果

其中,CPU核心数最容易被过度解读。对于计算密集型、数据主要驻留缓存的程序,核心数可能是主要变量;对于内存带宽受限的程序,增加核心数后吞吐可能很快趋于平坦;对于随机访问和共享数据较多的程序,远端访问比例和访问延迟可能比核心数量更重要。

在日本AMD服务器上建立可复测的NUMA测试

先记录测试环境

性能结论必须绑定具体环境。至少应记录以下信息:

  • 测试时间、服务器是裸机还是虚拟机;
  • EPYC的完整型号、Socket数量和操作系统版本;
  • 内核版本、CPU频率策略、SMT状态;
  • BIOS中的NPS、内存交错及相关拓扑设置;
  • NUMA节点数量、每个节点的CPU列表和内存容量;
  • 内存通道是否均衡、测试时是否存在其他负载;
  • 测试程序版本、编译选项、线程数和数据集大小。

可以先使用只读命令采集操作系统看到的拓扑:

date -Is
uname -a
lscpu
lscpu -e=CPU,CORE,SOCKET,NODE
numactl --hardware
cat /sys/devices/system/node/node*/cpulist

如果系统未安装numactl,不要直接根据CPU型号猜测NUMA结构。可以先使用lscpu和/sys/devices/system/node/确认系统是否暴露了多个节点,再根据发行版的软件包管理方式补充测试工具。

重点观察以下内容:

  • available显示多少个NUMA节点;
  • 每个节点包含哪些CPU;
  • 每个节点的内存大小是否明显不均衡;
  • CPU、Socket与NUMA节点是否存在交叉关系;
  • 虚拟机是否只暴露了一个简化后的虚拟节点。

如果虚拟机只显示一个NUMA节点,测试结果只能说明当前虚拟拓扑下的行为,不能据此推断完整EPYC物理平台的远端访问差异。

设计本地、远端和交错三组测试

假设numactl --hardware确认存在节点0和节点1,且测试程序名为./memory_benchmark,可以使用相同的程序、数据集和线程数进行以下对照:

export OMP_PLACES=cores
export OMP_PROC_BIND=close
export OMP_NUM_THREADS=8

numactl --cpunodebind=0 --membind=0 ./memory_benchmark
numactl --cpunodebind=0 --membind=1 ./memory_benchmark
numactl --cpunodebind=0 --interleave=all ./memory_benchmark

三条命令的含义分别是:

  • 本地测试:线程运行在节点0,内存严格从节点0分配;
  • 远端测试:线程仍运行在节点0,但内存严格从节点1分配;
  • 交错测试:线程运行在节点0,内存页在可用节点之间交错分布。

--membind属于严格约束。如果目标节点没有足够可用内存,程序可能分配失败,而不是自动切换到其他节点。执行前应确认节点容量,并避免在生产业务进程上直接进行严格绑定。

如果服务器只有一个NUMA节点,不应强行构造“远端测试”。这通常意味着NPS1、单Socket单节点,或者虚拟化平台没有向实例暴露完整拓扑。此时可以测试不同线程数下的带宽扩展,但不能得出同一平台内部远端访问的结论。

线程数不能只测一个点

建议至少覆盖以下几类线程规模:

  1. 单线程或单核心,用于建立基础延迟和带宽;
  2. 一个NUMA节点内逐步增加线程,观察本地带宽何时饱和;
  3. 跨多个NUMA节点增加线程,观察聚合带宽是否继续增长;
  4. 与真实业务相同的线程数和数据集,验证微基准结果是否成立。

内存带宽测试的数据集应足够大,不能完全停留在缓存中;随机访问延迟测试则应使用能够区分缓存命中与内存访问的工作集。具体数据规模应根据服务器内存容量和测试工具实现确定,不宜用一个固定数值套用所有EPYC型号。

每组测试应包含预热、多个重复样本和稳定运行阶段。报告时至少保留中位数、离散程度和异常样本,并记录测试期间是否有其他进程抢占CPU或内存带宽。

如何解释测试结果

带宽结果

对本地和远端测试分别记录读、写或复制带宽。可以使用以下相对指标,而不预设某个EPYC型号必须达到固定数值:

\[ 远端带宽比 = \frac{远端带宽}{本地带宽} \]

这个比值越低,说明当前线程与内存的跨节点访问代价越明显,但它不是一个脱离环境的通用常数。内存通道配置、节点数量、数据访问模式和测试线程数都会改变结果。

还要观察线程扩展曲线。如果单节点内线程增加后带宽很快趋于平坦,通常说明该节点的内存通道或控制器已经接近饱和;如果跨节点后总带宽继续上升,说明业务具备利用多个内存节点的条件。反过来,如果跨节点后吞吐下降,则可能是远端访问开销、互连竞争或线程与数据绑定不匹配。

延迟结果

随机访问、指针追踪和内存数据库类负载应重点观察访问延迟,而不是只看聚合带宽。应分别测量:

  • 本地节点随机访问延迟;
  • 远端节点随机访问延迟;
  • 数据在多个节点交错分布时的延迟;
  • 真实业务的平均延迟和高分位延迟。

如果平均吞吐没有明显下降,但P95或P99延迟明显上升,说明少量跨节点访问可能正在影响尾延迟。这类情况在共享缓存、锁竞争或请求数据分布不均的服务中更值得关注。

NUMA计数器

业务运行期间可以使用以下命令查看内存节点活动:

numastat -m
numastat -p 

不同内核版本显示的字段可能略有差异。通常可以关注本地节点访问、其他节点访问、NUMA miss以及页面迁移相关统计。观察方法应是比较业务运行前后的增量,而不是只看系统启动以来的累计值。

如果进程绑定在节点0,却持续出现较多其他节点内存访问,应进一步检查:

  • 内存是否由主线程在错误的节点上首次初始化;
  • 工作线程是否被调度到预期节点;
  • 应用是否主动使用了跨节点共享内存;
  • 自动NUMA平衡是否迁移了页面;
  • 虚拟机或容器的内存限制是否与宿主机节点不一致。

不同业务为什么受影响程度不同

计算密集型任务

如果数据主要驻留在缓存中,或者每次加载数据后进行大量计算,NUMA影响可能相对有限。此时应优先观察核心利用率、频率和线程扩展效率。

但当数据集超过缓存容量后,原本的计算密集型任务也可能转变为内存受限任务。不能只根据程序名称判断,必须结合实际工作集和硬件计数器复测。

内存带宽型任务

批量扫描、数组处理、部分科学计算和大规模数据转换通常会持续读取或写入内存。此类任务常见的限制是:

  • 单个NUMA节点的内存通道先达到上限;
  • 更多核心只能增加排队,不能同比增加吞吐;
  • 跨节点线程可以利用更多带宽,但会引入互连竞争;
  • 数据没有按线程分片时,部分节点可能空闲、部分节点过载。

这类业务适合按照NUMA节点拆分工作集,并让负责处理某片数据的线程尽量运行在对应节点。

低延迟和共享数据型任务

随机访问、锁密集型服务以及多个线程频繁修改同一缓存行时,对远端访问和缓存一致性更敏感。即使总带宽看起来充足,跨节点缓存行来回转移也可能推高请求延迟。

此时应同时记录线程迁移、内存节点访问和业务P99延迟,不能只用一个STREAM类带宽结果作判断。

虚拟化环境

如果日本AMD服务器提供的是虚拟机,客户看到的vCPU数量不一定等于完整物理拓扑。可能出现以下情况:

  • 客户系统只看到一个NUMA节点;
  • vCPU被分配在多个物理节点,但访客系统无法识别;
  • 虚拟机暴露了vNUMA,但内存实际没有按相同方式绑定;
  • 多个实例共享同一组物理内存控制器。

因此,虚拟机测试应明确标注“访客系统可见拓扑”和“实际可验证范围”。如果业务对NUMA局部性敏感,应在服务商侧确认是否提供裸机或完整vNUMA拓扑,而不能只按vCPU数量比较。

常见异常与复核方法

只有一个NUMA节点

这可能是NPS1配置、单节点平台,也可能是虚拟机隐藏了物理拓扑。先核对固件和虚拟机规格,再决定是否有条件进行远端测试。在确认前,不要把“单节点”理解成物理服务器一定只有一个内存访问域。

本地和远端结果几乎一样

优先排查工作集是否实际命中缓存、内存是否在测试线程启动前已经被其他节点分配,以及绑定参数是否生效。也可能是测试平台只暴露了一个节点,所谓远端命令并没有形成真正的远端访问。

本地结果反而低于远端结果

这不是正常规律,应先检查线程数量、节点内存是否充足、内存通道是否均衡,以及两个测试是否使用了完全相同的初始化和预热过程。短时间测试、后台负载和频率变化也可能造成反常样本,不能凭一次运行调整BIOS。

多核吞吐扩展很快停止

常见原因包括单节点内存带宽已经饱和、线程数超过有效并行度、数据访问存在锁竞争,或者所有数据被主线程初始化到了同一个节点。结合带宽曲线、CPU利用率和numastat结果,可以区分是内存瓶颈还是调度问题。

从业务指标反推日本AMD服务器的NUMA配置

选型和验收时,可以按以下顺序判断:

  1. 先确认业务是计算受限、内存带宽受限,还是访问延迟受限;
  2. 确认实际实例或裸机暴露的NUMA节点数量,以及每个节点的CPU和内存;
  3. 用本地、远端、交错三组测试建立相对基线;
  4. 使用真实数据集和真实线程数验证微基准结果;
  5. 对延迟型业务关注P95、P99和跨节点访问比例,对吞吐型业务关注单节点与聚合带宽;
  6. 如果应用能够按节点拆分,就优先采用CPU与内存同节点绑定;如果应用必须共享大内存,则重点评估交错分配和跨节点代价;
  7. 将测试时间、固件配置、内核版本、拓扑和负载状态写入报告,避免把一次测试结果当成所有日本AMD服务器的固定性能。

最终应比较的是“业务在目标线程数和目标数据集下的有效吞吐或尾延迟”,而不是处理器宣传页上的核心总数。EPYC的多核能力只有在CPU、内存通道、NUMA节点和线程数据布局相互匹配时,才能稳定转化为应用性能。

目录结构
全文