从讨论到实现的全历程分析 | 2025-2026
为什么需要 Cache Aware Scheduling
x86 和 ARM64 的缓存拓扑
缓存不感知调度的性能损失
从问题发现到方案成型
从v1到v6的完整技术演进
深入代码层面的技术实现
性能数据与案例分析
下一步发展方向
为什么需要 Cache Aware Scheduling?
现代服务器CPU(Intel Xeon、AMD EPYC、ARM64 Neoverse)普遍采用多Chiplet/Tile设计,每个Die拥有独立的Last Level Cache(LLC)
Linux传统调度器将同一NUMA节点内的所有CPU视为等价,忽略了LLC边界,导致任务在不同LLC间"跳动",产生严重的缓存抖动问题。
进程多线程共享堆内存,分散到不同LLC产生跨LLC流量
数据在不同LLC间传递产生缓存一致性开销
锁的缓存行在不同LLC间"乒乓"传输
连接处理线程共享查询缓存,分散调度导致失效
在数据库、Web服务器、科学计算等共享数据密集的工作负载中,缓存抖动可导致20-50%的性能损失。缓存未命中导致处理器流水线停顿,降低ILP利用率。
Linux现有的NUMA Balancing主要关注内存节点亲和性,而非缓存亲和性。当多个LLC共享一个NUMA节点时(如AMD EPYC),NUMA Balancing无法区分不同LLC。
CFS(完全公平调度器)的负载均衡以CPU负载为唯一指标,不考虑任务的数据共享模式。负载均衡器可能将共享数据的任务分散到不同LLC。
任务唤醒时的CPU选择(select_idle_sibling)只考虑空闲状态和简单拓扑,不追踪任务的历史缓存访问模式。
调度器虽然能识别SMT、MC(内存控制器)、NUMA层次,但缺乏对LLC边界的显式处理。
现有机制无法动态适应工作负载的缓存访问特征,缺乏运行时反馈机制。
调度器需要理解"共享数据的任务应该尽可能在同一个LLC上运行"这一核心原则,而不是仅仅追求CPU负载的数值均衡。
深入理解 x86 和 ARM64 的缓存拓扑
| 架构 | CCD数 | 核心/CCX | L3/CCD | 总L3 | NUMA | 内存 |
|---|---|---|---|---|---|---|
| Rome (Zen 2) | 8 | 4×2 CCX | 32MB | 256MB | 4节点 | 8×DDR4 |
| Milan (Zen 3) | 8 | 8×1 CCX | 32MB | 256MB | 4节点 | 8×DDR4 |
| Milan-X | 8 | 8×1 CCX | 96MB | 768MB | 4节点 | 8×DDR4 |
| Genoa (Zen 4) | 12 | 8×1 CCX | 32MB | 384MB | 4节点 | 12×DDR5 |
| Genoa-X | 12 | 8×1 CCX | 96MB | 1152MB | 4节点 | 12×DDR5 |
| Turin (Zen 5) | 16 | 8×1 CCX | 32MB | 384MB | 4-8节点 | 12×DDR5 |
关键观察:从Rome到Turin,CCD数量增加,每个NUMA节点的LLC数量也在增加。Genoa每NUMA节点有3个LLC,调度复杂度显著提升,这正是Cache Aware Scheduling的价值所在。
每Tile 24核心,6模块×4核心,每模块4MB L2
每Tile 192MB LLC,4 DDR5控制器
PCIe Gen5, CXL 2.0, UPI 2.0
分布在3个Base Tile上,每Tile被4个Compute Tile共享
Intel的LLC设计更复杂,Clearwater Forest形成层次化缓存结构,需要调度器理解这种拓扑关系。
架构:Armv9 (2021)
DSU:DSU-110
核心:8-64核心
L2:每核心1MB
L3:最大64MB共享
扩展:SVE2支持
架构:Armv9.2 (2025)
DSU:DSU-120
核心:8-192核心
L2:每核心2MB
提升:Perf/Watt +20%
ML:3x性能提升
架构:Armv9.2 (2025)
定位:高性能计算
优化:云计算/AI
向量:更大向量单元
缓存:增强一致性
ARM64服务器使用CMN互联,形成2D/3D mesh拓扑,缓存一致性通过分布式目录实现
Linux支持DIE、MC、CLUSTER等多个层次,需要处理更复杂的缓存拓扑
缓存不感知调度带来的性能损失
一个进程的多个线程共享堆内存,如果被调度到不同LLC,每次数据共享都产生跨LLC流量,严重降低性能。
一个线程生产数据,另一个线程消费,如果两者在不同LLC,每次数据传递都产生缓存一致性开销。
多个线程竞争同一锁,锁的缓存行在不同LLC间"乒乓"传输,严重降低性能。
数据库服务器的连接处理线程共享查询缓存和连接状态,分散调度导致缓存失效。
根据Intel和AMD的测试,这些场景下性能损失可达20-50%,极端情况下更高。
从顶层域(NUMA)向下遍历,找到最忙的调度组,选择任务迁移到轻载CPU。
负载均衡只考虑"负载"(运行队列长度和任务权重),不考虑"缓存亲和性"。一个"高负载"的LLC可能只是因为聚集了共享数据的任务,迁移它们反而降低性能。
当前调度器对不同层次的迁移成本有粗略估计(SMT低、NUMA高),但缺乏对LLC边界的显式处理。
从问题发现到方案成型的完整时间线
Peter Zijlstra(Linux调度器核心维护者)在LKML发布首个Cache Aware Scheduling原型补丁,开启了这一重要特性的讨论。
追踪每个进程的"最热LLC"(hottest LLC),即该进程线程运行时间最长的LLC,优先将任务调度到该LLC。
初步反馈积极,但指出需要处理:
2025年3月25日
Peter发布正式RFC补丁集,包含基础框架和初步实现
Linux调度器核心维护者,CFS主要开发者之一,Red Hat资深工程师
Tim Chen
Intel,主要开发者
Chen Yu
Intel,主要开发者
将原型发展为可合入主线的生产级代码,解决社区反馈的问题。
与NUMA Balancing的协调
避免过度聚合导致的负载不均
支持动态LLC拓扑(CPU热插拔)
添加调试和调优接口
Intel Sapphire Rapids(2 socket,30核心/socket,2 LLC)
AMD Genoa(4 NUMA节点,32 CPU/节点,2 CCX/节点)
AMD工程师K Prateek Nayak、Gautham R. Shenoy参与测试和优化,确保在AMD平台的效果
会议信息
2025年11月,东京
Linux Plumbers Conference(LPC)
演讲者
Tim Chen, Chen Yu
Intel工程师
"Cache Aware Scheduling - Improving Performance on Modern CPUs"
是否需要通过cgroups或prctl暴露调度策略控制
除了进程级别,是否支持NUMA组、waker/wakee关系、用户定义组
社区呼吁更多真实工作负载的测试数据
目标Linux 6.19或6.20
根据会议反馈,Intel团队发布v2补丁,重点改进NUMA协调和动态调整
在LLC较小的系统(如IBM Power10,SMT4共享LLC)上,过度聚合导致性能回退
回应:v4引入RSS监控,当进程内存占用超过LLC容量时禁用缓存感知调度
当NUMA Balancing决定将任务迁移到远程节点时,缓存感知调度不应阻止
回应:v2引入优先级机制,NUMA Balancing决策优先于缓存亲和性
缺乏运行时观察和调优手段
回应:添加多个debugfs接口(/sys/kernel/debug/sched/sched_cache_*)
单LLC系统不应承担缓存感知调度的开销
回应:引入sched_cache_present静态键,仅在多LLC时启用
社区反馈驱动的迭代开发模式确保了方案的实用性和通用性
从v1到v6的完整技术演进
preferred_llc 字段添加到task_struct
pcpu_sched 数组维护per-LLC运行时间统计
任务唤醒时选择preferred LLC中的空闲CPU
在account_mm_sched()中,每次任务获得CPU时间时更新对应LLC的统计。
在task_tick_cache()中定期检查(每10ms一个epoch):
仅在唤醒路径实现,负载均衡器不知情,可能导致负载不均。
当NUMA Balancing和缓存感知决策冲突时,优先NUMA Balancing
使用连续的LLC-ID空间,可直接作为数组索引
根据系统LLC数量动态调整per-LLC统计结构大小
添加三个调度特性开关:SCHED_CACHE、SCHED_CACHE_LB、SCHED_CACHE_WAKE
识别"偏好LLC"的任务,优先将其迁移到目标LLC
阻止将任务从其偏好LLC迁出
在busiest queue选择任务时,优先选择偏好目标LLC的任务
Sapphire Rapids上hackbench提升24%,schbench唤醒延迟降低21-28%
v2在AMD Milan和IBM Power10上某些工作负载出现性能回退:
1. RSS监控
添加/sys/kernel/debug/sched/sched_cache_ignore_rss接口
2. 线程数监控
追踪进程活跃线程数,与LLC核心数比较
3. 扫描范围限制
限制LLC候选扫描范围到相关NUMA节点
回退问题得到缓解,hackbench仍保持20-30%提升
单LLC系统(如大多数桌面CPU、小型服务器)不应承担缓存感知调度的开销
引入sched_cache_present静态键
系统存在至少一个拥有多个LLC的NUMA节点
且非非对称CPU拓扑
检查时机:build_sched_domains()
条件不满足时,编译器优化掉相关代码,零运行时开销
影响:单LLC系统完全不受代码体积和性能影响
移除RFC标记,正式请求合入主线
Patch 1
Peter的原始补丁(基础框架)
Patch 2-5
修复和调优
Patch 6-12
负载均衡基础设施
Patch 13-18
缓存感知负载均衡逻辑
Patch 19-20
SCHED_CACHE_LB和SCHED_CACHE_WAKE特性
44%
AMD Genoa ChaCha20提升
24-31%
Intel SPR hackbench提升
30+
Phoronix测试用例提升
当缓存感知负载均衡连续失败(达到cache_nice_tries)后,跳过该LLC
不再对busiest runqueue排序,改为在迁移时跳过不偏好目标的任务
直接使用sched_domain_topology_level数据计算LLC ID
将每个LLC的任务偏好计数移到per-CPU的最低层调度域
将sched_cache_stats从mm_struct分离
统一迁移策略到_get_migrate_hint()函数
Linux 6.19-rc3
Intel Sapphire Rapids
AMD Genoa
hackbench 1-group:+29-38%
schbench 99th延迟:-21-28%
深入代码层面的技术实现
preferred_llc: -1表示无偏好
关键设计:使用柔性数组存储per-LLC统计,根据实际LLC数量动态分配内存
在account_mm_sched()中,每次任务获得CPU时间时更新
在task_tick_cache()中定期检查(每10ms一个epoch)
找出运行时间最长的LLC(hottest_llc)
如果hottest_llc与当前preferred_llc不同
且运行时间差超过阈值,则切换
使用task_work_add()延迟执行LLC切换,避免在调度关键路径上执行复杂逻辑
在update_sg_lb_stats()中统计每个调度组的"偏好LLC任务数"
在update_sg_if_llc()中标记需要LLC均衡的调度组
在llc_balance()中决定是否进行缓存感知均衡
在detach_tasks()中,优先选择偏好目标LLC的任务
select_task_rq_fair()
→ select_idle_sibling()
→ select_cache_cpu()
当任务被多个CPU频繁唤醒时,禁用缓存感知唤醒,避免过度聚合
nosched_cache
完全禁用缓存感知调度
SCHED_CACHE
总开关,启用缓存感知调度
SCHED_CACHE_LB
启用缓存感知负载均衡
SCHED_CACHE_WAKE
启用缓存感知唤醒
/proc/sched_debug
查看每个runqueue的preferred_llc任务数
schedstat
追踪任务迁移次数和原因
性能数据与案例分析
DRAM Interleaving启用,形成1个NUMA节点+2个LLC
hackbench是Linux调度器基准测试,创建多对发送者/接收者线程通过pipe/socket通信
当活跃线程数少于LLC容量时,提升最明显;多组场景提升有限
| 配置 | 基线 | 缓存感知 | 提升 |
|---|---|---|---|
| threads-pipe-2, 1-group | 基准 | +29.06% | 显著 |
| threads-pipe-4, 1-group | 基准 | +28.63% | 显著 |
| threads-pipe-8, 1-group | 基准 | +24.68% | 显著 |
| threads-pipe-16, 1-group | 基准 | +28.46% | 显著 |
| threads-pipe-*, 2-group | 基准 | -1% ~ +8% | 不明显 |
| threads-pipe-*, 4/8-group | 基准 | -1% ~ +8% | 不明显 |
测试99th百分位唤醒延迟
总体趋势:中等负载下唤醒延迟改善明显
RISC-V模拟器,大量共享内存访问
基线
51432ms
缓存感知
28664ms
-45%
约10%提升
该测试最能体现缓存感知调度优势
内存带宽测试
512MB/2GB数据集
Stream是内存密集型,缓存影响有限
网络性能测试
1-256对连接
网络I/O主导,CPU调度影响小
压力测试
上下文切换测试
压力测试场景缓存影响有限
IBM Power10:小LLC(SMT4共享)+ 大内存占用工作负载
缓解方案:可通过调整sched_cache_ignore_rss缓解
下一步发展方向
当前
仅支持进程级别分组
未来
支持NUMA组、waker/wakee关系、cgroups、用户自定义组
当前
全局统一策略
未来
per-process、per-task-group策略,通过prctl()或cgroups暴露
当前
主要针对x86(Intel/AMD)
未来
优化ARM64(Neoverse)、RISC-V等架构支持
当前
基于RSS和线程数的启发式
未来
基于实际缓存未命中率反馈的自适应算法
2026年2月:v6补丁已发布,社区审查中
需要更多真实工作负载的长期测试数据
某些边缘场景的性能回退需要进一步解决
用户接口设计需要社区共识
乐观预测
Linux 6.20
2026年中
保守预测
Linux 6.21 或 6.22
2026年底
获得Peter Zijlstra、Ingo Molnar等核心维护者的Reviewed-by
通过0-day测试机器人的回归测试
至少两个主要发行版的预览集成
Cache Aware Scheduling是Linux调度器在现代多LLC CPU架构下的必要演进,通过将共享数据的任务聚合到同一LLC,可显著提升性能。
44%
AMD Genoa提升
31%
Intel SPR提升
建立了完整的缓存感知调度框架
解决了与NUMA Balancing的协调问题
引入了动态自适应机制避免过度聚合
Intel主导开发,但AMD平台获益更大,体现了开源协作的力量
感谢 Peter Zijlstra、Tim Chen、Chen Yu、K Prateek Nayak 及所有社区贡献者