产品探索我们的产品。有适用于GPU的Edera也有适用于容器的Edera。使用案例探索我们的使用案例涵盖多租户隔离、不可信代码执行、AI代理沙箱化。资源探索我们的资源包括葡萄藤博客、活动、文档还能在GitHub提交PR。公司认识Edera。了解为何选择Edera可联系我们交流看看人们对Edera的评价关注近期新闻与活动。也可直接联系我们。产品有适用于容器和适用于GPU的产品。使用案例包括多租户隔离、不可信代码执行、AI代理沙箱化。资源有葡萄藤博客、活动、文档。我们是谁可了解为何选择Edera查看社交好评关注新闻与活动。幕后故事涉及职业发展和法律条款。关注我们可在领英、YouTube、Bluesky关注我们。支持可联系Edera也能访问Edera GitHub。NUMA - 第一部分核心、内存及其之间的距离2026年6月23日史蒂文·努南发布文章。在同一主机上配置相同、运行相同工作负载的两个虚拟机其中一个始终比另一个慢20%。工作负载、主机均无问题也无其他租户竞争只是运行该虚拟机的CPU与它的内存位于互连两侧操作员无法在虚拟机内部调整解决。这就是本系列文章要讲述的故事。Edera推出一系列更改使基于Xen的虚拟化能够端到端地感知NUMA涵盖虚拟机内部、半虚拟化I/O驱动程序、dom0以及管理程序对主机硬件的视角部分功能首次实现。为解释这些更改的重要性需先了解NUMA到底是什么。NUMA的起源从UMA到多插槽服务器NUMA即非统一内存访问其定义属性是在NUMA机器上内存访问成本不均匀进行访问的CPU、数据所在的物理内存库以及两者之间的关系都会影响访问成本。与NUMA相对的是UMA统一内存访问在UMA架构中每个CPU通过相同的内存控制器以相同的成本访问内存的每一个字节适用于单插槽商用服务器和许多嵌入式系统。UMA概念简单但扩展存在限制大致是“在一块带有一组内存通道的硅片上能容纳的核心数量”。UMA扩展到一定程度会遇到瓶颈原因包括电气走线变长、信号传输时间增加、总线减速以保证信号完整性、单个内存控制器无法以全带宽为任意数量的核心提供数据、封装上的引脚数量限制内存通道数量、多个CPU同时读取内存时的总线争用问题等。业界的解决方案是为每个插槽或芯片配备自己的内存控制器机器变成NUMA架构。每个节点都有自己的CPU和内存CPU可从任何节点读取数据但读取远程节点的数据需要通过互连互连速度相对本地DRAM访问较慢。UMA中CPU 0、CPU 1、CPU 2、CPU 3共享一个内存控制器和内存池每个访问成本相同NUMA中节点0有CPU 0、CPU 1和自己的内存控制器及内存节点1有CPU 2、CPU 3和自己的内存控制器及内存节点之间通过互连连接本地访问成本低远程访问需通过互连。可将互连想象成两个相邻城镇之间的桥梁交通顺畅时过桥速度稍慢高峰期成本取决于排队情况在讨论NUMA在繁忙生产工作负载和安静微基准测试中的表现时会再次提及。回顾NUMA的发展历程20世纪90年代商用NUMA出现在大型主机系统中如SGI的Origin系列、Sequent NUMA - Q、Compaq的AlphaServer GS系列采用定制的节点间连接架构。2003年AMD的Opteron处理器将NUMA引入商用x86架构为每个插槽配备内存控制器并通过HyperTransport连接插槽。2008年英特尔凭借Nehalem和QuickPath InterconnectQPI迎头赶上此前长期使用真正的UMA架构。如今英特尔产品使用Ultra Path InterconnectUPIQPI的继任者AMD产品使用Infinity FabricHyperTransport的继任者每一代产品都大幅提高绝对带宽但远程DRAM访问与本地DRAM访问的成本比大致保持在相同范围内。曾经每个插槽被认为是一个NUMA节点但到了2010年代后期情况发生变化。AMD基于Zen架构的EPYC处理器将每个插槽的核心数量提高采用多个小芯片组成的架构一个插槽内包含多个核心复合体芯片CCD每个CCD有自己的L3缓存和内存控制器通过Infinity Fabric连接。因此根据具体型号和BIOS配置单个EPYC插槽可向操作系统呈现多个NUMA节点。英特尔的子NUMA集群SNC功能在最近的至强处理器中默认关闭但可在BIOS中启用会对芯片内的内存控制器网络进行分区并将结果呈现为每个插槽多个NUMA节点。“一个插槽一个节点”的说法在几年前在商用硬件上就不再正确现代系统中有趣的NUMA复杂性来自单个双插槽机箱内部现在拥有四到八个节点很常见。“慢”的程度有多大具体数值因平台而异人们对精确测量方法存在争议但一个有用的经验法则是在现代服务器上远程DRAM访问的成本大约是本地访问的1.5到3倍带宽也会相应降低。这些数值是在空闲机器上通过微基准测试得到的。在实际工作负载中差距通常更大因为互连是共享的。许多核心同时进行跨节点访问时会争夺相同的互连带宽导致每个请求的延迟增加这是很多“为什么这台服务器在高负载下变慢”问题的原因。微基准测试显示跨节点访问慢2倍但在生产工作负载中可能会慢4到5倍。需要注意的是NUMA效应不仅会降低受影响工作负载的速度还会使性能变得更不可预测。同一个请求的处理时间可能在80纳秒到250纳秒之间变化具体取决于当时互连的使用情况。平均延迟是有用的指标但NUMA问题通常首先体现在尾部延迟上。内存交错牺牲峰值性能换取稳定性有一种方法可以在不了解内存放置位置的情况下平滑成本曲线即内存交错。将页面以轮询的方式分配到所有节点无论请求任务所在的节点如何。在Linux中可使用 numactl --interleave all 实现一些数据库和JVM在自己的分配器中有类似操作一些管理程序在没有更好方法时会隐式地对虚拟机内存进行交错分配。BIOS层面也有类似功能大多数双插槽服务器固件有“节点交错”或“跨节点内存交错”选项可将物理内存以缓存行或页面粒度分散到各个插槽。启用该选项后操作系统会将其视为一个大的UMA资源池任何感知NUMA的代码都将失效。这个选项在双插槽机箱中常见但深层次问题在任何地方都相同交错分配扩展性差。节点越多任何核心与任何内存库之间的最坏情况路径就越长互连承担轮询流量的压力也越大。交错分配是用可预测的中等性能换取不可预测的混合性能随着拓扑结构的扩展这种权衡会更明显。在节点0上执行相同任务页面有三种放置方式感知NUMA的方式下访问快速且一致每次访问都是本地的不感知NUMA的方式下性能不可预测每次运行情况不同交错分配的方式下性能一致但平庸“让所有情况变得同样糟糕”页面可能位于节点0本地或节点1远程。交错分配使任何单个分配的预期成本大致等于机器上所有节点间成本的平均值。在双节点主机上大约一半的访问是本地的一半是远程的。延迟方差减小因为工作负载不再取决于恰好访问的页面而有好坏之分。一位前同事总结这种权衡为交错分配让所有情况变得同样糟糕是以牺牲峰值性能为代价来换取稳定性。原本可完全在单个节点上运行、只访问本地内存的工作负载现在每次分配中有一半都要支付远程访问成本。这是一种可行的方法有使用它的实际原因。在NUMA主机上运行的不感知NUMA的软件可通过交错分配获得可预测的性能“可预测的糟糕”通常比“不可预测的混合”更容易管理。对于需要占用整个机器内存带宽的工作负载来说甚至可能会受益因为它们可以聚合每个内存控制器的带宽而不是使单个控制器饱和。但这种方法也有局限性。如果工作负载了解自己的访问模式并且运行在能够提供正确拓扑结构并尊重其放置请求的系统上那么在延迟和吞吐量方面都可以比交错分配做得更好。本系列文章所涉及工作的核心就是让Xen虚拟机不再需要在“盲目尝试”和“盲目交错”之间做出选择虚拟机可以了解自己的位置并据此行动。两种亲和性而非一种很多开发者在整个职业生涯中都没有考虑过NUMA。如果部署目标是笔记本电脑、单插槽工作站或从单个主机节点划分出来的云虚拟机实际上一直都在使用UMA机器对NUMA感知工具最多只是略有耳闻。当工作负载迁移到更大的服务器时这个问题就会突然出现通常伴随着“这怎么这么慢”的疑问开发者会发现一个全新的放置维度。第一个维度是大家熟悉的CPU亲和性若使用过 taskset 或 sched_setaffinity 将进程固定到特定的CPU上就已经了解了问题的一半。任务对CPU有亲和性内核调度器会尽量将它们保持在指定的位置CPU亲和性在教程和博客文章中经常出现。另一个维度是内存亲和性出现频率低得多。在Linux中可通过 mbind()、set_mempolicy() 以及 libnuma 辅助函数如 numa_alloc_onnode()来控制这些函数决定了分配的内存来自哪个NUMA节点[numactl(8) 手册页](https://man7.org/linux/man - pages/man8/numactl.8.html)是实际操作的入口。内核也会自动进行NUMA平衡通过 NUMA_BALANCING 选项将页面迁移到经常访问它们的CPU所在的节点。了解Linux默认的内存放置行为很重要因为它会给很多人带来困扰。这种行为被称为“首次触摸”。当用户空间通过 malloc()、mmap() 等方式请求页面最终都会导致页面错误时页面并不会立即分配而是在某个CPU首次访问它时才会分配。页面最终落在哪一个节点上取决于进行“触摸”操作的CPU所在的节点而不是请求页面的CPU所在的节点如果两者不同。对于在同一线程中分配和使用内存的进程来说这没有问题。但对于常见的模式即一个初始化线程分配并清零一个大缓冲区然后其他节点上的工作线程使用该缓冲区这就会成为一个潜在的问题。每个页面都会落在初始化线程所在的节点上每个工作线程的访问都是远程访问操作员会看到“这个基准测试比预期慢但我找不到原因”的情况。CPU亲和性和内存亲和性之间的冲突是大多数有趣行为发生的地方。为了实现优化这两者必须一致。如果任务被固定到节点0但它的内存位于节点1那么情况会比不固定更糟因为确保了每次访问都是远程的。反之如果内存分配在节点0但任务被调度到节点1结果也是一样的。“首次触摸”是一种在操作员没有注意到的情况下导致两者不一致的具体情况。远程NUMA访问可能由以下三个原因导致任务相对于其数据运行在错误的CPU上。数据相对于任务位于错误的内存库中。或者两者都有问题。普通的Linux为用户空间提供了工具来处理所控制的进程的这些问题。更难的问题在于更高的层面操作系统本身对拓扑结构了解多少谁向谁暴露这些信息以及当其中一个层面失去感知能力时会发生什么。操作系统看到的内容 - 以及Xen的dom0所缺失的信息一个感知NUMA的内核不仅仅是知道存在拓扑结构。它会将物理内存映射划分为每个节点的块并跟踪每个节点的本地CPU然后将这些信息暴露给用户空间。numastat、numactl -H 以及 /sys/devices/system/node/ 层级结构是这个机制的可见部分用户空间进程可以询问“节点0上有多少空闲内存”或“我刚分配的内存的本地CPU是哪些”并得到真实的答案。底层的信息来源是固件的系统资源亲和性表SRAT和系统局部性信息表SLIT后续文章会再次提到。过去“一个插槽一个节点”的服务器每个节点的内存很充足如一个拥有256 GiB内存的双插槽服务器有两个128 GiB的节点“将工作负载放入一个节点”很容易。多节点插槽打破了这种直觉以同样的双插槽服务器为例安装EPYC处理器并将固件设置为NPS4每个插槽有四个节点每个主机有八个节点每个节点32 GiB。一个原本显然可以在单个节点上运行的工作负载可能突然需要两个或三个节点即使它的CPU占用可以轻松地放在一个节点上很多人第一次遇到时会感到惊讶。明确这里涉及的感知层次很重要因为出错的成本取决于哪个层次缺乏了解在NUMA硬件上运行在感知NUMA内核上的感知NUMA应用程序这是自20世纪90年代以来大家一直追求的目标延迟低方差小应用程序可以根据需要进行自我调整。运行在感知NUMA内核上的不感知NUMA应用程序这是实际中常见的情况。内核会尽力为应用程序进行平衡因此应用程序可以获得接近相同的平均性能但方差更大并且还有一些性能潜力未被挖掘。运行在不感知NUMA内核上的不感知NUMA应用程序一切都取决于“分配器碰巧放置的位置”。内核无法暴露它不知道的信息用户空间工具也无法询问没有答案的问题任何想要感知NUMA的应用程序都将无法实现。最后一种情况是最糟糕的而且令人惊讶的是当将两个这样的层次堆叠在一起时就会陷入这种情况。从一开始上游Xen的dom0就处于这种境地。它是NUMA主机上的特权虚拟机但Xen根本没有为它提供NUMA拓扑结构无论硬件实际上有多少个节点它都只看到一个无差别的机器。这不仅仅是一个表面上的问题dom0的内存位于主机的物理节点上它的vCPU位于主机的CPU上但由于没有拓扑结构可供参考dom0中的存储守护进程或容器运行时无法区分本地页面和远程页面因此它的一部分访问会在不知不觉中跨越互连而在dom0内部运行 numactl -H 只会报告一个整齐的节点其中的程序也不会意识到这个问题这就引出了接下来要讨论的虚拟化问题。两个层次各知一半普通的Linux机器只有一个内核、一个调度器和一个内存分配器它们共享状态在做出放置决策时至少有机会进行协调。虚拟化系统则有两个这样的层次堆叠在一起。管理程序分配主机内存并固定或浮动vCPU。虚拟机内核分配虚拟机内存并将自己的线程调度到vCPU上。这两个层次试图对相同的物理资源做出相同类型的决策但每个层次只能看到部分情况。这种不匹配的影响取决于管理程序下面的情况。在KVM中下面有一个完整的Linux内核虚拟机只是一个qemu进程主机内核的调度器、页面分配器和NUMA平衡器将其视为任何其他进程因此大多数NUMA机制已经可以免费应用于它。而在Xen、ESXi或Hyper - V父分区中没有底层操作系统管理程序直接驱动硬件如果它不理解NUMA那么主机上的任何程序都无法处理这个问题。这个区别对于本系列文章很重要。在KVM中主机Linux已经了解NUMA大多数相关的基础设施如qemu进程上的 numactl、cgroup CPU固定、自动NUMA平衡无需任何人构建新的东西就可以直接使用。在Xen中特权管理虚拟机通常被称为dom0“域零”它拥有物理设备驱动程序和启动其他所有虚拟机的工具栈。任何非特权虚拟机通常被称为domU“域U”表示非特权后续文章将自由使用这两个术语。dom0本身是管理程序的一个虚拟机下面的管理程序必须代表它做出这些放置决策。本系列文章的很多内容都是关于Xen追赶这个基线甚至在某些方面超越它。“KVM可以免费获得NUMA支持”的另一面也值得考虑让一个自主的主机端调度器在多个租户之间平衡NUMA正是那种会产生“吵闹邻居”问题的“智能”行为。由于VM B的放置发生了变化VM A的内存发生了迁移现在VM A的尾部延迟会因为VM A无法看到的原因而波动。在工作负载开始之前由控制平面做出明确的放置决策可以避免这类问题但前提是控制平面有准确的拓扑结构来做出决策这又回到了最初的两个层次不匹配的问题上。具体来说在Xen中这种不匹配表现为关于虚拟机NUMA体验的三个独立决策而虚拟机本身无法参与这些决策虚拟机的内存位于主机的何处工具栈在启动时选择管理程序会遵循这个放置决策。虚拟机通过读取其vNUMA拓扑结构如果有的话来了解决策结果但它无法对此发表意见。虚拟机的vCPU在主机上运行的位置由管理程序的调度器选择或者如果vCPU被固定则由操作员在启动时选择。虚拟机认为的拓扑结构是什么如果不做任何处理虚拟机将看到一个单一的统一地址空间无论其内存和vCPU实际上如何分布在主机上。在虚拟机内部运行 numactl -H 会显示一个平坦的节点而主机可能有八个节点虚拟机自己的感知NUMA的代码也没有实际的拓扑结构可供操作。第三个问题是最严重的因为它会使虚拟机自己的优化适得其反。管理程序可能已经完美地将虚拟机的内存和vCPU放在一起但虚拟机的用户空间信任内核显示的平坦映射将一个热点线程固定到一个vCPU上并随意分配该线程的缓冲区。一旦“随意”分配的缓冲区落在其后备内存位于另一个插槽的vnode上该线程的每次访问都会默默地跨越互连只要线程运行就会一直如此。大多数时候虚拟机很幸运但有时它会精心优化自己却走上了慢路径而这些“有时”正是操作员最终需要调试的问题。放置并不是Xen的分离式设计发挥作用的唯一方面调度器也是另一个方面在NUMA主机上这两个问题本质上是同一个问题的不同表现。在KVM主机上Linux调度器负责所有事情虚拟机vCPU线程、围绕它们的qemu I/O和模拟器线程、主机内核自己的工作、每个用户空间守护进程以及其他每个租户的工作负载都在同一个调度器上竞争相同的CPU时间。在负载下为一个租户提供可靠、公平的CPU时间需要大量的显式干预如 isolcpus 或cgroup cpusets、对每个线程类别进行 taskset 固定、更改调度器类别以避免饥饿等而且无论做多少统一的设计仍然是一个需要调整的问题。同样的不透明性也是在KVM栈上正确处理NUMA困难的原因。要将虚拟机的vCPU和内存放在同一个节点上首先需要确定qemu或云管理程序进程中哪些线程是vCPU而不是I/O线程、模拟器线程、迁移辅助线程然后手动固定线程和内存并在进程产生更多线程时保持它们的一致性。这是可行的也有人这样做但很麻烦而且很容易出现细微的错误这就是为什么最终会出现vCPU在一个节点上而它们访问的内存却在另一个节点上的情况。Xen的分离式调度器模型避免了这两个问题。Xen在管理程序层将虚拟机vCPU调度到主机pCPU上dom0内部的Linux调度器只将dom0自己的线程调度到dom0的vCPU上。这两个层次不会竞争因为它们在不同的抽象级别查看不同的资源。而且由于管理程序直接控制vCPU到pCPU的放置将虚拟机的核心和内存放在同一个节点上是工具栈在一开始就做出的决策而不是操作员通过cgroup原语和线程分析来重构的事情这与放置工作的前期决策思路一致只是在更低的层次上。这三个决策也不是全部的故事当开始考虑半虚拟化I/O时会出现第四个维度但这需要单独介绍。目前两个层次的不匹配已经足以引出后续的所有内容。本系列的后续内容本系列的下一部分将介绍Edera如何在任何虚拟机启动之前自动做出上述第1和第2项中的放置决策。简单来说在常见情况下无需配置为了解自己需求的工作负载提供显式的逃生通道并优先考虑稳定的性能而不是峰值性能详细内容将更有趣。未找到相关项目。你肯定想试试。让我们一起解决问题可探索演示。也可了解关于我们的信息关注职业发展、法律条款、博客联系我们查看信任中心。订阅我们的时事通讯提交表单可能成功收到信息也可能出现问题。可通过YouTube、Bluesky、GitHub、领英关注我们。订阅我们的时事通讯虽可能觉得又一份时事通讯麻烦但保证会带来很棒的内容。提交表单可能成功收到信息也可能出现问题。给我们留个言网络表单可能让人意愿落空但可试试看。正在加载表单Edera可能会就产品和服务联系可随时取消订阅通信如需了解更多信息可查看隐私政策。提交表单可能成功收到信息也可能出现问题。
Edera革新Xen虚拟化:揭秘NUMA原理与解决方案,提升性能稳定性
发布时间:2026/6/30 3:55:31
产品探索我们的产品。有适用于GPU的Edera也有适用于容器的Edera。使用案例探索我们的使用案例涵盖多租户隔离、不可信代码执行、AI代理沙箱化。资源探索我们的资源包括葡萄藤博客、活动、文档还能在GitHub提交PR。公司认识Edera。了解为何选择Edera可联系我们交流看看人们对Edera的评价关注近期新闻与活动。也可直接联系我们。产品有适用于容器和适用于GPU的产品。使用案例包括多租户隔离、不可信代码执行、AI代理沙箱化。资源有葡萄藤博客、活动、文档。我们是谁可了解为何选择Edera查看社交好评关注新闻与活动。幕后故事涉及职业发展和法律条款。关注我们可在领英、YouTube、Bluesky关注我们。支持可联系Edera也能访问Edera GitHub。NUMA - 第一部分核心、内存及其之间的距离2026年6月23日史蒂文·努南发布文章。在同一主机上配置相同、运行相同工作负载的两个虚拟机其中一个始终比另一个慢20%。工作负载、主机均无问题也无其他租户竞争只是运行该虚拟机的CPU与它的内存位于互连两侧操作员无法在虚拟机内部调整解决。这就是本系列文章要讲述的故事。Edera推出一系列更改使基于Xen的虚拟化能够端到端地感知NUMA涵盖虚拟机内部、半虚拟化I/O驱动程序、dom0以及管理程序对主机硬件的视角部分功能首次实现。为解释这些更改的重要性需先了解NUMA到底是什么。NUMA的起源从UMA到多插槽服务器NUMA即非统一内存访问其定义属性是在NUMA机器上内存访问成本不均匀进行访问的CPU、数据所在的物理内存库以及两者之间的关系都会影响访问成本。与NUMA相对的是UMA统一内存访问在UMA架构中每个CPU通过相同的内存控制器以相同的成本访问内存的每一个字节适用于单插槽商用服务器和许多嵌入式系统。UMA概念简单但扩展存在限制大致是“在一块带有一组内存通道的硅片上能容纳的核心数量”。UMA扩展到一定程度会遇到瓶颈原因包括电气走线变长、信号传输时间增加、总线减速以保证信号完整性、单个内存控制器无法以全带宽为任意数量的核心提供数据、封装上的引脚数量限制内存通道数量、多个CPU同时读取内存时的总线争用问题等。业界的解决方案是为每个插槽或芯片配备自己的内存控制器机器变成NUMA架构。每个节点都有自己的CPU和内存CPU可从任何节点读取数据但读取远程节点的数据需要通过互连互连速度相对本地DRAM访问较慢。UMA中CPU 0、CPU 1、CPU 2、CPU 3共享一个内存控制器和内存池每个访问成本相同NUMA中节点0有CPU 0、CPU 1和自己的内存控制器及内存节点1有CPU 2、CPU 3和自己的内存控制器及内存节点之间通过互连连接本地访问成本低远程访问需通过互连。可将互连想象成两个相邻城镇之间的桥梁交通顺畅时过桥速度稍慢高峰期成本取决于排队情况在讨论NUMA在繁忙生产工作负载和安静微基准测试中的表现时会再次提及。回顾NUMA的发展历程20世纪90年代商用NUMA出现在大型主机系统中如SGI的Origin系列、Sequent NUMA - Q、Compaq的AlphaServer GS系列采用定制的节点间连接架构。2003年AMD的Opteron处理器将NUMA引入商用x86架构为每个插槽配备内存控制器并通过HyperTransport连接插槽。2008年英特尔凭借Nehalem和QuickPath InterconnectQPI迎头赶上此前长期使用真正的UMA架构。如今英特尔产品使用Ultra Path InterconnectUPIQPI的继任者AMD产品使用Infinity FabricHyperTransport的继任者每一代产品都大幅提高绝对带宽但远程DRAM访问与本地DRAM访问的成本比大致保持在相同范围内。曾经每个插槽被认为是一个NUMA节点但到了2010年代后期情况发生变化。AMD基于Zen架构的EPYC处理器将每个插槽的核心数量提高采用多个小芯片组成的架构一个插槽内包含多个核心复合体芯片CCD每个CCD有自己的L3缓存和内存控制器通过Infinity Fabric连接。因此根据具体型号和BIOS配置单个EPYC插槽可向操作系统呈现多个NUMA节点。英特尔的子NUMA集群SNC功能在最近的至强处理器中默认关闭但可在BIOS中启用会对芯片内的内存控制器网络进行分区并将结果呈现为每个插槽多个NUMA节点。“一个插槽一个节点”的说法在几年前在商用硬件上就不再正确现代系统中有趣的NUMA复杂性来自单个双插槽机箱内部现在拥有四到八个节点很常见。“慢”的程度有多大具体数值因平台而异人们对精确测量方法存在争议但一个有用的经验法则是在现代服务器上远程DRAM访问的成本大约是本地访问的1.5到3倍带宽也会相应降低。这些数值是在空闲机器上通过微基准测试得到的。在实际工作负载中差距通常更大因为互连是共享的。许多核心同时进行跨节点访问时会争夺相同的互连带宽导致每个请求的延迟增加这是很多“为什么这台服务器在高负载下变慢”问题的原因。微基准测试显示跨节点访问慢2倍但在生产工作负载中可能会慢4到5倍。需要注意的是NUMA效应不仅会降低受影响工作负载的速度还会使性能变得更不可预测。同一个请求的处理时间可能在80纳秒到250纳秒之间变化具体取决于当时互连的使用情况。平均延迟是有用的指标但NUMA问题通常首先体现在尾部延迟上。内存交错牺牲峰值性能换取稳定性有一种方法可以在不了解内存放置位置的情况下平滑成本曲线即内存交错。将页面以轮询的方式分配到所有节点无论请求任务所在的节点如何。在Linux中可使用 numactl --interleave all 实现一些数据库和JVM在自己的分配器中有类似操作一些管理程序在没有更好方法时会隐式地对虚拟机内存进行交错分配。BIOS层面也有类似功能大多数双插槽服务器固件有“节点交错”或“跨节点内存交错”选项可将物理内存以缓存行或页面粒度分散到各个插槽。启用该选项后操作系统会将其视为一个大的UMA资源池任何感知NUMA的代码都将失效。这个选项在双插槽机箱中常见但深层次问题在任何地方都相同交错分配扩展性差。节点越多任何核心与任何内存库之间的最坏情况路径就越长互连承担轮询流量的压力也越大。交错分配是用可预测的中等性能换取不可预测的混合性能随着拓扑结构的扩展这种权衡会更明显。在节点0上执行相同任务页面有三种放置方式感知NUMA的方式下访问快速且一致每次访问都是本地的不感知NUMA的方式下性能不可预测每次运行情况不同交错分配的方式下性能一致但平庸“让所有情况变得同样糟糕”页面可能位于节点0本地或节点1远程。交错分配使任何单个分配的预期成本大致等于机器上所有节点间成本的平均值。在双节点主机上大约一半的访问是本地的一半是远程的。延迟方差减小因为工作负载不再取决于恰好访问的页面而有好坏之分。一位前同事总结这种权衡为交错分配让所有情况变得同样糟糕是以牺牲峰值性能为代价来换取稳定性。原本可完全在单个节点上运行、只访问本地内存的工作负载现在每次分配中有一半都要支付远程访问成本。这是一种可行的方法有使用它的实际原因。在NUMA主机上运行的不感知NUMA的软件可通过交错分配获得可预测的性能“可预测的糟糕”通常比“不可预测的混合”更容易管理。对于需要占用整个机器内存带宽的工作负载来说甚至可能会受益因为它们可以聚合每个内存控制器的带宽而不是使单个控制器饱和。但这种方法也有局限性。如果工作负载了解自己的访问模式并且运行在能够提供正确拓扑结构并尊重其放置请求的系统上那么在延迟和吞吐量方面都可以比交错分配做得更好。本系列文章所涉及工作的核心就是让Xen虚拟机不再需要在“盲目尝试”和“盲目交错”之间做出选择虚拟机可以了解自己的位置并据此行动。两种亲和性而非一种很多开发者在整个职业生涯中都没有考虑过NUMA。如果部署目标是笔记本电脑、单插槽工作站或从单个主机节点划分出来的云虚拟机实际上一直都在使用UMA机器对NUMA感知工具最多只是略有耳闻。当工作负载迁移到更大的服务器时这个问题就会突然出现通常伴随着“这怎么这么慢”的疑问开发者会发现一个全新的放置维度。第一个维度是大家熟悉的CPU亲和性若使用过 taskset 或 sched_setaffinity 将进程固定到特定的CPU上就已经了解了问题的一半。任务对CPU有亲和性内核调度器会尽量将它们保持在指定的位置CPU亲和性在教程和博客文章中经常出现。另一个维度是内存亲和性出现频率低得多。在Linux中可通过 mbind()、set_mempolicy() 以及 libnuma 辅助函数如 numa_alloc_onnode()来控制这些函数决定了分配的内存来自哪个NUMA节点[numactl(8) 手册页](https://man7.org/linux/man - pages/man8/numactl.8.html)是实际操作的入口。内核也会自动进行NUMA平衡通过 NUMA_BALANCING 选项将页面迁移到经常访问它们的CPU所在的节点。了解Linux默认的内存放置行为很重要因为它会给很多人带来困扰。这种行为被称为“首次触摸”。当用户空间通过 malloc()、mmap() 等方式请求页面最终都会导致页面错误时页面并不会立即分配而是在某个CPU首次访问它时才会分配。页面最终落在哪一个节点上取决于进行“触摸”操作的CPU所在的节点而不是请求页面的CPU所在的节点如果两者不同。对于在同一线程中分配和使用内存的进程来说这没有问题。但对于常见的模式即一个初始化线程分配并清零一个大缓冲区然后其他节点上的工作线程使用该缓冲区这就会成为一个潜在的问题。每个页面都会落在初始化线程所在的节点上每个工作线程的访问都是远程访问操作员会看到“这个基准测试比预期慢但我找不到原因”的情况。CPU亲和性和内存亲和性之间的冲突是大多数有趣行为发生的地方。为了实现优化这两者必须一致。如果任务被固定到节点0但它的内存位于节点1那么情况会比不固定更糟因为确保了每次访问都是远程的。反之如果内存分配在节点0但任务被调度到节点1结果也是一样的。“首次触摸”是一种在操作员没有注意到的情况下导致两者不一致的具体情况。远程NUMA访问可能由以下三个原因导致任务相对于其数据运行在错误的CPU上。数据相对于任务位于错误的内存库中。或者两者都有问题。普通的Linux为用户空间提供了工具来处理所控制的进程的这些问题。更难的问题在于更高的层面操作系统本身对拓扑结构了解多少谁向谁暴露这些信息以及当其中一个层面失去感知能力时会发生什么。操作系统看到的内容 - 以及Xen的dom0所缺失的信息一个感知NUMA的内核不仅仅是知道存在拓扑结构。它会将物理内存映射划分为每个节点的块并跟踪每个节点的本地CPU然后将这些信息暴露给用户空间。numastat、numactl -H 以及 /sys/devices/system/node/ 层级结构是这个机制的可见部分用户空间进程可以询问“节点0上有多少空闲内存”或“我刚分配的内存的本地CPU是哪些”并得到真实的答案。底层的信息来源是固件的系统资源亲和性表SRAT和系统局部性信息表SLIT后续文章会再次提到。过去“一个插槽一个节点”的服务器每个节点的内存很充足如一个拥有256 GiB内存的双插槽服务器有两个128 GiB的节点“将工作负载放入一个节点”很容易。多节点插槽打破了这种直觉以同样的双插槽服务器为例安装EPYC处理器并将固件设置为NPS4每个插槽有四个节点每个主机有八个节点每个节点32 GiB。一个原本显然可以在单个节点上运行的工作负载可能突然需要两个或三个节点即使它的CPU占用可以轻松地放在一个节点上很多人第一次遇到时会感到惊讶。明确这里涉及的感知层次很重要因为出错的成本取决于哪个层次缺乏了解在NUMA硬件上运行在感知NUMA内核上的感知NUMA应用程序这是自20世纪90年代以来大家一直追求的目标延迟低方差小应用程序可以根据需要进行自我调整。运行在感知NUMA内核上的不感知NUMA应用程序这是实际中常见的情况。内核会尽力为应用程序进行平衡因此应用程序可以获得接近相同的平均性能但方差更大并且还有一些性能潜力未被挖掘。运行在不感知NUMA内核上的不感知NUMA应用程序一切都取决于“分配器碰巧放置的位置”。内核无法暴露它不知道的信息用户空间工具也无法询问没有答案的问题任何想要感知NUMA的应用程序都将无法实现。最后一种情况是最糟糕的而且令人惊讶的是当将两个这样的层次堆叠在一起时就会陷入这种情况。从一开始上游Xen的dom0就处于这种境地。它是NUMA主机上的特权虚拟机但Xen根本没有为它提供NUMA拓扑结构无论硬件实际上有多少个节点它都只看到一个无差别的机器。这不仅仅是一个表面上的问题dom0的内存位于主机的物理节点上它的vCPU位于主机的CPU上但由于没有拓扑结构可供参考dom0中的存储守护进程或容器运行时无法区分本地页面和远程页面因此它的一部分访问会在不知不觉中跨越互连而在dom0内部运行 numactl -H 只会报告一个整齐的节点其中的程序也不会意识到这个问题这就引出了接下来要讨论的虚拟化问题。两个层次各知一半普通的Linux机器只有一个内核、一个调度器和一个内存分配器它们共享状态在做出放置决策时至少有机会进行协调。虚拟化系统则有两个这样的层次堆叠在一起。管理程序分配主机内存并固定或浮动vCPU。虚拟机内核分配虚拟机内存并将自己的线程调度到vCPU上。这两个层次试图对相同的物理资源做出相同类型的决策但每个层次只能看到部分情况。这种不匹配的影响取决于管理程序下面的情况。在KVM中下面有一个完整的Linux内核虚拟机只是一个qemu进程主机内核的调度器、页面分配器和NUMA平衡器将其视为任何其他进程因此大多数NUMA机制已经可以免费应用于它。而在Xen、ESXi或Hyper - V父分区中没有底层操作系统管理程序直接驱动硬件如果它不理解NUMA那么主机上的任何程序都无法处理这个问题。这个区别对于本系列文章很重要。在KVM中主机Linux已经了解NUMA大多数相关的基础设施如qemu进程上的 numactl、cgroup CPU固定、自动NUMA平衡无需任何人构建新的东西就可以直接使用。在Xen中特权管理虚拟机通常被称为dom0“域零”它拥有物理设备驱动程序和启动其他所有虚拟机的工具栈。任何非特权虚拟机通常被称为domU“域U”表示非特权后续文章将自由使用这两个术语。dom0本身是管理程序的一个虚拟机下面的管理程序必须代表它做出这些放置决策。本系列文章的很多内容都是关于Xen追赶这个基线甚至在某些方面超越它。“KVM可以免费获得NUMA支持”的另一面也值得考虑让一个自主的主机端调度器在多个租户之间平衡NUMA正是那种会产生“吵闹邻居”问题的“智能”行为。由于VM B的放置发生了变化VM A的内存发生了迁移现在VM A的尾部延迟会因为VM A无法看到的原因而波动。在工作负载开始之前由控制平面做出明确的放置决策可以避免这类问题但前提是控制平面有准确的拓扑结构来做出决策这又回到了最初的两个层次不匹配的问题上。具体来说在Xen中这种不匹配表现为关于虚拟机NUMA体验的三个独立决策而虚拟机本身无法参与这些决策虚拟机的内存位于主机的何处工具栈在启动时选择管理程序会遵循这个放置决策。虚拟机通过读取其vNUMA拓扑结构如果有的话来了解决策结果但它无法对此发表意见。虚拟机的vCPU在主机上运行的位置由管理程序的调度器选择或者如果vCPU被固定则由操作员在启动时选择。虚拟机认为的拓扑结构是什么如果不做任何处理虚拟机将看到一个单一的统一地址空间无论其内存和vCPU实际上如何分布在主机上。在虚拟机内部运行 numactl -H 会显示一个平坦的节点而主机可能有八个节点虚拟机自己的感知NUMA的代码也没有实际的拓扑结构可供操作。第三个问题是最严重的因为它会使虚拟机自己的优化适得其反。管理程序可能已经完美地将虚拟机的内存和vCPU放在一起但虚拟机的用户空间信任内核显示的平坦映射将一个热点线程固定到一个vCPU上并随意分配该线程的缓冲区。一旦“随意”分配的缓冲区落在其后备内存位于另一个插槽的vnode上该线程的每次访问都会默默地跨越互连只要线程运行就会一直如此。大多数时候虚拟机很幸运但有时它会精心优化自己却走上了慢路径而这些“有时”正是操作员最终需要调试的问题。放置并不是Xen的分离式设计发挥作用的唯一方面调度器也是另一个方面在NUMA主机上这两个问题本质上是同一个问题的不同表现。在KVM主机上Linux调度器负责所有事情虚拟机vCPU线程、围绕它们的qemu I/O和模拟器线程、主机内核自己的工作、每个用户空间守护进程以及其他每个租户的工作负载都在同一个调度器上竞争相同的CPU时间。在负载下为一个租户提供可靠、公平的CPU时间需要大量的显式干预如 isolcpus 或cgroup cpusets、对每个线程类别进行 taskset 固定、更改调度器类别以避免饥饿等而且无论做多少统一的设计仍然是一个需要调整的问题。同样的不透明性也是在KVM栈上正确处理NUMA困难的原因。要将虚拟机的vCPU和内存放在同一个节点上首先需要确定qemu或云管理程序进程中哪些线程是vCPU而不是I/O线程、模拟器线程、迁移辅助线程然后手动固定线程和内存并在进程产生更多线程时保持它们的一致性。这是可行的也有人这样做但很麻烦而且很容易出现细微的错误这就是为什么最终会出现vCPU在一个节点上而它们访问的内存却在另一个节点上的情况。Xen的分离式调度器模型避免了这两个问题。Xen在管理程序层将虚拟机vCPU调度到主机pCPU上dom0内部的Linux调度器只将dom0自己的线程调度到dom0的vCPU上。这两个层次不会竞争因为它们在不同的抽象级别查看不同的资源。而且由于管理程序直接控制vCPU到pCPU的放置将虚拟机的核心和内存放在同一个节点上是工具栈在一开始就做出的决策而不是操作员通过cgroup原语和线程分析来重构的事情这与放置工作的前期决策思路一致只是在更低的层次上。这三个决策也不是全部的故事当开始考虑半虚拟化I/O时会出现第四个维度但这需要单独介绍。目前两个层次的不匹配已经足以引出后续的所有内容。本系列的后续内容本系列的下一部分将介绍Edera如何在任何虚拟机启动之前自动做出上述第1和第2项中的放置决策。简单来说在常见情况下无需配置为了解自己需求的工作负载提供显式的逃生通道并优先考虑稳定的性能而不是峰值性能详细内容将更有趣。未找到相关项目。你肯定想试试。让我们一起解决问题可探索演示。也可了解关于我们的信息关注职业发展、法律条款、博客联系我们查看信任中心。订阅我们的时事通讯提交表单可能成功收到信息也可能出现问题。可通过YouTube、Bluesky、GitHub、领英关注我们。订阅我们的时事通讯虽可能觉得又一份时事通讯麻烦但保证会带来很棒的内容。提交表单可能成功收到信息也可能出现问题。给我们留个言网络表单可能让人意愿落空但可试试看。正在加载表单Edera可能会就产品和服务联系可随时取消订阅通信如需了解更多信息可查看隐私政策。提交表单可能成功收到信息也可能出现问题。