LWN:让user space触发巨页融合!

用户空间触发巨页融合
本文介绍了一种新的机制,允许用户空间程序控制巨页(hugepage)的使用,通过调用madvise()请求来触发内存范围内的巨页融合。这一特性有助于提高应用程序性能,同时减少khugepaged内核线程的负担。

关注了就能看到更多这么棒的文章哦~

Triggering huge-page collapse from user space

By Jonathan Corbet
March 14, 2022
DeepL assisted translation
https://lwn.net/Articles/887753/

当内核首次开始支持巨页(huge page)的时候,这个方案把大部分的工作都留给了用户空间来做。系统管理员必须在一个专门的 hugetlbfs 文件系统中留出内存才能使用 huge page,而用户程序必须明确指定从那里来映射内存。随着时间的推移,新增的 transparent huge page 机制就开始把使用 huge page 的任务自动化了。但这一机制并不完美,一些用户认为他们自己才更加了解某一个进程是否应该使用 huge page。因此,随着 Zach O'Keefe 最近的 patch set 将 huge page 的控制重新交回到用户空间,现在 huge page 的机制又回来了。

huge page 是来自于 CPU 实现了更大的 page size。具体的 page size 取决于不同的处理器类型以及它的页表布局定义(page-table layout)。例如,一个 X86 处理器通常会支持 4KB 的 "base" page size,以及 2MB 和 1GB 的 huge page。huge page 省去了页表层次结构(page-table hierarchy)的最底层(或底层的多层),因此稍微加快了地址转换过程。不过,huge page 带来的性能优势主要还是来自于对处理器里数量稀少的 TLB (translation lookaside buffer)条目的压力。一个 2MB 的 huge page 需要一个 TLB 条目,而当该内存是映射成 base page 来访问的时候,则需要 512 个条目。对于某些类型应用来说,可以看到非常明显的速度提升,所以在可能的情况下,使用 huge page 是有价值的。

不过,使用 huge page 也是有成本的,首先就是来自它们的 huge 属性。进程并不总是需要那么大的、虚拟连续的内存区域,所以如果将所有的进程内存都放在 huge page 中,最终会浪费大量的内存。transparent huge page 机制就是试图通过扫描进程内存并找到 huge page 可能会有价值的地方,来找到一个平衡点。每次找到这样的位置时,base page 区域就被 "折叠(collapsed)" 成为一个 huge page,而其所属进程也不会意识到这里发生过变化。

不过,transparent huge page 也是有成本的。这个扫描本身就需要消耗 CPU 时间,所以 khugepaged 内核线程每秒可以扫描的内存数量是有限制可以设置的。通过施加限制来使 khugepaged 的开销保持在合理范围内,但也降低了 huge page 的使用效率,导致那些可以从中受益的进程就花了更多时间在更低效的模式下运行。

O'Keefe 的 patch set 背后的想法是允许用户空间在已知(或希望)使用 huge page 会有优势的地方,尽快触发 huge page 的融合(collapse)动作。这个想法最早是由 David Rientjes 在 2021 年初提出的,并最终由 O'Keefe 实现。O'Keefe 说,除了让 huge page 的融合能更早发生之外,这项工作还会让 huge page 进行融合所消耗的 CPU 时间也被记入提出请求的进程,更加公平合理。

它还允许进程来控制什么时候进行这项工作。存储在 base page 中的数据会分散在整个物理内存中各处,将这些 page 融合成一个 huge page,需要将数据复制到一个单独并且物理连续的 huge page 上去。因此,在复制过程中需要阻止对这些 page 的改动以及 CPU 时间消耗,因为这些都会导致延迟增加,所以在这段时间避免这些无关工作是有好处的。

一个进程可以通过一个新增的 madvise() 请求来请求一个内存范围的 huge page 进行融合:

int madvise(void *begin, size_t length, MADV_COLLAPSE);

这个调用试图把从 begin 开始的 length 那么多字节数的内存融合成 huge page。这些参数似乎没有什么对齐要求,尽管 huge page 确实有对齐要求。如果 begin 指向的这个 base page 是它所位于的 huge page 的中间,那么 begin 之前的那些 page 也会成为最终结果的一部分。换句话说,begin 将被向后对齐到包含 huge page 的正确起始地址。length 也是如此,如果有必要的话就会增加 length 来包含整个 huge page。

当然,我们不能保证这个调用可以成功创建出 huge page。这取决于很多因素,包括系统中是否有空闲的 huge page。哪怕操作成功了,极端情况下内核也可能在这个调用返回之前再次将 huge page 分割开来。如果能成功,就会拿到 0 返回值 0;否则将返回一个错误码。具体到缺乏 huge page 可用的情况下,将产生一个 EAGAIN 错误代码。

对 MADV_COLLAPSE 的支持也被添加到 process_madvise()中,允许一个进程在另一个进程中触发 huge page 融合。在这种情况下有几个 flag 可供使用(这些将是 process_madvise() 第一次真正开始使用 flags 参数):

  • MADV_F_COLLAPSE_LIMITS 控制着这个操作是否应该受到 khugepaged 所遵循的 huge page 融合限制条件的约束;这些限制是通过现有内核中的 sysctl 开关来设置的。如果调用进程缺乏 CAP_SYS_ADMIN 能力,那么就强制要使用这个 flag。可以说,需要一个明确 flag 来请求执行默认行为,这是有点奇怪的,但这就是现在的实现。

  • MADV_F_COLLAPSE_DEFRAG,如果使用这个 flag,就允许此操作强制 page compaction 来创建空闲的 huge page,哪怕系统配置不允许这样做。这个 flag 不需要任何额外的 capability 权限,也许是因为这个 compaction 的开销反正是由受影响的进程本身承担。

O'Keefe 说,最终得到了一种机制,允许用户空间来控制 huge page 的使用,也许就不再需要内核参与了:

虽然不需要说明这组 patch 的必要性,但 hugepage 的管理可以完全 offload 给一个充分了解情况的用户空间代理,从而取代内核中 khugepaged。

但是,首先,这项工作需要进入内核的 mainline。到目前为止,大多数 review 意见都集中在细节上,但 David Hildenbrand 确实对这个新操作的一个方面提出了异议。在当前版本的 patch 中,任何虚拟内存区域都会被创建 huge page,哪怕是那些被明确标记为不使用 huge page 的 madvise(MADV_NOHUGEPAGE)调用也一样。他说,这在 s390 架构上 "会严重破坏 KVM"。因此,这种行为需要改变。

目前的 patch set 只适用于匿名页,计划今后再添加对文件相关(file-backed)page 的支持。由于使用这个 patch 的理由之一是能够快速让可执行 text 段来使用 huge page,因此对 file-backed page 的支持似乎也很重要,开发者希望在批准合入这个工作之前先看到这一部分。不过,这个功能看起来对某些使用场景很有用,所以它应该迟早会进入 mainline。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

78017b3acd1e1d4317171d3642299b00.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值