0/3 已展开

LLM 分析

sched/fair:NOHZ 均衡优先选完全空闲 core

系列概况

  • 标题:[PATCH v5] sched/fair: Prefer fully idle cores for NOHZ balancing
  • 作者:Andrea Righi arighi@nvidia.com
  • 版本:v5
  • 规模:1 个文件,+44 / -11
  • 修改文件:kernel/sched/fair.c
  • 代码统计:55 行变更(44 新增,11 删除)
  • Message-ID20260806134355.592145-1-arighi@nvidia.com
  • 完整性:完整(diff、commit message、签名、变更日志齐全)

补丁目的

修改 find_new_ilb(),让 NOHZ 模式下挑选 idle load balancer(ILB)目标 CPU 时,优先选择 SMT 整 core 全闲的 CPU,而不是 housekeeping 集合中的第一个 idle CPU。

解决的问题:NVIDIA Olympus(Vera)上,ILB 即使短暂唤醒一个空闲兄弟线程,被唤醒的线程也要等约 10 Ki cycle 才完全恢复单线程最高性能,反复打断造成持续性能惩罚。

旧流程的问题

旧实现只从 nohz.idle_cpus_mask ∩ housekeeping_cpumask(HK_TYPE_KERNEL_NOISE) 取第一个 idle CPU,完全忽略 SMT 兄弟线程状态。后果:

  • 在 SMT 系统上可能唤醒本 core 上另一个空闲兄弟;
  • 即使该兄弟立即回到 WFI,Vera 上另一线程需 10 Ki cycle 才恢复单线程性能;
  • ILB 周期性触发会形成持续干扰。

新流程

find_new_ilb() 的步骤:

  1. 重用 per-CPU select_rq_mask 作为临时 cpumask(中断已禁用,并发安全)。
  2. nohz.idle ∩ HK_NOISE 求交得到候选集合 ilb_cpus
  3. 遍历 ilb_cpus
    • busy CPU:若 sched_smt_active() 且 fallback 已存在,剔除该 CPU 的 SMT 兄弟,然后 continue
    • idle CPU:若 SMT 启用且 is_core_idle() 为 false,记下 fallback 并剔除它的 SMT 兄弟;若整 core 闲,立即返回。
  4. 遍历结束仍未命中整 core 全闲则 return fallback,再不行 return -1
+-------------------+
|  find_new_ilb()   |
+-------------------+
         |
         v
+-------------------+
| ilb_cpus = idle   |
|   & HK_NOISE      |
+-------------------+
         |
         v
+-------------------+
| for_each_cpu(.)   |
+-------------------+
   |        |
   v        v
 busy?    idle?
   |        |
   |        +-- is_core_idle? --yes--> return ilb_cpu
   |        |       |
   |        |       no
   |        |       |
   |        |       +-- fallback unset -> fallback=ilb_cpu
   |        |       |                    prune smt siblings
   |        |       +-- fallback set  -> prune smt siblings
   |        |
   |        +--(loop ends)--> return fallback
   |
   +-- smt_active && fallback>=0?
                 |
                yes -> prune smt siblings
                 |
                 v
              continue

关键实现

static inline int find_new_ilb(void)
{
    int ilb_cpu, fallback = -1;
    struct cpumask *ilb_cpus;

    lockdep_assert_irqs_disabled();

    /* Reuse the per-CPU select_rq_mask; interrupts are disabled,
     * so the per-CPU mask is safe to use here. */
    ilb_cpus = this_cpu_cpumask_var_ptr(select_rq_mask);
    cpumask_and(ilb_cpus, nohz.idle_cpus_mask,
                housekeeping_cpumask(HK_TYPE_KERNEL_NOISE));

    for_each_cpu(ilb_cpu, ilb_cpus) {
        if (!idle_cpu(ilb_cpu)) {
            /* Busy CPU + already have fallback => skip its SMT siblings. */
            if (sched_smt_active() && fallback >= 0)
                cpumask_andnot(ilb_cpus, ilb_cpus, cpu_smt_mask(ilb_cpu));
            continue;
        }

        /* Prefer a fully idle SMT core; keep first idle CPU as fallback. */
        if (sched_smt_active() && !is_core_idle(ilb_cpu)) {
            if (fallback < 0)
                fallback = ilb_cpu;
            cpumask_andnot(ilb_cpus, ilb_cpus, cpu_smt_mask(ilb_cpu));
        }
    }
    return fallback;
}

注:伪代码示意;真实源码在 is_core_idle() 成立时立即 return ilb_cpu,循环结束才 return fallback

Patch 概览(与版本变化)

版本关键改动
v1初版:加入 is_core_idle() 偏好
v2宽 SMT 系统一次 prune 部分 busy core 的所有兄弟,避免重复 is_core_idle()(Prateek Nayak)
v3fallback 已存在时,遇到 busy CPU 立即跳过该 core 所有兄弟(Mete Durlu)
v4删掉冗余 this_cpu 检查(Prateek Nayak、Vincent Guittot)
v5收集 Tested/Reviewed 标签,整理局部变量声明顺序(Prateek Nayak)

测试数据:自制 GEMM benchmark,每个 SMT core 一个 CPU 密集任务,从约 6.2 TFLOP/s 提升到 9.4 TFLOP/s。

签名行:Tested-by K Prateek Nayak;Reviewed-by K Prateek Nayak、Mete Durlu、Vincent Guittot、Shrikanth Hegde;Signed-off-by Andrea Righi。

类比

SMT core 像一张双人餐桌。ILB 想找个座位时:

  • 旧逻辑:看到桌边有一把空椅子就坐下,可能打扰正在专心吃饭的人;
  • 新逻辑:先看是否有一整张空桌(整 core 闲),有就坐那里;所有桌都有人时,才坐到边上空椅子(fallback)。

Vera 平台上,被打扰的人需要约 10 Ki cycle 才完全专注;ILB 频繁插手会让他持续"重新进入状态"。另一个类比:图书馆里找座位时,有一整排空座位就不要先去坐有人打盹的长椅——fallback 才是实在没空排时再凑合。

Highlight:风险与注意点

  • 更远的 core 唤醒:偏好完全空闲 core 可能拉远 CPU 唤醒距离,增加 ILB 启动延迟或能耗;commit message 已注明。
  • 扫描变多:判断 is_core_idle() 需遍历候选 CPU;代码通过"一次剪掉整个 SMT 兄弟集"避免重复检查。
  • 非 SMT 系统零影响sched_smt_active() 为 false 时退化到原版行为。
  • 错误边界:housekeeping 集合中无 idle CPU 时返回 -1,调用方需照原方式处理。
  • 并发假设:注释明示依赖"调用时中断已禁用"以安全复用 select_rq_mask,要确认所有调用点都满足。
  • 跨架构泛化:论证主要基于 NVIDIA Olympus/Vera,其他 SMT 平台需 workload 验证收益。
  • 跟进:本 thread 中 Shrikanth Hegde 两封 reply 仅引用 patch 文本、无实质评论,需等待 maintainer(Peter Zijlstra 等)Ack/nack。

版本变化

v1 → v2:宽 SMT 一次 prune 整个 SMT 兄弟集,避免重复 is_core_idle()
v2 → v3:fallback 已存在时遇到 busy CPU 立即跳过该 core 所有兄弟。
v3 → v4:删掉冗余 this_cpu 检查。
v4 → v5:补 Tested/Reviewed 标签,整理局部变量声明顺序。

一句话总结

通过让 find_new_ilb() 优先选整 core 全闲的 CPU 并以 idle CPU 作为 fallback,在 NVIDIA Olympus/Vera 这种 SMT 唤醒惩罚严重的平台上把 GEMM benchmark 从 ~6.2 提升到 9.4 TFLOP/s。