numatune 是什么
numatune 是 libvirt 的一个参数, 可以用在 numa 架构的 host 上, 以控制子机的内存访问策略.
使用方法如下, 参考 libvirt 文档 https://libvirt.org/formatdomain.html
- <domain>
- ...
- <numatune>
- <memory mode="strict" nodeset="1-4,^3" />
- <memnode cellid="0" mode="strict" nodeset="1" />
- <memnode cellid="2" mode="preferred" nodeset="2" />
- </numatune>
- ...
- </domain>
numatune 的 mode 选项有:
strict: 默认的策略, 如果值指定的 node 上无法分配内存, 则虚拟机分配内存失败, 即无法启动
interleave: 通过轮询方式, 在指定的多个 node 上分配内存
preferred: 优先在指定 node 上分配内存, 如果内存不足, 允许在其他 node 上分配内存
在了解了 numatune 之后, 接下来我们将讨论 numatune 的影响以及哪些场景需要使用 numatune
numatune 对性能有多大影响
从 libvirt 和 RedHat 的文档看, numatune 会对虚拟机的性能大概会有 10% 或者更高的影响, 为了评估 numatune 的影响, 我们进行了详细的测试.
RedHat 文档
Combining vCPU pinning with numatune can avoid NUMA misses. The performance impacts of NUMA misses are significant, generally starting at a 10% performance hit or higher. vCPU pinning and numatune should be configured together.
测试环境
虚拟机配置:
CPU:32c
内存: 128GB
网络方式: DPDK
虚拟机 CPU 分为两个 node, 并且分别绑定在 host 的两个 node 上
- <cputune>
- <vcpupin vcpu="0" cpuset="1-22,49-70" />
- ......
- <vcpupin vcpu="26" cpuset="25-46,73-94" />
- </cputune>
物理机配置:
CPU:96c
内存: 256GB
使用 hugetlbfs, 以实现子机 DPDK
虚拟化方式: kvm(内核版本 3.10.0-693)+qemu(2.6.0)
测试用例
CPU 性能测试: speccpu2017 unixbench linpack
内存性能测试: stream mlc
测试结论
speccpu 测得 fpspeed 和 fprate 分别有 12% 和 25% 的提升
stream 测得内存带宽有 78% 以上的提升
性能对比. PNG
从测试结果看, numatune 对虚拟机的性能有非常大的影响, 接下来我们分析一下为什么会有这么大的影响.
分析
对内存带宽有如此大的影响, 猜测应该是出现跨 numa 访问导致, 用 https://github.com/intel/intel-cmt-cat 看下内存的分布情况.
numa miss.PNG
从上图可以看到, 在 stream 测试过程中, 子机 CPU 出现了访问 remote memory, 即子机的 CPU node0 绑定在 host 的 node0 上, 但是访问的内存却是在 host 的 numa1 上. 因为内存的跨 numa 访问, 导致 CPU 性能有大幅下降, 就很容易理解了.
为什么会出现跨 numa 访问
从 qemu 的源码看, 主要完成子机内存绑定的代码位于 hostmem.c 的 host_memory_backend_memory_complete 函数.
- if (mbind(ptr, sz, backend->policy,
- maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) {
- if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) {
- error_setg_errno(errp, errno,
- "cannot bind memory to host NUMA nodes");
- return;
- }
- }#endif
- /* Preallocate memory after the NUMA policy has been instantiated.
- * This is necessary to guarantee memory is allocated with
- * specified NUMA policy in place.
- */
- if (backend->prealloc) {
- os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
- smp_cpus, &local_err);
- if (local_err) {
- goto out;
- }
- }
mbind 函数首先设置 NUMA 内存的访问策略, 接着调用 prealloc 进行内存预分配.
如果没有设置 numatune,backend->policy 为默认值 MPOL_DEFAULT, 表示在运行当前分配逻辑 CPU node 对应的 NUMA 上分配内存, 即如果当前程序运行的 node1 上, 那子机所有的 node 都会在 numa1 上分配内存.
- The system-wide default policy
- allocates pages on the node of the CPU that triggers the
- allocation. For MPOL_DEFAULT, the nodemask and maxnode
arguments must be specify the empty set of nodes.
如果设置了 numatune, 看到 qemu 的参数会多出来 host-nodes=1,policy=preferred, 执行 backend->policy 为 MPOL_PREFERRED, 会优先从 host_nodes 上分配内存.
numatune.PNG
接下来的 prealloc() 函数就是按照上面的设置的 numa 策略, 进行预分配.
从上文可以看到, 在使用 hugetlbfs 进行 prealloc() 内存的情况下, numatune 可以实现 numa 透传的目的.
哪些场景需要添加 numatune
如上文介绍, 如果使用 hugetlbfs, 对虚拟机进行内存预分配的情况需要使用 numatune
没有使用内存预分配, 但使用 vfio + 透明巨页, 同样可能出现内存需要使用 numatune
vfio 的流程中也会去设置内存亲和性, 如果没有 numatune, 同样会出现跨 numa 访问的问题, 后续会有更详细的分析
其他情况下, 加 numatune 效果不明显
其他情况下, 虽然有 mbind 操作, 但因为没有预分配, 最终分配内存时仍然优先在最近的 numa 分配内存
本文特别致谢 mungerjiang
来源: https://www.qcloud.com/developer/article/1452341