LWN:两组跟memory-tier有关的patch!

共 4354字,需浏览 9分钟

 ·

2022-07-07 21:35

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

Two memory-tiering patch sets

By Jonathan Corbet
June 27, 2022
DeepL assisted translation
https://lwn.net/Articles/898766/

很久以前,计算机只有一种类型的内存 memory,所以一个特定系统内的内存是可以互相替换使用的。非统一内存访问(NUMA)系统的出现使情况大大复杂化,因为有些内存的访问速度会比其他内存快,内存管理算法必须适应这一点,否则性能会受到影响。但 NUMA 只是一个开始;如今出现的分层内存系统(tiered-memory system),可能会包括具有不同性能特性的多层内存,这正在给我们增加新的挑战。目前正在审查的几个相关 patch set 有助于说明这里有哪些类型的问题需要我们解决。

NUMA 系统的核心挑战是要确保内存能要能根据后面需要在哪里使用,而决定从哪个 node 上来分配。一个进程如果能主要在本地节点(local node)的内存上运行,就会比使用大量远程内存的进程表现得更好。因此,为某个特定 page 来找到合适的分配来源是一个一次性的任务;一旦这个 page 和它的用户都进入了同一个 NUMA 节点,问题就解决了,剩下的唯一问题就是避免再次将它们分开。

Tiered memory (分层内存)是建立在 NUMA 概念之上的,但有一些区别。可能会有一段内存区域是作为一个没有 CPU 的 NUMA 节点来管理的,因此该内存将不会被视为系统中任何进程的本地内存(local memory)。一般来说,这些无 CPU 的 node 上的内存要比正常的系统 DRAM 慢。例如它可能是一个 size 巨大的 persistent memory。但情况并不一定如此,我们将在下面看到。

由于无 CPU 节点上的内存不是任何进程的本地内存,那么必须有一些其他的标准用来决定何时从那里分配内存。目前采取的方法是使用内核的正常回收机制来将页面从较快的 DRAM 降级到这样的 node 之上;在一个 page 本来会被驱逐(evicted)或推到交换区(pushed to swap)的情况下,可以改为移到较慢的内存上。这样就可以利用这种较慢的内存区域了,并且也同时保持该 page 是可用的,如果它仍然是有被用到的话就有价值了。最终,如果该 page 待在这个慢速的 memory 中一直未被使用,那么就可以 push 到一个更慢的 tier,或完全被 evict 出去。

不过,将 page 降级慢速 tier 不能是一个单向的操作,否则性能会受到影响;毕竟其中一些 page 最终会被频繁访问,如果将它们保留在慢速内存中会降低运行速度。因此,需要有一种机制来促进 page 回到更快的内存中去。在降级后的第一次访问到的时候将 page 直接移回快速内存是一种可能的方法,但这也会导致一些不是经常使用的内存就也会被提级,还有可能会在 tier 之间产生大量的 page 移动动作,这本身就有很大的开销。这里需要一个更好的解决方案。

Hot-page selection

Huang Ying 的 hot-page selection patch set 就是在试图找到更好的解决方案。这里所采取的方法是试图去估计每个 slow-tier page 的访问频率,并把那些最经常被访问的 page 提到更快的一个 tier。不过,由于没有 page 的访问 counter 数据,所以需要某种启发式的方法。具体方法是偶尔扫描一下 slow-tier 内存,将各个 page protection 设置为 PROT_NONE(无访问)。这样做了之后再把当前时间填到相关的 page structure 里面。后续在试图访问该 page 的时候会产生一个 fault,这时可以恢复以前的权限,从而让 fault 了的进程能继续正常运行。但是,内核也可以将当前时间与之前存储的时间戳进行比较;如果这个时间非常短,那么结论就是这个 page 会被频繁访问,应该被提级。

什么是 "足够短" 呢?该系列的第一个 patch 将阈值设置为一秒,这个值据说 "在我们的性能测试中很有效"。该 patch 也指出了这种方法的一些缺点;正确的阈值会是高度依赖于工作场景的,而且提升 tier 的机制在访问模式发生变化的时候响应会比较缓慢。如果一组在慢速内存中放置了一段时间的 page 突然变得很抢手,那么第一次访问仍然会显得很慢,而且这些 page 在被提升 tier 之前必须要再经过一次扫描周期。

为了缓解这最后一个问题,如果系统中有大量的空闲快速内存的话,访问时间阈值将不会被用到。换句话说,只要有可用资源,那么哪怕 page 看起来不常被使用,也会简单地被提升 tier。不过在不满足这种情况的时候,仍然会使用访问时间来检查,这里一秒钟的设置可能并不适合所有的工作场景。Ying 指出,似乎没有办法让用户自己尝试以合理的方式来配置这个值,所以没有提供做这种设置的配置项。

相反,该系列增加了一个配置项来限制每秒执行的 page promote 动作的数量,以 MB/s 带宽值表示。一旦在某个时间段内达到了此速率限制,page promote 这种提级动作将暂时停止,防止过多的 page promote 导致系统不堪重负从而损害其本身的性能。最后一步是动态调整这个 access-time 阈值,使符合 promote 条件的 page 数量与配置的 promotion limit 大致相符。因此,如果有太多的 page 被提级的时候,阈值将变得更严格,使算法能集中在最频繁访问的 page 上。

与 patch set 一起提供的性能测试结果显示新算法有一些明显的性能改进。不过,在回复之前发布的 patch set 时,Johannes Weiner 提出了一个更简单的方法,显然 Meta 也在使用这个方法。它使用了目前一般用来管理 memory eviction 的最小最近使用(LRU)机制,从而实现的效果是是 page 在第二次访问时将被提级。Ying 回答说,LRU 对识别 cold page 很有帮助,但对识别 hot page 则不太有效。Weiner 没有进一步回应。目前来看这组 patch set 的未来还不是非常清晰,但它确实提供了一个解决方案,看起来 mainline kernel 里面应该迟早会需要类似的功能。

Rethinking tier assignment

在支持 tier memory 的早期,将无 CPU 的 NUMA 节点分配到较慢的内存层是一种事实上很有效的启发式方法。但是,正如 Aneesh Kumar K.V 在其 patch set 中指出的,现实世界总是比这更复杂的。一个没有 CPU 的节点可能被填充了较慢的内存,但它也可能持有和正常系统 DRAM 一样快速的内存,甚至比它更快。这方面的例子有很多,比如底层是虚拟机中的 DRAM 的虚拟 node、快速互连(fast interconnect)后面的 CXL 内存,或者由专门设备提供的高带宽内存。将这种内存视为较慢的内存的话会剥夺系统的优势。文中还指出,CPU hot-add event 可能会导致一个没有 CPU 的 node 后续会带有 CPU,并被移到另一个 tier,即使该节点的内存特性没有发生改变。

他建议的解决方案是用更复杂且明确的方案来取代内核简单的 tier 配置。目前的内核并没有如此来确定 "tier";相反,它们把 node 设置一个内部维护的 "降级顺序(demotion order)",也就是根据 node distance 得到的一个函数。这个 order 不容易在用户空间看到,也不能修改。patch set 把 tier 变成了一个相应的的内核对象,并允许创建任意数量的用户可见的 tier,每个 tier 由一个整数 ID 值来识别(和排序)。编号更高的 tier 应该是包含了更快的内存。

default tier 的 ID 是 200(内部命名为 MEMORY_TIER_DRAM),所有有内存的 node 都是以这个 tier 来开始的。那些带有内存的设备的驱动程序可以请求将它们的内存放在不同的层中。另外两个 tier 是为了这个目的而建立的。MEMORY_TIER_HBM_GPU(300)是用于高带宽内存,而 MEMORY_TIER_PMEM(100)用于持久性内存(persistent memory)。如果一个驱动程序知道它的设备有其中某一种类型的内存,它可以将该内存放入适当的 tier。

这组 patch 还在 sysfs 中提供了一组文件(在 patch 的说明和相关文档 patch 中有描述),可以用来查看当前的内存 tier 配置。可以从用户空间来创建新的 tier,并且可以使用 sysfs 接口在 tier 之间移动 node。该 patch set 还让 demotion policy (用于将 page 移动到较慢 tier 的降级策略)变得更加明确以及方便配置。

Weiner 对早期版本的 patch set 的回应中质疑这种无论所有 node 是否包含 CPU 都将它们分配到同一 tier 的做法:

将层级明确声明出来是一个好主意,但是我们能不能保持目前的默认方式,也就是无 CPU 的 node 要比有 CPU 的 node 的 tier 层级低?我很难想象在哪些情况下这个做法是不合理的…… 或者说,就算是有,那些也应该是需要手动调整的非常特殊的场景。

不过,这种行为在当前版本的 patch set 中仍然没有改变。

无论如何,默认的 tier 分配策略是一件很可能会在未来某个时候进行的改动。一旦它被合并了,并且其 sysfs 文件成为内核 API 的一部分,使得 tier 成为显式对象(explicit object)的机制的整体结构可能就很难改变了。不过,这个 patch set 在这方面似乎没有什么争议;经过七次修订,大多数 review 意见都得到了解决。所以,虽然可能还有一两处需要调整的地方,但这项工作似乎已经准备好,可以合并了。

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

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

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



浏览 34
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报