上一篇《cfs调度类深入解刨——mm_cid科普篇》讲述了kernel中mm_cid及解决的问题。
  本篇文章介绍CAS的研发背景及架构。


CAS
相关结构
系列文章列表


CAS

  缓存感知调度(Cache Aware Scheduling,简称CAS)是Linux内核近期迎来的一项里程碑式的底层调度技术变革。在经历了英特尔(Intel)和社区工程师长达一年多的密集迭代与重构后,该机制的核心补丁在Linux 7.2内核主线中被正式合并。

  https://lkml.org/lkml/2025/3/25/625

  在包含多个复杂高速缓存域(Cache Domains)的现代处理器(如AMD Ryzen/EPYC的多CCX架构,以及Intel Xeon的多模块架构)上,缓存感知调度通过让内核理解硬件的缓存拓扑,极大地减少了多线程应用在运行中的“缓存未命中(Cache Misses)”与“缓存颠簸(Cache Bouncing)”,在特定高并发任务中带来了高达14%至44%的巨大性能飞跃。

  https://www.phoronix.com/news/Linux-7.2-Scheduler


  在传统的Linux完全公平调度器(CFS/EEVDF调度器)中,负载均衡(Load Balancing)的核心指标是CPU的使用率和队列长度。当一个多线程程序运行时,如果某些核心过载,调度器会激进地将部分线程迁移(Migrate)到闲置的核心上,以求让所有CPU算力均摊。然而,这种传统的“只看算力,不看缓存”的调度策略在现代多核处理器上面临严重的架构冲突:
    缓存域的物理隔离:现代大核CPU通常由多个核心簇(如AMD的CCX)组成。每个簇内的核心共享一个末级缓存(LLC/L3 Cache),但不同簇之间的L3缓存是物理隔离的。
    严重的缓存颠簸(Cache Bouncing):如果两个线程(例如PostgreSQL数据库的两个工作进程)频繁读写同一块共享内存:如果它们被调度在同一个L3缓存域内,由于可以直接命中共享的L3缓存,数据交换速度极快。如果负载均衡器盲目地将其中一个线程迁移到了另一个L3缓存域的核心上,两个独立的L3缓存为了维持内存一致性(Cache Coherency),必须频繁通过慢速的片上总线同步修改状态(触发大量 Invalid/Shared 信号)。
    后果:这导致CPU耗费大量时钟周期在等待总线同步上,引发流水线停顿(Stall),基准测试中经常表现为核心利用率很高,但实际吞吐量(IPC)暴跌。


  缓存感知调度的核心思想:将负载均衡的考量维度,从单纯的“算力均摊”,升级为“算力与缓存局域性的动态权衡”。它强行在调度器中引入了“任务聚合(Task Aggregation)”的逻辑。

  1. 识别“数据共享链”内核通过引入新的跟踪标志(如CONFIG_SCHED_CACHE开关),动态监测同一进程(mm_struct)下不同线程的活跃度与内存访问关联。它能计算出哪些线程之间存在高频的共享数据交互。
  2. LLC拓扑感知(Last Level Cache Domain)调度器在初始化时会完全吃透硬件的cpu_topology。当线程发生唤醒(Wake-up)或需要负载均衡时,调度器会优先将存在数据共享关系的线程“向内聚拢”约束在同一个末级缓存域(LLC Domain)内,而不是将其散布到整个芯片的各个角落。
  3. 动态负载平衡(防止过度迁移)在旧版的CAS补丁(如v3版本)中,曾出现过由于任务聚合过快,导致负载均衡器反向拉扯、频繁迁移任务的性能回退问题(Schbench测试退化)。在最终合入的稳定版本中,内核引入了自适应延迟窗口:只有当目标缓存域的过载程度超过了设定的硬阈值,才会允许破坏缓存局域性进行跨域迁移。

  为了配合SCHED_CACHE机制的落地,内核在底层进行了深度的结构体扩展:

  1. task_struct注入异步回调在进程描述符中,内核增加了cache_work异步回调头,用于在上下文切换和任务退出时,安全、低开销地更新当前任务的缓存足迹:
struct task_struct {
    ......
#ifdef CONFIG_SCHED_CACHE
    struct callback_head cache_work;  用于追踪和更新任务缓存局域性的异步工作项
#endif
	......
};
  1. 与mm_cid(内存映射并发ID)机制的完美闭环
    缓存感知调度的高效运行:
    无原子锁切换:借助mm_cid分配机制,被聚合到同一个缓存域内的线程可以共享极度紧凑的并发ID。
    当 sched_switch(调度切换快路径)发生时,由于CAS保证了线程尽量呆在相同的缓存域,this_cpu_write可以直接免锁重用当前核心的cid。
    结合确保:上层用户态(Glibc/RSEQ)分配的Per-CPU缓存数组,不仅在物理内存上是NUMA本地化的,在CPU硬件层也是LLC缓存本地化的。

相关结构

struct mm_sched {  每cpu轻量级统计结构体,被动态挂载在进程的内存描述符中(mm->pcpu_sched)。它的唯一目的,就是在不破坏调度器跨核性能的前提下,定量计算并追踪“当前进程的线程在当前CPU/末级缓存(LLC)域中留下的热数据(缓存占用率)”
	u64 runtime;  进程累积执行时间,该进程(所有线程叠加)在当前 CPU 核心上所消耗的实际硬件执行时间
	unsigned long epoch;  缓存半衰期“时间戳”,记录上一次更新该CPU上mm_sched统计数据时,内核调度队列(Runqueue)的全局时代计数器(rq->cpu_epoch)
	unsigned long occ;  缓存域占用率,经由runtime结合CPU整体运行负荷、衰减系数加权计算出来的,当前进程对该CPU或对应末级缓存(LLC)的相对占用比例(Occupancy)
};

struct mm_struct {
	......
	struct mm_sched __percpu *pcpu_sched;  进程的每cpu缓存热度统计结构
	raw_spinlock_t mm_sched_lock;
	unsigned long mm_sched_epoch;  进程全局的缓存计数器
	int mm_sched_cpu;  记录进程最热的(或者最新被全局扫描锁定的)那个核心/缓存域编号
	......
};

系列文章列表

《cfs调度类深入解刨——核心结构的用意》
《cfs调度类深入解刨——核心结构细节分析》
《cfs调度类深入解刨——最新内核细节分析1》
《cfs调度类深入解刨——最新内核细节分析2》
《cfs调度类深入解刨——最新内核细节分析3》
《cfs调度类深入解刨——最新内核细节分析4》
《cfs调度类深入解刨——最新内核细节分析5》
《cfs调度类深入解刨——最新内核细节分析6》
《cfs调度类深入解刨——MMU科普篇》
《cfs调度类深入解刨——irq科普篇》
《cfs调度类深入解刨——EAS科普篇》
《cfs调度类深入解刨——clock科普篇》
《cfs调度类深入解刨——HZ科普篇》
《cfs调度类深入解刨——psi科普篇》
《cfs调度类深入解刨——pelt细节篇》
《cfs调度类深入解刨——cfs任务状态统计细节篇》
《cfs调度类深入解刨——RDT科普篇》
《cfs调度类深入解刨——mm_cid科普篇》

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐