sched discussion
[PATCH] kernel/sched/fair: The CPU idle check can be placed before the CPU overload check
LLM 分析
kernel/sched/fair:把 CPU idle 检查放到 CPU overload 检查之前
系列概况
- 标题: [PATCH] kernel/sched/fair: The CPU idle check can be placed before the CPU overload check
- 作者: Li kunyu likunyu10@163.com
- 版本: v1(单封 patch,无 series)
- 规模: 1 个文件,3 行新增 / 3 行删除
- 修改文件: kernel/sched/fair.c(
update_sg_lb_stats()函数体内) - 代码统计: 6 行 diff,3 insertions(+), 3 deletions(-)
- Message-ID: 20260723083518.117991-1-likunyu10@163.com
- 完整性: 标题、commit 描述、diff 块、Signed-off-by 全部齐全;动机在 commit body 中自洽
补丁目的
在 CFS 调度器的 update_sg_lb_stats() 中,作者把 cpu_overutilized(i) 这一过载判定从循环前段挪到 !nr_running && idle_cpu(i) 之后。目标是用便宜的 idle 判定做一次短路:当 CPU 处于"无人 + 空闲"时,过载检查毫无意义,可以直接跳过。
旧流程的问题
原版顺序里,cpu_overutilized(i) 在循环体内无条件挨个 CPU 执行一次——即便这颗 CPU 后面紧接着就被 idle 检查判为"无任务可调度",前面的过载判定工作也白做了。在 idle 核很多的服务器或者容器云里,这是一笔不小的浪费。
新流程
先做 nr_running 与 idle_cpu(i) 的快速判断;满足 idle 条件的 CPU 直接 continue 走 idle 路径,跳过 cpu_overutilized();只有非 idle CPU 才继续做 utilization 比较。group_overutilized 这个 OR-style 标记的最终结果不变(idle CPU 本来就不会被置为过载),但每颗 idle CPU 省下了一次过载判定调用。
Patch 概览
- 单文件、单 hunk。
- 位置:
kernel/sched/fair.c:11819起。 - diff 本质上是把一段
if (cpu_overutilized(i)) sgs->group_overutilized = 1;从循环顶部搬到底部,挪到 idle 检查之后。
关键实现
修改前(语义示意):
for_each_cpu(i, env->cpus) {
if (cpu_overutilized(i)) /* expensive */
sgs->group_overutilized = 1;
nr_running = rq->nr_running;
sgs->sum_nr_running += nr_running;
if (!nr_running && idle_cpu(i)) /* cheap */
continue;
}
修改后:
for_each_cpu(i, env->cpus) {
nr_running = rq->nr_running;
sgs->sum_nr_running += nr_running;
if (!nr_running && idle_cpu(i)) /* cheap first */
continue;
if (cpu_overutilized(i)) /* only on non-idle */
sgs->group_overutilized = 1;
}
关键点:
cpu_overutilized()是纯读函数,不会改sgs之外的副作用,所以单纯换位置不影响其它逻辑。group_overutilized是组级 OR 标记,idle CPU 永远不会被它置位,因此短路掉完全安全。nr_running/sum_nr_running的累加顺序没变,统计语义保持一致。
Old order: every CPU runs the expensive check, even idle ones
+---------------------+
| cpu_overutilized(i) | ---> (expensive; wasted on idle)
+---------------------+
|
v
+---------------------+
| read nr_running |
+---------------------+
|
v
+---------------------+
| idle_cpu(i) ? |
| yes -> continue |
+---------------------+
New order: short-circuit on the cheap check first
+---------------------+
| read nr_running |
+---------------------+
|
v
+---------------------+
| idle_cpu(i) ? |
| yes -> continue | ---> (idle CPU skips expensive check)
+---------------------+
| no
v
+---------------------+
| cpu_overutilized(i) | ---> (only non-idle CPUs)
+---------------------+
类比
把每颗 CPU 想象成停车场里的一辆车:
cpu_overutilized()相当于给发动机做温度扫描——比较贵。idle_cpu()+nr_running == 0是"车没发动、车库没人"。- 旧流程是"不管什么车,先扫一次温度再说";新流程是"先看一眼是不是停着的冷车,是的话连温度都不用测"。
- 这跟体检里"先问你今天有没有运动,没有就别安排心电图"是一个套路:用便宜的预筛把昂贵的检查跳过去。
Highlight:风险与注意点
- DL 任务未覆盖:作者自己在 commit body 中已经指出,
cpu_overutilized()只看 CFS 任务的 util,DL(deadline)任务不在它视野里。新顺序虽然不会引入新问题,但也没有解决"DL 把核压满却被当 idle"的潜在盲区,后续可能需要单独处理。 - 纯读检查,安全性高:
cpu_overutilized()不修改 rq 或 sgs 之外的副作用,单纯换位置不会破坏其它不变式;这是这种"挪一挪"patch 能被合的关键。 - 收益有限但稳:在大规模 idle 系统(很多容器/虚拟机空闲)上能省一点点函数调用;在满载机器上几乎无差别。属于"低风险低收益"的清理型 patch。
- 跟更广重排是否兼容:如果上游在
update_sd_lb_stats()或find_busiest_group()上做类似的重排,本次 patch 可能并入更大的清理 series;建议关注后续 v2 是否会扩展范围。
版本变化
无(单封 v1 patch)。
一句话总结
作者把 update_sg_lb_stats 里 cpu_overutilized() 的判定挪到 idle_cpu() 检查之后,让空闲 CPU 走短路、跳过昂贵的过载扫描,语义不变但减少冗余调用。