
在Linux 6.17合并窗口期间引入了一项面向ARM64架构的优化,旨在将函数调用次数缩减至原有的1/16。遗憾的是,该补丁最终在某些系统上引发了颇为显著的性能倒退问题,现已得到修复。
上周,英特尔内核自动化测试框架在stress-ng内核微基准测试中检测到一项性能下降37%的回归问题。来自Oracle的工程师Lorenzo Stoakes成功复现该问题,并在英特尔Raptor Lake平台上观察到Linux 6.17 Git内核版本存在43%的性能衰退。
Stoakes 锁定该问题并成功提交修复方案,该方案现已合并至 Linux Git 主分支,用于避免在 mremap 的 folios PTE 批处理过程中进行高开销的 folios 查找操作。Stoakes 总结道:
“所附报告发现,提交记录 f822a9a81a31(”mm:通过 PTE 批处理优化 mremap()”)在 x86-64 架构的多个性能指标中引发了显著倒退,其中 stress-ng.bigheap.realloc_calls_per_sec 指标尤为突出——显示每秒 mremap() 调用次数出现 37.3% 的性能回退。:ml-citation{ref=”1,4″ data=”citationList”}。
在本地 Intel x86-64 Raptor Lake 处理器架构系统上成功复现此问题:应用补丁前每秒平均 realloc 调用次数为 143,857(标准差 4,531,占比 3.1%),应用后降至 81,503(标准差 2,131,占比 2.6%)——性能倒退达 43.3%。:ml-citation{ref=”3,4″ data=”citationList”}
测试过程中,我确认优化folio_pte_batch()操作与检查folio_test_large()均未产生显著性能差异。这符合预期,如此显著的性能倒退很可能表明我们正在访问尚未载入缓存行的内存(甚至可能触发主存访问)。
从一开始讨论时,参与者就预期
vm_normal_folio()(由mremap_folio_pte_batch()调用)很可能是性能问题的根源,因为它需要从 vmemmap 获取内存(而 mremap() 的页表移动操作通常不会涉及这种访问,这意味着这部分内存必然是冷内存)。我能够明确地确定这个理论确实是正确的,也是问题的原因。
解决方案是恢复以前在审查时被丢弃的方法的一部分,即调用 pte_batch_hint(),它通过单独引用 PTE(因此没有 vmemmap 查找)显式确定 PTE 批量大小可能是多少。
在非 arm64 架构平台上,当前实现中该函数被硬编码为返回 1,这自然解决了 x86-64 架构的问题;而对于 arm64 架构,由于 PTE 缓存行将处于热状态(hot),该设计几乎不会引入额外开销。
应用此补丁后,我们从 81,503 次 realloc 调用/秒增加到 138,701 次(stddev 为 496.1 或 0.4%),这是一个 -3.6% 的倒退,但是考虑到原始结果的差异,这在很大程度上将性能恢复到之前的状态。
在Linux 6.17-rc1发布后仅数日,这一严重的性能倒退问题已得到控制,仅需两行补丁即可避免在不太可能带来益处的情况下执行开销高昂的 folio 查找操作。
我将观察该补丁能否改善在Linux 6.17初步测试阶段出现的混合基准测试结果,同时后续测试阶段将展开更多基准测试。
Linuxeden开源社区