LWN:针对透明巨页的shrinker!

这篇博客介绍了内核开发者Alexander Zhu提出的一组补丁,旨在解决透明大页面(transparent hugepage)内部碎片问题。当hugepage未被充分利用时,这些补丁能够检测并回收浪费的内存,提高内存管理效率。通过扫描匿名hugepage,确定其使用情况,并在内存紧张时主动拆分未充分利用的页面,从而释放资源。这一改进对于平衡性能提升与内存使用至关重要,有望使透明大页面成为默认启用的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

The transparent huge page shrinker

By Jonathan Corbet
September 8, 2022
DeepL assisted translation
https://lwn.net/Articles/906511/

huge page 是 CPU 实现的一种机制,允许以较大的粒度(chunk)来管理内存。使用 huge page 可以显著提高性能,这就是为什么内核有一个 "transparent huge page" 的机制,会在可能的情况下去创建 huge page。但是,只有当它所包含的大部分内存真正都被使用时,huge page 才是有价值的;否则它只会是对内存的巨大浪费。来自 Alexander Zhu 的这组 patch 实现了一个新机制,可以检测出未被充分利用的 huge page,并将浪费的内存找回来,供其他用途使用。

大多数运行 Linux 的系统的 base page size 是 4,096 字节,这个数字多年来一直保持不变,尽管这些系统中所拥有的内存数量在增长。典型情况下,可以将 512 个物理连续的 base page 组合成一个 huge page,就可能可以减少管理这些 page 的开销。但更重要的是,处理器中的 TLB(translation lookaside buffer)数量很少,TLB 是用于缓存虚拟地址到物理地址的转换结果的,使用 huge page 则可以占用更少的 TLB。TLB miss 如果发生了,性能损失会很大,所以如果能扩大 TLB 可以支撑的内存地址空间的话(就像 huge page 起到的效果)就可以显著提高性能。

huge page 通常来说 size 都更大,所以它的缺点就是容易有内部碎片(internal fragmentation)。如果一个 huge page 只有一部分被实际使用了,那么其余部分就是浪费的内存,不能给别人用了。由于这样的 page 中包含的实际在用的内存很少,那么我们希望达到的目标(TLB 带来的性能提升)就无法实现了。在最坏的情况下,我们完全应该将一个利用率很低的 huge page 分解成 base page,只保留那些明显在使用的 page。内核的内存管理子系统可以将 huge page 分割开来便于回收(reclaim),但是它无法找到哪些 huge page 是未被充分利用的。

Zhu 的 patch set 就是通过几个步骤来填补这一空白的。首先弄清系统中哪些 huge page 被充分利用了,哪些没有。因此需要启动一个扫描函数,每秒钟都从内核 workqueue 中醒来运行一次;每次运行时会检查多达 256 个 huge page,以确定每一个被利用的充分程度。只会扫描 anonymous huge page,也就是目前并不涉及 file-backed huge page。扫描结果可以从 /sys/kernel/debug/thp_utilization 中读出来,看起来像下面这样的表格:

Utilized[0-50]: 1331 680884
Utilized[51-101]: 9 3983
Utilized[102-152]: 3 1187
Utilized[153-203]: 0 0
Utilized[204-255]: 2 539
Utilized[256-306]: 5 1135
Utilized[307-357]: 1 192
Utilized[358-408]: 0 0
Utilized[409-459]: 1 57
Utilized[460-512]: 400 13
Last Scan Time: 223.98
Last Scan Duration: 70.65

这个例子(取自 patch 的说明邮件)是一个直方图(histogram),显示了包含特定数量的已在使用的 base page 的 huge page 的数量。例如,第一行显示了有多少个 huge page 仅使用了不超过 50 个 base page,这里有 1331 个这样的 huge page,其中包含 680,884 个未使用的 base page。这个结果有一个明显的特征:几乎所有的 page 都位于两个极端之上。一般来说,一个 huge page 要么被完全利用了,要么几乎完全没有利用起来。

在解释这些数据时,需要回答的一个重要问题是:代码是如何确定一个 huge page 中的哪些 base page 正在被实际使用中?CPU 和内存管理单元在这个任务中没有提供太多的帮助;如果内存被映射为一个 huge page,就不可能去检查其中每个 base page 的 "accessed" bit 了。因此需要使用 Zhu 的 patch set 来对这部分 memory 进行扫描,看看那里存储了什么。那些全 0 的 base page 都被认为是未使用的,而那些包含了非零数据的 base page 则被当作已在使用中。这显然不是一个完美的判断条件;毕竟一个程序可以用非零数据来初始化 page,然后再也不碰它们。但可能很难设计出一个更好的、不需要主动将 huge page 分解成 base page 的方法。

扫描的结果确定了系统中 huge page 中的一部分应该被拆分成更小的 base page。不过在目前的内核中,拆分一个由 0 填充的 huge page 会导致产生很多全是 0 的 base page,而且不能马上增加 unused memory 的数量。Zhu 的 patch set 改变了分割 huge page 的方式,使其直接丢弃那些全是 0 的 base page,而不是将其重新映射到进程的地址空间上。由于这些是匿名页,所以如果进程最终访问了其中一个被丢弃的 page,内核可以快速地替换一个新分配的、由 0 填充的 page 上来。

最后一步是在内核里寻找需要回收的内存时,主动拆分那些未被充分利用的 huge page。为此,scan 代码会把利用率最低的 page(如上图所示的 0-50 这一档里的 page)添加到一个新的 linked list 链表里,这样就可以快速找到它们。在内存管理子系统中注册了一个新的 shrinker (内存回收机制),会在内存紧张时调用。在被调用时,这个 shrinker 将从未被充分利用的 huge page 链表中取出一些,并将其分割,最终就可以把其中的所有全是 0 的 base page 返回给系统。

关于这组 patch 的大多数评论都集中在细节上,而不是它的整体方式。David Hildenbrand 表达了一个担忧,即在一个由 userfaultfd() handler 程序所管理的区域中,如果该 handler 程序后来收到它预料之外的 page fault,那么如果对全是 0 的 page 进行 unmap 可能会导致混乱。Zhu 回答说,如果这是一个问题,在 userfaultfd() 管理的区域中,可以把这些全是 0 的 base page 映射到内核那个共享的 zero page 上。

自从 transparent huge page 在 2011 年合入 2.6.38 内核后,kernel 就已经支持了这个功能,但目前仍未对所有进程启用这个功能。其中原因之一就是担忧内部碎片问题可能超过了透明巨页所提供的好处。Zhu 的目标很明确,就是希望让这个问题消失,从而允许默认启用透明巨页。如果这项工作获得成功,它可能代表了一个长期存在的内核功能的重要一步,可以把它的潜力完全发挥出来。

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

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

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

format,png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值