LWN:让 CPU 调度器考虑 Cache!

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

Cache awareness for the CPU scheduler

By Jonathan Corbet
April 29, 2025
Gemini-1.5-flash translation
https://lwn.net/Articles/1018334/

内核的 CPU 调度器必须平衡各种各样的目标。系统中所有任务的调度必须是公平的,任何给定任务的延迟都要控制在一定范围内。如果系统中有足够的工作要做,那么系统中的所有 CPU 都应该保持繁忙,但不需要的 CPU 应该关闭以降低功耗。任务也应该在最有可能缓存该任务正在使用的内存的 CPU 上运行。来自 Chen Yu 的这个补丁系列旨在改进调度器处理多线程进程的缓存局部性的方式。

RAM(随机存取存储器)速度很快,但仍然无法以 CPU 能够消耗数据的速度提供数据。因此,系统构建了多层缓存,旨在保存频繁使用的数据并使其更快地可用。从缓存中读取一个值相对较快;而如果读取需要一直走到 RAM 才能获取到的值,可能会使 CPU 暂停执行数百条指令的时间。因此,有效地利用缓存对于应用程序的良好性能至关重要。编写良好的应用程序在实现时会考虑到缓存行为,但内核也需要发挥作用。

每一层缓存都可以被不同数量的 CPU 访问;最接近的(L1)缓存可能特定于单个 CPU,而随后的(较慢但通常更大的)缓存层将由一组 CPU 共享。最后一级缓存(LLC)(Last-Level Cache),离 CPU 最远,因此也是最慢的,但它往往是最大的,并且被最大数量的 CPU 共享。将一个任务从一个 CPU 移动到另一个 CPU 可能会使其远离它在缓存中建立的数据,从而损害其性能。如果一个任务被移动到同一插槽(socket)中的另一个 CPU,那么它的大部分缓存数据可能仍然可以在较低级别的缓存中找到;如果它被移动到另一个 NUMA(Non-Uniform Memory Access,非一致性内存访问)节点,它可能必须从一个空的(从它的角度来看)缓存重新开始。

因为移动一个任务会损害其性能,所以 CPU 调度器会尽量避免这样做。这个目标经常与其他目标相冲突,比如需要在整个系统中平衡负载,以最大限度地利用可用的 CPU 资源。然而,调度器目前没有做的是尝试识别可能共享资源的任务组,也就是说,如果它们一起调度,可能会因为共享同一个缓存而有性能优势。相反,将这些任务分散在整个系统中可能会导致竞争,因为它们会争夺将数据保存在其本地缓存中。

今年 3 月,Peter Zijlstra 发布了一个 RFC 补丁,以探索改善这种情况。它基于这样的想法:如果一个进程有多个线程,那么这些线程很可能共享内存,并且可以从在同一个缓存域中运行中获益。它向描述地址空间的(原本已经很大的)mm_struct 结构体添加了一些检测代码,包括一个 per-CPU 数组,调度器使用该数组来跟踪使用该 mm_struct 的线程在系统中每个 CPU 上花费的时间。这个数据会随着时间的推移而衰减,因此最近的使用情况比遥远、被遗忘的过去(比如几十毫秒前)的使用情况更能被体现。

当唤醒一个一直在等待某个事件的线程时,调度器会访问这个 per-CPU 数组,并确定哪个 CPU 花费了最多的时间来执行来自同一进程的线程。如果感兴趣的线程一直在其他地方运行,它将被移动到所选的 CPU,在那里它将更接近其他线程,并且如果运气好,可以从与它们共享缓存空间中获益。正如 Zijlstra 当时指出的那样:“这个补丁不是为了合并,而是为了测试和开发。我们需要首先让它真正改善工作负载”。

然后,Chen 接手了这项工作,并对其进行了一些改进。最初的代码会将任务移动到“热”CPU,即使该任务已经在同一个 LLC 域中运行,因此已经与该 CPU 共享最大的缓存。在这种情况下,移动只会减慢任务的速度,而不会带来太多(如果有的话)的性能提升,因此在这种情况下会抑制任务迁移。

出现的另一个问题从对原始补丁工作方式的描述中可能看起来很明显。线程被迁移到“热”CPU,而没有考虑到该 CPU 已经有多忙。如果线程数量很大,那么这种聚集很可能会使目标缓存域过载,从而损害整体性能。这个问题通过查看调度器的负载均衡算法生成的总体使用统计数据来解决,以确保拥有相关线程的进程不会使目标域过载。具体来说,如果进程在该域中使用超过 25% 的 CPU 时间,或者在该域中拥有超过 33% 的总体负载,那么调度器将不会将更多线程移动到该域。

这已经改善了情况,但这项工作仍处于相对早期的阶段。例如,它可能会与负载均衡器发生冲突:

任务的聚集将使任务在唤醒期间非常迅速地向首选的 LLC 移动。但是,负载均衡往往会将任务从聚集的 LLC 移开。这两种迁移方向相反,并且往往会在 LLC 之间反弹任务。

CPU 调度是由许多启发式方法驱动的,这些方法经常会发生冲突。因此,添加另一个启发式方法(“将进程的线程集中在单个缓存域中”)的补丁系列肯定会带来更多令人惊讶的交互,这些交互可能需要用更多的启发式方法来解决。

这里还有一个尚未被提出的问题:收集进程的线程是识别将从共享缓存中受益的任务的最佳方法吗?可能不是,但它的优势在于实际上是可能的;检测没有这种直接关系的任务中的缓存共享可能是困难的,如果它可能的话。最终,这个补丁系列的命运将取决于它是否真正显示出对实际工作负载的改进,而不会导致其他工作负载的退步。对于像这样的更改来说,这是一个很高的标准。内核在未来很可能会有更多缓存感知的调度,但似乎还需要一段时间才能准备好。

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

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

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

Using RCU's CPU Stall Detector 2 3 This document first discusses what sorts of issues RCU's CPU stall 4 detector can locate, and then discusses kernel parameters and Kconfig 5 options that can be used to fine-tune the detector's operation. Finally, 6 this document explains the stall detector's "splat" format. 7 8 9 What Causes RCU CPU Stall Warnings? 10 11 So your kernel printed an RCU CPU stall warning. The next question is 12 "What caused it?" The following problems can result in RCU CPU stall 13 warnings: 14 15 o A CPU looping in an RCU read-side critical section. 16 17 o A CPU looping with interrupts disabled. 18 19 o A CPU looping with preemption disabled. 20 21 o A CPU looping with bottom halves disabled. 22 23 o For !CONFIG_PREEMPT kernels, a CPU looping anywhere in the kernel 24 without invoking schedule(). If the looping in the kernel is 25 really expected and desirable behavior, you might need to add 26 some calls to cond_resched(). 27 28 o Booting Linux using a console connection that is too slow to 29 keep up with the boot-time console-message rate. For example, 30 a 115Kbaud serial console can be -way- too slow to keep up 31 with boot-time message rates, and will frequently result in 32 RCU CPU stall warning messages. Especially if you have added 33 debug printk()s. 34 35 o Anything that prevents RCU's grace-period kthreads from running. 36 This can result in the "All QSes seen" console-log message. 37 This message will include information on when the kthread last 38 ran and how often it should be expected to run. It can also 39 result in the "rcu_.*kthread starved for" console-log message, 40 which will include additional debugging information. 41 42 o A CPU-bound real-time task in a CONFIG_PREEMPT kernel, which might 43 happen to preempt a low-priority task in the middle of an RCU 44 read-side critical section. This is especially damaging if 45 that low-priority task is not permitted to run on any other CPU, 46 in which case the next RCU grace period can never complete, which 47 will eventually cause the system to run out of memory and hang. 48 While the system is in the process of running itself out of 翻译上面文档
最新发布
10-23
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值