sched discussion
[PATCH v5] sched/fair: Prefer fully idle cores for NOHZ balancing
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-ID:20260806134355.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() 的步骤:
- 重用 per-CPU
select_rq_mask作为临时 cpumask(中断已禁用,并发安全)。 - 与
nohz.idle ∩ HK_NOISE求交得到候选集合ilb_cpus。 - 遍历
ilb_cpus:- busy CPU:若
sched_smt_active()且 fallback 已存在,剔除该 CPU 的 SMT 兄弟,然后continue。 - idle CPU:若 SMT 启用且
is_core_idle()为 false,记下fallback并剔除它的 SMT 兄弟;若整 core 闲,立即返回。
- busy CPU:若
- 遍历结束仍未命中整 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) |
| v3 | fallback 已存在时,遇到 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。