auto NUMA学习记录-20230302
- 一、autonuma: Optimize memory placement for memory tiering system(autonuma:优化内存分层系统的内存放置)
- 二、Toward better NUMA scheduling(迈向更好的NUMA调度)
- 三、AutoNUMA: the other approach to NUMA scheduling(AutoNUMA:NUMA 调度的另一种方法)
- 四、Chapter 9. NUMA(第9章)
- 9.1. NUMA Memory Allocation Policies
- 9.2. Automatic NUMA Balancing
- 9.3. libvirt NUMA Tuning
- 9.3.1. Monitoring Memory per host NUMA Node(监控每个主机NUMA节点的内存)
- 9.3.2. NUMA vCPU Pinning(NUMA vCPU固定)
- 9.3.3. Domain Processes(域进程)
- 9.3.4. Domain vCPU Threads (域 vCPU 线程)
- 9.3.5. Using Cache Allocation Technology to Improve Performance(使用缓存分配技术提高性能)
- 9.3.6. Using emulatorpin(使用emulatorpin)
- 9.3.7. Tuning vCPU Pinning with virsh(使用 virsh 调整 vCPU 固定)
- 9.3.8. Tuning Domain Process CPU Pinning with virsh(使用 virsh 调整域进程 CPU 固定)
- 9.3.9. Tuning Domain Process Memory Policy with virsh( 使用 virsh 调整域进程内存策略)
- 9.3.10. Guest NUMA Topology(来宾 NUMA 拓扑)
- 9.3.11. NUMA Node Locality for PCI Devices
- 9.4. NUMA-Aware Kernel SamePage Merging (KSM)(NUMA 感知内核 SamePage 合并 (KSM))
一、autonuma: Optimize memory placement for memory tiering system(autonuma:优化内存分层系统的内存放置)
随着各种新内存类型的出现,可能会有多种一个系统中的内存类型,例如 DRAM 和 PMEM(持久内存)。因为不同类型内存的性能和成本可能不同,机器的内存子系统可以称为内存分层系统。
在提交 c221c0b0308f(“device-dax:“热插拔”持久内存,
像普通 RAM 一样使用”)之后,PMEM 可以用作单独 NUMA 节点中具有成本效益(cost-effective)的易失性内存。在典型的内存分层系统中,每个物理 NUMA 节点中都有 CPU、DRAM 和 PMEM。CPU 和 DRAM 将放在一个逻辑节点中,而 PMEM 将放在另一个(伪造的)逻辑节点中。
为了优化系统的整体性能,热点页面应该是放置在 DRAM 节点中。为此,我们需要识别中的热门页面PMEM 节点,并通过 NUMA 迁移将它们迁移到 DRAM 节点。
在autoNUMA中,有一套现有的机制来识别节点中的 CPU 最近访问的页面并迁移页面到节点。所以我们可以重用这些机制来构建优化内存分层系统中页面放置的机制。这已在此补丁集中实现。
另一方面,冷页应该放在 PMEM 节点中。所以,我们还需要识别 DRAM 节点中的冷页并迁移它们到 PMEM 节点。
在以下补丁集中,
[RFC][PATCH 0/9] [v4][RESEND] Migrate Pages in lieu of discard
https://lore.kernel.org/linux-mm/20201007161736.ACC6E387@…
实现了一种在内存压力下将冷DRAM页降级到PMEM节点的机制。在此基础上,可以主动将冷DRAM页面降级到PMEM节点,释放DRAM节点上的部分内存空间。这将释放DRAM节点上的空间,以便将热PMEM页面提升到。这个补丁集也实现了这一点.
该补丁集基于以下尚未合并的补丁集,
[RFC][PATCH 0/9] [v4][RESEND] Migrate Pages in lieu of discard
https://lore.kernel.org/linux-mm/20201007161736.ACC6E387@…
这是一个更大的补丁集的一部分。如果你想应用这些或玩它们,我建议使用下面的树,
https://github.com/hying-caritas/linux/commits/autonuma-r4
我们用pmbench内存访问基准测试了这个解决方案,读/写比率为80:20,访问地址分布在2核(socket)的Intel服务器上,使用Optane DC持久内存模型。基础内核和分步优化的测试结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eEZ0K70R-1677755617678)(C:\Users\49192\AppData\Roaming\Typora\typora-user-images\1677744614307.png)]
整个补丁集将基准测试分数提高到119.1%。基于AutoNUMA的基本优化方案(补丁1)、热页选择算法(补丁3)和阈值自动调整算法(补丁5)提高了性能或降低了开销(促销MB/s)大部分。
以上内容翻译自文章:autonuma: Optimize memory placement for memory tiering system
二、Toward better NUMA scheduling(迈向更好的NUMA调度)
了解Linux 内核 NUMA 已经有一段时间了,因为它知道将进程从一个节点移动到另一个节点可能是一项代价高昂的任务。还有一个接口(通过 mbind()系统调用可用),进程可以通过该接口为其内存请求特定的分配策略。可能性包括要求所有分配都发生在一组特定的节点 ( MPOL_BIND ) 中,设置更宽松的“首选”节点 ( MPOL_PREFERRED ),或要求分配分配到整个系统 ( MPOL_INTERLEAVE )。也可以使用 mbind()请求将页面从一个节点主动迁移到另一个节点。
所以 NUMA 不是内核的新概念,但是,正如 Peter Zijlstra 在介绍大型 NUMA 补丁集时指出的那样,事情并没有像它们应该的那样工作:
当前上游任务内存分配优先使用任务当前运行的节点(除非明确说明,请参阅 mbind()/set_mempolicy()),并且随着调度程序随意移动任务,任务的内存最终分布在整个机器的节点上。
虽然调度程序将短时间运行的任务保持在单个节点上做了合理的工作(通过简单地不经常进行跨节点迁移),但它完全打击了内存占用量大的长时间运行的进程。
正如所料,补丁集专用于创建不会“完全崩溃”的内核。为此,它对内核中的内存管理和调度方式进行了一些重大更改。
Peter 的补丁集包含三个主要的子部分。第一个是 Lee Schermerhorn 于 2010 年首次发布的经过修改的补丁集。这些补丁更改了内存策略机制,使内核在远程节点上分配进程内存后更容易修复问题。“页面迁移”是将页面从一个节点移动到另一个节点,而所属进程没有注意到更改的过程。使用 Lee 的补丁,内核实现了一种称为“惰性迁移”的变体,它不会立即重新定位任何页面。相反,目标页面只是从进程的页表中取消映射,这意味着下一次访问它们中的任何一个都会产生页面错误。然后在页面错误时完成实际迁移。惰性迁移是移动大量页面的一种成本较低的方法;只移动实际使用的页面,工作可以随着时间的推移而分散,
惰性迁移机制对于补丁集的其余部分是必需的,但它本身也具有价值。因此,该功能可通过MPOL_MF_LAZY标志提供给用户空间;它旨在与MPOL_MF_MOVE标志一起使用,否则会强制立即迁移受影响的页面。还有一个新的 MPOL_MF_NOOP标志允许调用进程根据当前策略请求页面迁移,而无需更改(甚至不知道)该策略。
通过惰性迁移,由于内存分配和调度决策而分布在系统中的内存可以慢慢拉回到最佳节点。但最好从一开始就避免造成这种混乱。因此,补丁集的第二部分首先向进程添加“主节点”的概念。每个进程(或“NUMA 实体”——意思是包含一组进程的组)在fork()时被分配一个主节点。然后,调度程序将努力避免将进程移出其主节点,但要在一定范围内:如果替代方案是一个不平衡的系统,则进程仍将在非主节点上运行。默认情况下,内存分配将在主节点上执行,即使该进程当时正在其他地方运行也是如此。
这些策略应该最大限度地减少内存在系统中的分散,但是,使用这种调度机制,最终一个节点将不可避免地出现进程过多和内存过少而其他节点未得到充分利用的情况。因此,有时需要重新平衡事物。当调度程序注意到长时间运行的任务被迫离开它们的主节点时——或者它们不得不在非本地分配内存——它会考虑将它们迁移到新节点。在这种情况下,迁移不是权宜之计;调度程序会将进程及其内存(使用惰性迁移机制)移动到目标节点。此举代价高昂,但一旦完成,流程(和系统)的运行效率应该会高得多。尽管它只对将要存在一段时间的进程有意义; 补丁集试图通过仅考虑具有至少一秒的迁移运行时间的进程来接近该目标。
最后一部分是一对新的系统调用,允许将进程放入共享同一主节点的“NUMA 组”。如果迁移其中一个,则将迁移整个组。第一个系统调用是:
int numa_tbind(int tid, int ng_id, unsigned long flags);
该系统调用会将tid 标识的线程绑定到ng_id标识的 NUMA 组;flags参数当前未使用,必须为零。如果ng_id作为MS_ID_GET传递,则系统调用将只返回给定线程的当前 NUMA 组 ID。相反,值为MS_ID_NEW会创建一个新的 NUMA 组,将线程绑定到该组,并返回新 ID。
第二个新的系统调用是:
int numa_mbind(void *addr, unsigned long len, int ng_id, unsigned long flags);
此调用将为从addr开始的len bytes区域设置内存策略,并将其绑定到由ng_id标识的 NUMA 组 。如有必要,将使用惰性迁移将内存移动到给定 NUMA 组所在的节点。同样,flags是未使用的,且必须为零。一旦内存绑定到 NUMA 组,它将留在该组中的进程;如果进程移动,内存将随之移动。
Peter 提供了一些双节点系统的benchmark结果。如果没有 NUMA 平衡补丁,随着时间的推移,benchmark最终会出现与本地访问一样多的远程内存访问——分配的内存分布在整个系统中。使用 NUMA 平衡器,86% 的内存访问是本地的,从而显著提高了速度。正如Peter所说:“这些数字还表明,虽然有了显著地改进,但仍有一些状况,会使效果变的很糟糕。当前的NUMA平衡器仍然有些变化无常。” 考虑到它是如此年轻,对于这样一个复杂的补丁集,一定程度的变化可能是意料之中的。经过一些时间、审查和测试,它应该会发展成为一个可靠的调度程序组件,为 Linux 提供比过去更好的 NUMA 性能。
以上翻译自文章:Toward better NUMA scheduling
三、AutoNUMA: the other approach to NUMA scheduling(AutoNUMA:NUMA 调度的另一种方法)
上周的内核页面包含一篇关于 Peter Zijlstra 的 NUMA 调度补丁集的文章。碰巧的是,Peter 并不是唯一从事该领域工作的开发人员;Andrea Arcangeli 发布了他自己的 NUMA 调度补丁集,称为AutoNUMA。Andrea 的目标是相同的——将进程和它们的内存放在同一个 NUMA 节点上——但实现目标所采用的方法却大不相同。这两个补丁集体现了对如何解决问题的分歧,可能需要一些时间才能解决。
Peter 的补丁集通过为每个进程分配一个“主节点”来工作,然后尝试将进程及其内存集中在该节点上。Andrea 的补丁缺少主节点的概念;他认为,除非开发人员添加代码以使用 Peter 的新系统调用,否则这个想法对于不适合单个节点的程序将无法很好地工作。相反,Andrea 希望 NUMA 调度能够像透明大页面一样“正常工作”。所以他的补丁集似乎假定资源将分布在整个系统中;然后它专注于事后清理。清理任务的关键是一堆统计数据和几个新的内核线程。
这些线程中的第一个线程称为knuma_scand. 它的主要工作是扫描每个进程的地址空间,用一组特殊的位标记其在 RAM 中的匿名页面,使这些页面在硬件看来就像它们不存在一样。如果进程试图访问这样的页面,将导致页面错误;内核将通过再次将页面标记为“存在”来做出响应,以便该进程可以继续其业务。但内核还会跟踪页面所在的节点和访问进程运行的节点,并记录任何不匹配。对于每个进程,内核维护一个计数器数组来跟踪它最近访问的每个页面位于哪个节点。对于页面,跟踪的信息必然更粗;内核只是记住访问每个页面(page)的最后一个节点(node)。
当调度程序做出决定的时候到了,它会传递每个进程的统计信息,以确定如果将目标进程移动到另一个节点,它是否会更好。如果该进程似乎正在远程访问它的大部分页面,并且它比已经在那里运行的进程更适合远程节点,它将被迁移过来。这段代码引起了Peter 的强烈反对,他不喜欢在调度程序的热路径(hot path)中间放置一个大的 for-each-CPU 循环的想法。经过一些轻微的阻力后,Andrea同意这个逻辑最终需要找到一个不同的家,在那里它不会经常运行。不过,对于测试,他喜欢事物本来的样子,因为它可以使调度程序更快地收敛到它选择的解决方案。
但是,如果进程的内存分布在多个 NUMA 节点上,那么移动进程的作用就有限了。从系统中获得最佳性能显然需要一种机制来将内存页面也收集到同一节点上。在 AutoNUMA 补丁中,第一个非本地故障(响应上述特殊页面标记)将导致该页面的“最后节点 ID”值设置为访问节点;该页面也将排队等待迁移到该节点。但是,来自不同节点的后续故障将取消该迁移;在第一个故障之后,需要来自同一节点的连续两个故障才能使页面排队等待迁移。
每个 NUMA 节点都有一个新的内核线程 ( knuma_migrated ),负责传递排队等待迁移的页面列表,并将它们实际移动到目标节点。迁移不是无条件的——例如,它取决于目标节点上是否有足够的可用内存。但是,大多数时候,这些迁移线程应该设法将页面拉到实际使用它们的节点。
除了上述关于将繁重的计算放入 schedule()的抱怨之外,Peter 还发现了很多不喜欢这个补丁集的地方。他不喜欢工作线程,首先:
The problem I have with farming work out to other entities is that its thereafter terribly hard to account it back to whoemever caused the actual work.
我对其他实体(entities)的耕种工作(farming work)遇到的问题是,此后很难将其归咎于造成实际工作的人。假设你的 kworker 线程消耗了大量的 cpu 时间——这个时间显然对你的应用程序不可用——但是你如何找出是什么/谁导致了这个问题并解决它?
Andrea 回应说,这些线程的成本小到无法真正衡量的程度。不过,要摆脱 Peter 的其他抱怨有点困难:这个补丁集占用了大量内存。内核为系统中的每一页内存维护一个结构页。由于一个典型的系统可能有数百万页,所以这个结构必须尽可能小。但是 AutoNUMA 补丁为每个页面结构添加了一个list_head结构(用于迁移队列)和两个计数器。最终结果可能是 AutoNUMA 机器丢失了大量内存。
计划是最终将此信息移出struct page;然后,除其他事项外,如果 AutoNUMA 未实际使用,内核可以完全避免分配它。但是,对于 NUMA 的情况,不管它的位置如何,内存仍然会被消耗,一些用户不太可能高兴,即使其他人,如 Andrea 断言的那样,如果他们能得到20%的性能提升,他们会很乐意放弃一大块内存。这个争论似乎不会在不久的将来得到解决,而且AutoNUMA的内存影响可能需要以某种方式减少。也许,你的editor天真地建议,knuma_migrated和它的每页list_head结构可以被 Peter’s 补丁中使用的“惰性迁移”方案所取代。
NUMA 调度很难,要正确执行它需要调度和内存管理方面的大量专业知识。因此,该问题受到一些社区顶级调度程序和内存管理开发人员的关注似乎是一件好事。可能他们的一个或两个解决方案将被证明对某些工作负载不可行,以至于只需要放弃它。不过,更有可能的是,这些开发人员最终将停止在彼此的补丁中挖洞,并一起找出如何将每个方面的最佳方面结合到一个所有人都能接受的工作解决方案中。似乎可以肯定的是,达到这一点可能需要一些时间。
以上内容翻译自文章:AutoNUMA: the other approach to NUMA scheduling
四、Chapter 9. NUMA(第9章)
从历史上看,所有 CPU 都可以平等地访问 AMD64 和 Intel 64 系统上的所有内存。称为统一内存访问 (UMA),无论哪个 CPU 执行操作,访问时间都是相同的。
最近的 AMD64 和 Intel 64 处理器不再有这种行为。在非统一内存访问 (NUMA) 中,系统内存在 NUMA节点之间划分,这些节点对应于sockerts或一组特定的 CPUs,这些 CPUs 对系统内存的local subset具有相同的访问延迟。
本章介绍虚拟化环境中的内存分配和 NUMA 调整配置。
9.1. NUMA Memory Allocation Policies
以下策略定义了如何从系统中的节点分配内存:
Strict: Strict policy意味着如果无法在目标节点上分配内存,分配将失败。指定 NUMA 节点集列表而不定义内存模式属性默认为strict模式。
Interleave: 内存页面在节点集指定的节点之间分配,但以循环方式分配。
Preferred: 内存是从单个首选内存节点分配的。如果没有足够的内存可用,可以从其他节点分配内存。
要启用预期策略,请将其设置为域 XML 文件元素的值:
<numatune>
<memory mode='preferred' nodeset='0'>
</numatune>
注意:如果在strict mode下内存过度使用,并且gust进程没有足够的交换空间,内核将终止一些来宾进程以获取额外的内存。Red Hat建议使用preferred分配并指定单个节点集(例如,nodeset=‘0’)来防止这种情况。
9.2. Automatic NUMA Balancing
Automatic NUMA Balancing提高了在 NUMA 硬件系统上运行的应用程序的性能。它在 Red Hat Enterprise Linux 7 系统上默认启用。
当其进程的线程访问与调度线程相同的 NUMA 节点上的内存时,应用程序通常会表现最佳。Automatic NUMA Balancing使任务(可以是线程或进程)更靠近它们正在访问的内存。它还将应用程序数据移动到内存中,使其更靠近引用它的任务。当Automatic NUMA Balancing处于活动状态时,这一切都由内核自动完成。
Automatic NUMA Balancing使用许多算法和数据结构,只有当Automatic NUMA Balancing在系统上处于活动状态时,它们才会被激活和分配:
- 进程内存的周期性 NUMA 取消映射
- NUMA 提示故障
- Migrate-on-Fault (MoF) - 将内存移动到使用它的程序运行的位置
- task_numa_placement - 将正在运行的程序移近它们的内存
9.2.1. Configuring Automatic NUMA Balancing
自动 NUMA 平衡在 Red Hat Enterprise Linux 7 中默认启用,并在具有 NUMA 属性的硬件上启动时自动激活。
当满足以下两个条件时,将启用自动 NUMA 平衡:
- # numactl --hardware显示多个节点
- # cat /proc/sys/kernel/numa_balancing
节目
1
应用程序的手动 NUMA 调整将覆盖Automatic NUMA Balancing,禁用内存的定期取消映射、NUMA 故障、迁移以及这些应用程序的自动 NUMA 放置。
在某些情况下,首选系统范围内的手动 NUMA 调整。
要禁用自动 NUMA 平衡,请使用以下命令:
# echo 0 > /proc/sys/kernel/numa_balancing
要启用自动 NUMA 平衡,请使用以下命令:
# echo 1 > /proc/sys/kernel/numa_balancing
9.3. libvirt NUMA Tuning
通常,NUMA 系统的最佳性能是通过将guest size大小限制为单个 NUMA 节点上的资源量来实现的。避免跨 NUMA 节点不必要地拆分资源。
使用该numastat
工具查看进程和操作系统的每个 NUMA 节点内存统计信息。
在以下示例中,该numastat
工具显示了四个虚拟机在 NUMA 节点上的内存对齐不理想:
# numastat -c qemu-kvm
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Node 2 Node 3 Node 4 Node 5 Node 6 Node 7 Total
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
51722 (qemu-kvm) 68 16 357 6936 2 3 147 598 8128
51747 (qemu-kvm) 245 11 5 18 5172 2532 1 92 8076
53736 (qemu-kvm) 62 432 1661 506 4851 136 22 445 8116
53773 (qemu-kvm) 1393 3 1 2 12 0 0 6702 8114
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
Total 1769 463 2024 7462 10037 2672 169 7837 32434
运行numad
以自动对齐来宾的 CPU 和内存资源。
然后再次运行numastat -c qemu-kvm
查看运行结果numad
。以下输出显示资源已对齐:
# numastat -c qemu-kvm
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Node 2 Node 3 Node 4 Node 5 Node 6 Node 7 Total
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
51747 (qemu-kvm) 0 0 7 0 8072 0 1 0 8080
53736 (qemu-kvm) 0 0 7 0 0 0 8113 0 8120
53773 (qemu-kvm) 0 0 7 0 0 0 1 8110 8118
59065 (qemu-kvm) 0 0 8050 0 0 0 0 0 8051
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
Total 0 0 8072 0 8072 0 8114 8110 32368
笔记
运行
numastat
with-c
提供紧凑的输出;添加该-m
选项会在每个节点的基础上向输出添加系统范围的内存信息。numastat
有关更多信息, 请参见手册页。
9.3.1. Monitoring Memory per host NUMA Node(监控每个主机NUMA节点的内存)
您可以使用该nodestats.py
脚本报告主机上每个 NUMA 节点的总内存和可用内存。该脚本还报告每个运行域的特定主机节点严格绑定了多少内存。例如:
# /usr/share/doc/libvirt-python-2.0.0/examples/nodestats.py
NUMA stats
NUMA nodes: 0 1 2 3
MemTotal: 3950 3967 3937 3943
MemFree: 66 56 42 41
Domain 'rhel7-0':
Overall memory: 1536 MiB
Domain 'rhel7-1':
Overall memory: 2048 MiB
Domain 'rhel6':
Overall memory: 1024 MiB nodes 0-1
Node 0: 1024 MiB nodes 0-1
Domain 'rhel7-2':
Overall memory: 4096 MiB nodes 0-3
Node 0: 1024 MiB nodes 0
Node 1: 1024 MiB nodes 1
Node 2: 1024 MiB nodes 2
Node 3: 1024 MiB nodes 3
这个示例显示了四个host NUMA节点,每个节点总共包含大约4GB的RAM (MemTotal)。几乎所有内存都消耗在每个domain上(MemFree)。有四个domains(虚拟机)正在运行: domain ‘rhel7-0’ 有1.5GB内存,没有固定在任何特定的主机NUMA节点上。然而,domain 'rhel7-2’有4GB内存和4个NUMA节点,这些NUMA节点与主机节点1:1固定。
要打印主机 NUMA 节点统计信息,请nodestats.py
为您的环境创建一个脚本。示例脚本可以在libvirt-python包文件中找到。可以使用命令 /usr/share/doc/libvirt-python-*version*/examples/nodestats.py``rpm -ql libvirt-python
显示脚本的具体路径。
9.3.2. NUMA vCPU Pinning(NUMA vCPU固定)
vCPU 固定提供与裸机系统上的任务固定类似的优势。由于 vCPU 在主机操作系统上作为用户空间任务运行,因此固定可提高缓存效率。其中一个示例是所有 vCPU 线程都在同一物理插槽上运行的环境,因此共享一个 L3 缓存域。
笔记
在 Red Hat Enterprise Linux 版本 7.0 到 7.2 中,只能固定活动 vCPU。但是,在 Red Hat Enterprise Linux 7.3 中,固定不活动的 vCPU 也可用。
结合使用 vCPU 固定numatune
可以避免 NUMA 未命中。NUMA 未命中对性能的影响很大,通常从 10% 或更高的性能下降开始。vCPU pinning 和 numatune
应该一起配置。
如果虚拟机正在执行存储或网络 I/O 任务,则将所有 vCPU 和内存固定到物理连接到 I/O 适配器的同一物理socket可能是有益的。
笔记
lstopo 工具可用于可视化 NUMA 拓扑。它还可以帮助验证 vCPU 是否绑定到同一物理插槽上的内核。有关lstopo的更多信息,请参阅以下知识库文章:https://access.redhat.com/site/solutions/62879。
重要的
在 vCPU 多于物理内核的情况下,固定会导致复杂性增加。
下面的示例XML配置将一个domain进程固定在物理cpu 0-7上。vCPU 线程固定到它自己的 cpuset。例如,vCPU0 固定到物理 CPU 0,vCPU1 固定到物理 CPU 1,依此类推:
<vcpu cpuset='0-7'>8</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='0'/>
<vcpupin vcpu='1' cpuset='1'/>
<vcpupin vcpu='2' cpuset='2'/>
<vcpupin vcpu='3' cpuset='3'/>
<vcpupin vcpu='4' cpuset='4'/>
<vcpupin vcpu='5' cpuset='5'/>
<vcpupin vcpu='6' cpuset='6'/>
<vcpupin vcpu='7' cpuset='7'/>
</cputune>
vcpu 和 vcpupin tags之间存在直接关系。如果未指定 vcpupin 选项,则该值将自动确定并从父 vcpu 标记选项继承。以下配置显示<vcpupin>
缺少vcpu 5。因此,vCPU5将固定到物理 CPU 0-7,如父标签中指定的那样<vcpu>
:
<vcpu cpuset='0-7'>8</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='0'/>
<vcpupin vcpu='1' cpuset='1'/>
<vcpupin vcpu='2' cpuset='2'/>
<vcpupin vcpu='3' cpuset='3'/>
<vcpupin vcpu='4' cpuset='4'/>
<vcpupin vcpu='6' cpuset='6'/>
<vcpupin vcpu='7' cpuset='7'/>
</cputune>
重要的
<vcpupin>
、<numatune>
和<emulatorpin>
应该一起配置以实现最佳的确定性性能。有关<numatune>
标签的更多信息,请参阅第 9.3.3 节,“域进程”。有关<emulatorpin>
标签的更多信息,请参阅第 9.3.6 节,“使用 emulatorpin”。
9.3.3. Domain Processes(域进程)
正如 Red Hat Enterprise Linux 中提供的那样,libvirt 使用 libnuma 为 domain 进程设置内存绑定策略。这些策略的节点集可以配置为静态(在domain XML 中指定)或自动(通过查询 numad 配置)。有关如何在<numatune>
标记中配置这些参数的示例,请参阅下面的XML配置
<numatune>
<memory mode='strict' placement='auto'/>
</numatune>
<numatune>
<memory mode='strict' nodeset='0,2-3'/>
</numatune>
libvirt 使用**sched_setaffinity(2)*为域进程设置 CPU 绑定策略。cpuset 选项可以是静态的(在域 XML 中指定)或*自动的(通过查询 numad 配置)。有关如何在标记内配置这些内容的示例,请参阅以下 XML 配置<vcpu>
:
<vcpu placement='auto'>8</vcpu>
<vcpu placement='static' cpuset='0-10,ˆ5'>8</vcpu>
在<vcpu>
和<numatune>
的放置模式之间存在隐式继承规则:
<numatune>
的放置模式默认为<vcpu>
的相同放置模式,如果指定了<nodeset>
则为静态。<nodeset>
- 同样,
<vcpu>
的放置模式默认为<numatune>
的相同放置模式,如果指定了<cpuset>
,则为静态放置模式。
这意味着可以分别指定和定义domain进程的CPU调优和内存调优,但也可以将它们配置为依赖于其他进程的放置模式。
也可以使用 numad 配置系统以引导选定数量的 vCPU,而无需在启动时固定所有 vCPU。
例如,要在具有 32 个 vCPU 的系统上启动时仅启用 8 个 vCPU,请配置类似于以下内容的 XML:
<vcpu placement='auto' current='8'>32</vcpu>
笔记
有关 vcpu 和 numatune 的更多信息,请参见以下 URL:http://libvirt.org/formatdomain.html#elementsCPUAllocation和http://libvirt.org/formatdomain.html#elementsNUMATuning
9.3.4. Domain vCPU Threads (域 vCPU 线程)
除了调整域进程之外,libvirt 还允许在 XML 配置中为每个 vcpu 线程设置固定策略。为<cputune>
标签内的每个 vcpu 线程设置固定策略:
<cputune>
<vcpupin vcpu="0" cpuset="1-4,ˆ2"/>
<vcpupin vcpu="1" cpuset="0,1"/>
<vcpupin vcpu="2" cpuset="2,3"/>
<vcpupin vcpu="3" cpuset="0,4"/>
</cputune>
在此标记中,libvirt 使用 cgroup 或**sched_setaffinity(2)**将 vcpu 线程固定到指定的 cpuset。
笔记
有关 的更多详细信息
<cputune>
,请参见以下 URL:http 😕/libvirt.org/formatdomain.html#elementsCPUTuning
此外,如果您需要设置 vCPU 多于单个 NUMA 节点的虚拟机,请配置主机,以便guest检测主机上的 NUMA 拓扑。这允许 CPU、内存和 NUMA 节点的 1:1 映射。例如,这可以应用于具有 4 个 vCPU 和 6 GB 内存的guest以及具有以下 NUMA 设置的主机:
4 available nodes (0-3)
Node 0: CPUs 0 4, size 4000 MiB
Node 1: CPUs 1 5, size 3999 MiB
Node 2: CPUs 2 6, size 4001 MiB
Node 3: CPUs 0 4, size 4005 MiB
在这种情况下,使用以下域 XML 设置:
<cputune>
<vcpupin vcpu="0" cpuset="1"/>
<vcpupin vcpu="1" cpuset="5"/>
<vcpupin vcpu="2" cpuset="2"/>
<vcpupin vcpu="3" cpuset="6"/>
</cputune>
<numatune>
<memory mode="strict" nodeset="1-2"/>
</numatune>
<cpu>
<numa>
<cell id="0" cpus="0-1" memory="3" unit="GiB"/>
<cell id="1" cpus="2-3" memory="3" unit="GiB"/>
</numa>
</cpu>
9.3.5. Using Cache Allocation Technology to Improve Performance(使用缓存分配技术提高性能)
您可以在特定 CPU 型号上使用内核提供的缓存分配技术 (Cache Allocation Technology, CAT)。这可以为 vCPU 线程分配主机 CPU 的部分缓存,从而提高实时性能。
关于如何在 cachetune
标签中配置vCPU缓存分配的示例,请参阅下面的XML配置:
<domain>
<cputune>
<cachetune vcpus='0-1'>
<cache id='0' level='3' type='code' size='3' unit='MiB'/>
<cache id='0' level='3' type='data' size='3' unit='MiB'/>
</cachetune>
</cputune>
</domain>
上面的 XML 文件将 vCPU 0 和 1 的线程配置为从第一个 L3 缓存(level=‘3’ id=‘0’)分配 3 MiB,一次用于 L3CODE,一次用于 L3DATA。
笔记
一个虚拟机可以有多个
<cachetune>
元素。
有关详细信息,请参阅cachetune
上游libvirt
文档。
9.3.6. Using emulatorpin(使用emulatorpin)
调整域进程固定策略的另一种方法是使用<cputune>
中的<emulatorpin>
标记.
<emulatorpin>
标记指定模拟器(域的一个子集,不包括vcpu)将固定到哪个主机物理cpu上。<emulatorpin>
标记提供了一种设置与模拟器线程进程精确关联的方法。因此,vhost线程在相同的物理cpu和内存子集上运行,从而受益于缓存局部性。例如:
<cputune>
<emulatorpin cpuset="1-3"/>
</cputune>
笔记
在 Red Hat Enterprise Linux 7 中,默认启用自动 NUMA 平衡。自动 NUMA 平衡减少了手动调整的需要
<emulatorpin>
,因为 vhost-net 模拟器线程更可靠地遵循 vCPU 任务。有关自动 NUMA 平衡的更多信息,请参阅第 9.2 节,“自动 NUMA 平衡”。
9.3.7. Tuning vCPU Pinning with virsh(使用 virsh 调整 vCPU 固定)
重要的
这些只是示例命令。您将需要根据您的环境替换值。
下面的 virsh
命令将把ID为1的vcpu线程rhel7固定到物理CPU 2上:
% virsh vcpupin rhel7 1 2
也可以通过 virsh
命令获取当前vcpu的绑定配置。例如:
% virsh vcpupin rhel7
9.3.8. Tuning Domain Process CPU Pinning with virsh(使用 virsh 调整域进程 CPU 固定)
重要的
这些只是示例命令。您将需要根据您的环境替换值。
该emulatorpin
选项将 CPU 关联设置应用于与每个域进程关联的线程。对于完整的固定,您必须同为每个guest时使用virsh vcpupin
(如前所示)和virsh emulatorpin
。例如:
% virsh emulatorpin rhel7 3-4
9.3.9. Tuning Domain Process Memory Policy with virsh( 使用 virsh 调整域进程内存策略)
可以动态调整域进程内存。请参阅以下示例命令:
% virsh numatune rhel7 --nodeset 0-10
这些命令的更多示例可以在手册页中找到virsh
。
9.3.10. Guest NUMA Topology(来宾 NUMA 拓扑)
可以使用来宾虚拟机XML中的 <cpu>
标记中的 <numa>
标记指定guest NUMA拓扑。参考以下示例,并相应地替换值:
<cpu>
...
<numa>
<cell cpus='0-3' memory='512000'/>
<cell cpus='4-7' memory='512000'/>
</numa>
...
</cpu>
每个<cell>
元素指定一个 NUMA 单元或一个 NUMA 节点。cpus
指定属于节点的 CPU 或 CPU 范围,memory
以kibibytes(1024 字节的块)为单位指定节点内存。每个单元格或节点按从0开始递增的顺序分配一个 cellid
或 nodeid
。
重要的
在使用已配置的 CPU sockets、内核和线程拓扑修改guest虚拟机的 NUMA 拓扑时,请确保将属于单个socket的内核和线程分配给同一 NUMA 节点。如果来自同一socket的线程或内核分配给不同的 NUMA 节点,guest可能无法启动。
警告
Red Hat Enterprise Linux 7不支持在大页面上同时使用guestnuma拓扑,只有在Red Hat虚拟化或Red Hat OpenStack平台等分层产品中才可用。
9.3.11. NUMA Node Locality for PCI Devices
在启动一个新的虚拟机时,了解主机NUMA拓扑结构和与NUMA节点的PCI设备从属关系非常重要,这样当请求PCI直通时,guest将固定在正确的NUMA节点上,以获得最佳的内存性能。
例如,如果一个客户机固定到NUMA节点0-1,但它的一个PCI设备关联到节点2,则节点之间的数据传输将花费一些时间。
在Red Hat Enterprise Linux 7.1及以上版本中,libvirt在guest XML中报告PCI设备的NUMA节点位置,使管理应用程序能够做出更好的性能决策。
该信息在 /sys/devices/pci*/*/numa_node的sysfs
文件中可见。验证这些设置的一种方法是使用lstopo工具报告 sysfs
数据:
# lstopo-no-graphics
Machine (126GB)
NUMANode L#0 (P#0 63GB)
Socket L#0 + L3 L#0 (20MB)
L2 L#0 (256KB) + L1d L#0 (32KB) + L1i L#0 (32KB) + Core L#0 + PU L#0 (P#0)
L2 L#1 (256KB) + L1d L#1 (32KB) + L1i L#1 (32KB) + Core L#1 + PU L#1 (P#2)
L2 L#2 (256KB) + L1d L#2 (32KB) + L1i L#2 (32KB) + Core L#2 + PU L#2 (P#4)
L2 L#3 (256KB) + L1d L#3 (32KB) + L1i L#3 (32KB) + Core L#3 + PU L#3 (P#6)
L2 L#4 (256KB) + L1d L#4 (32KB) + L1i L#4 (32KB) + Core L#4 + PU L#4 (P#8)
L2 L#5 (256KB) + L1d L#5 (32KB) + L1i L#5 (32KB) + Core L#5 + PU L#5 (P#10)
L2 L#6 (256KB) + L1d L#6 (32KB) + L1i L#6 (32KB) + Core L#6 + PU L#6 (P#12)
L2 L#7 (256KB) + L1d L#7 (32KB) + L1i L#7 (32KB) + Core L#7 + PU L#7 (P#14)
HostBridge L#0
PCIBridge
PCI 8086:1521
Net L#0 "em1"
PCI 8086:1521
Net L#1 "em2"
PCI 8086:1521
Net L#2 "em3"
PCI 8086:1521
Net L#3 "em4"
PCIBridge
PCI 1000:005b
Block L#4 "sda"
Block L#5 "sdb"
Block L#6 "sdc"
Block L#7 "sdd"
PCIBridge
PCI 8086:154d
Net L#8 "p3p1"
PCI 8086:154d
Net L#9 "p3p2"
PCIBridge
PCIBridge
PCIBridge
PCIBridge
PCI 102b:0534
GPU L#10 "card0"
GPU L#11 "controlD64"
PCI 8086:1d02
NUMANode L#1 (P#1 63GB)
Socket L#1 + L3 L#1 (20MB)
L2 L#8 (256KB) + L1d L#8 (32KB) + L1i L#8 (32KB) + Core L#8 + PU L#8 (P#1)
L2 L#9 (256KB) + L1d L#9 (32KB) + L1i L#9 (32KB) + Core L#9 + PU L#9 (P#3)
L2 L#10 (256KB) + L1d L#10 (32KB) + L1i L#10 (32KB) + Core L#10 + PU L#10 (P#5)
L2 L#11 (256KB) + L1d L#11 (32KB) + L1i L#11 (32KB) + Core L#11 + PU L#11 (P#7)
L2 L#12 (256KB) + L1d L#12 (32KB) + L1i L#12 (32KB) + Core L#12 + PU L#12 (P#9)
L2 L#13 (256KB) + L1d L#13 (32KB) + L1i L#13 (32KB) + Core L#13 + PU L#13 (P#11)
L2 L#14 (256KB) + L1d L#14 (32KB) + L1i L#14 (32KB) + Core L#14 + PU L#14 (P#13)
L2 L#15 (256KB) + L1d L#15 (32KB) + L1i L#15 (32KB) + Core L#15 + PU L#15 (P#15)
HostBridge L#8
PCIBridge
PCI 1924:0903
Net L#12 "p1p1"
PCI 1924:0903
Net L#13 "p1p2"
PCIBridge
PCI 15b3:1003
Net L#14 "ib0"
Net L#15 "ib1"
OpenFabrics L#16 "mlx4_0"
此输出显示:
- NIC
em*
和 diskssd*
连接到 NUMA 节点 0 和核心 0、2、4、6、8、10、12、14。 - NIC
p1*
并ib*
连接到 NUMA 节点 1 和核心 1、3、5、7、9、11、13、15。
9.4. NUMA-Aware Kernel SamePage Merging (KSM)(NUMA 感知内核 SamePage 合并 (KSM))
Kernel SamePage Merging (KSM) 允许虚拟机共享相同的内存页面。KSM 可以检测到系统正在使用 NUMA 内存并控制跨不同 NUMA 节点的页面合并。
使用该sysfs /sys/kernel/mm/ksm/merge_across_nodes
参数来控制跨不同 NUMA 节点的页面合并。默认情况下,来自所有节点的页面可以合并在一起。当此参数设置为零时,仅合并来自同一节点的页面。
通常,除非您超额订阅了系统内存,否则您将通过禁用 KSM 共享获得更好的运行时性能。
重要的
当 KSM 在 NUMA 主机上跨节点与多个guest虚拟机合并时,来自更远节点的guest和 CPU 对合并后的 KSM 页面的访问延迟可能会显着增加。
要指示管理程序禁用 guest 的共享页面,请将以下内容添加到来宾的 XML 中:
<memoryBacking>
<nosharepages/>
</memoryBacking>
有关使用<memoryBacking>
元素调整内存设置的更多信息,请参阅第 8.2.2 节 “使用 virsh 调整内存”。
通常,除非您超额订阅了系统内存,否则您将通过禁用 KSM 共享获得更好的运行时性能。
重要的
当 KSM 在 NUMA 主机上跨节点与多个guest虚拟机合并时,来自更远节点的guest和 CPU 对合并后的 KSM 页面的访问延迟可能会显着增加。
要指示管理程序禁用 guest 的共享页面,请将以下内容添加到来宾的 XML 中:
<memoryBacking>
<nosharepages/>
</memoryBacking>
有关使用<memoryBacking>
元素调整内存设置的更多信息,请参阅第 8.2.2 节 “使用 virsh 调整内存”。
以上内容翻译自Chapter 9. NUMA