一位 Intel 内核工程师的一次「手术刀式」改进,让 2011 年的 Sandy Bridge 焕发第二春
一、一个让人揪心的数字:FPS 4
一台 i7-2600K(2011 年发布),配一张 RX 580,跑个游戏——后台挂着浏览器和更新程序。
然后帧率监控告诉你:最低 FPS 4。
不是显卡不够用,不是 CPU 太老。是你的操作系统内核调度器,在 cgroup 层级里迷路了。
这个场景太熟悉了。Steam Deck、老笔记本、客厅 HTPC——任何跑 Linux 的旧硬件,在后台有负载时都会遭遇这种「帧率断崖」。而 Peter Zijlstra 给出的答案,名字就很形象:Flatten The Pick——把选任务的路,压平。
二、EEVDF 很好,但 cgroup 是个坑
2.1 EEVDF 干了件漂亮事
Linux 6.6 合入的 EEVDF(Earliest Eligible Virtual Deadline First)调度器,取代了沿用近二十年的 CFS。它的核心思想优雅而简洁:
每个任务有一个虚拟截止时间,调度器每次都选「最紧急」的那个。
公平性更好,延迟更可控。但有一个问题 EEVDF 没解决——当任务被装进 cgroup 这个盒子的时候。
2.2 cgroup 层级:一个隐形的迷宫
现代 Linux 用 cgroup 管理 CPU 资源分配。你可以把任务分组,给不同组分配不同的 CPU 权重:
root(权重 1024) ├── 游戏组(权重 1024) │ ├── game.exe │ └── steam.exe ├── 浏览器组(权重 512) │ └── chrome └── 后台组(权重 256) └── updated
看起来很合理。但问题是:调度器怎么从这棵树里选下一个任务?
在 flatten 之前,EEVDF 是这样 pick 的:
- 从根 cgroup 开始,计算所有子节点的 eligibility
- 选中最「紧急」的子节点(可能是一个中间 cgroup,而不是实际任务)
每经过一层中间节点,都要做一次完整的 enqueue/dequeue 操作。
这就带来了两个问题:
- 延迟不可控
- 开销随机放大:后台有任务争抢时,调度器在层级间来回奔波,开销叠加
游戏场景最受伤的原因在于:帧渲染对调度延迟极度敏感。一次 100ms+ 的调度抖动,就是一次掉帧。
三、Flatten The Pick:把路铺平
3.1 一句话概括
保留 cgroup 层级来算权重,但让 EEVDF 选任务时走平路。
换言之:cgroup 的层级树继续用于 weight/shares 的财务记账,但调度选择(pick)不再沿着这棵树走,而是在一个平坦的单层 runqueue 上直接挑选。
3.2 数学原理
Peter Zijlstra 在 cover letter 里给出了详细的推导。每个任务在 EEVDF 中的 eligibility 由以下因素决定:
V_i(t) = 虚拟运行时间deadline_i = V_i(t) + (lag / weight_i)
在层级模型中,任务的权重需要乘上所有祖先 cgroup 的权重比例,得到有效权重(F_g^d 项)。原来的做法是在 pick 时动态计算这个值——这就要求遍历层级。
Flatten 的洞察是:权重可以在 enqueue/dequeue 时预先折算好,pick 时直接用。这样 EEVDF 的 pick 操作从递归遍历变成 O(1) 的直接选择,数学上的公平性等价性保持不变。
3.3 架构变化
| | |
|---|
| pick 路径 | | |
| cgroup 层级职责 | | |
| 中间节点 | | |
| pick 复杂度 | | |
| 延迟抖动来源 | | |
3.4 新增的 cgroup_mode
为了让平坦 runqueue 工作得更顺滑,patch 引入了一个调试接口:
/sys/kernel/debug/sched/cgroup_mode
| |
|---|
| concur | 使用任务组内活跃任务数计算权重,避免过小的 F_g 值 |
| |
| |
| |
| |
Peter 的 commit message 写得很坦诚:
“改变默认值肯定会有问题……但新架构下旧的权重计算方式会放大不公平性。既然可配置,就改默认值,希望更多人受益。”
四、数据:从 4 到 29
测试平台:Intel Core i7-2600K + AMD Radeon RX 580(Lutris/Proton)
帧率
| | | |
|---|
| 最低 FPS | 4.0 | 29.0 | +625% 🔥 |
| 平均 FPS | | | |
帧时间(越低越好)
| | | |
|---|
| 平均帧时间 | | 17.0 ms | |
| 最大帧时间 | 121.2 ms | 30.0 ms | -75% 🔥 |
关键解读:
- 最低 FPS 4→29:意味着「卡成幻灯片」的场景变成了「不富裕但流畅」。这是体感改善最大的部分。
- 最大帧时间 121ms→30ms:这个指标直接对应 micro-stutter(微卡顿)。121ms 意味着每几秒就有一帧卡住超过 1/8 秒——人眼完全能感知到。降到 30ms 后,这种卡顿基本消失。
Peter 选 i7-2600K 做测试是有意为之——这是 2011 年的 CPU,正是「potato hardware」的典型代表。如果这个改进在它上面有效,那任何现代 CPU 只会更好。
五、交叉映射:这不是第一次「压平」
Linux 内核历史上,类似的「压平」改进并不少见:
| | |
|---|
| 调度器 | | |
| 内存管理 | | |
| 文件系统 | | 绕过 VFS 层,直接操作 block layer |
| 网络 | | |
它们的共同模式是:当一个层级结构从「管理工具」变成了「性能瓶颈」,就把它从热路径(hot path)中剥离出去。
Flatten The Pick 的优雅之处在于它没有推翻 cgroup——cgroup 的权重计算仍然保留层级。它只是把「选任务」这件事从层级中解耦出来。这种最小侵入式优化,是内核 hacking 中非常难得的好品味。
六、谁受益?
- 🎮 游戏玩家:最低帧率大幅提升,Proton/Lutris 场景尤其明显
- 📦 容器/虚拟化:cgroup 层级越深,收益越大。K8s 环境下多级 cgroup 的调度延迟显著降低
- 🏢 多租户服务器
- ⏱️ 低延迟/实时场景
换言之,所有跑 Linux 7.3+ 的设备都会受益,只是游戏场景最容易被人感知到。
七、时间线与总结
| | |
|---|
| | 8 patches,首次提出 flatten 思路 |
| | |
| v3 | 2026-06 | 7 patches,合入 tip/sched/core |
| Linux 7.3 | 2026-08(目标) | 合入主线 |
Flatten The Pick 是一次精准的手术式改进。不长篇大论,不推倒重来,不引入新框架。它只改了一件事:让 EEVDF 选任务的时候,不再绕路。
有时候,最好的性能优化不是加更多东西,而是把路铺平。
参考来源:
- Phoronix: Linux 7.3 Expected To “Flatten The Pick”
- Phoronix: “Flatten The Pick” Linux Patches Progress
- LWN: sched: Flatten the pick (RFC)
- Peter Zijlstra v3 patch series on LKML
- v3 patch 7/7: sched/eevdf: Move to a single runqueue