0/1 已展开

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_runningidle_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:风险与注意点

  1. DL 任务未覆盖:作者自己在 commit body 中已经指出,cpu_overutilized() 只看 CFS 任务的 util,DL(deadline)任务不在它视野里。新顺序虽然不会引入新问题,但也没有解决"DL 把核压满却被当 idle"的潜在盲区,后续可能需要单独处理。
  2. 纯读检查,安全性高cpu_overutilized() 不修改 rq 或 sgs 之外的副作用,单纯换位置不会破坏其它不变式;这是这种"挪一挪"patch 能被合的关键。
  3. 收益有限但稳:在大规模 idle 系统(很多容器/虚拟机空闲)上能省一点点函数调用;在满载机器上几乎无差别。属于"低风险低收益"的清理型 patch。
  4. 跟更广重排是否兼容:如果上游在 update_sd_lb_stats()find_busiest_group() 上做类似的重排,本次 patch 可能并入更大的清理 series;建议关注后续 v2 是否会扩展范围。

版本变化

无(单封 v1 patch)。

一句话总结

作者把 update_sg_lb_statscpu_overutilized() 的判定挪到 idle_cpu() 检查之后,让空闲 CPU 走短路、跳过昂贵的过载扫描,语义不变但减少冗余调用。