sched/fair: Prefer fully idle cores for NOHZ balancing(v4 进展更新)

CFS 调度器 · NOHZ idle load balancer · SMT 感知优化(v3 → v4 进展)

💡 一句话总结

在 SMT 超线程服务器上,NOHZ idle load balancer(负责替空闲 CPU 做均衡的 ILB)旧选核逻辑会去唤醒一个忙碌 core 的空闲 sibling,在 NVIDIA Vera(Olympus 核)上把正在计算的那个 sibling 的算力持续拉低;本补丁让 find_new_ilb() 优先选择整个 core 完全空闲的 CPU 作 ILB、以第一个空闲 CPU 兜底,遇到 busy core 就批量跳过其 siblings,从而保护 busy core 的算力——GEMM 基准实测吞吐 6.2 → 9.4 TFLOP/s(+50%)。v4 是第四版(v1→v2→v3→v4):按 AMD Prateek Nayak 与 Linaro Vincent Guittot 的意见移除冗余的 this_cpu 检查,并新获 IBM Mete Durlu 的 Reviewed-by(性能数据为作者自报,未独立验证,与 v3 相同)。

📋 补丁基本信息

项目内容
补丁类型优化(performance)——ILB 选核路径的 SMT 感知裁剪
状态In Review(v4,NVIDIA 提出)
当前版本v4 · lore 链接
版本演进 v1(07-28)→ v2(07-29)→ v3(07-31)→ v4(08-04,+44/-11,移除冗余 this_cpu 检查 + 新获 Mete Durlu Reviewed-by)
作者机构Andrea Righi(NVIDIA)
提交日期2026-08-04(v4)
改动范围kernel/sched/fair.c,+44/-11 行,1 文件(单补丁,非 patchset)
核心函数find_new_ilb() / kick_ilb()
评审标签Reviewed-by: Mete Durlu(IBM,v4 新增)
原始链接lore Message-ID

📊 速览卡片

核心机制
SMT 感知选核
优化目标
保护 sibling 算力
适用场景
超线程服务器
实测提升
GEMM +50%

🎯 解决什么问题

背景 / 原始动机
这是对 08-03 已分析过 v3 的进展更新,问题本身自 v1 起未变:NOHZ 模式(CPU 进入 NOHZ 空闲后不再每个 tick 自己处理负载均衡)需要选一个「idle load balancer」(ILB)代表所有空闲 CPU 定期做均衡。旧 find_new_ilb() 只挑第一个空闲的 housekeeping CPU,不看它所在的物理 core 是否完全空闲。NVIDIA 在 Vera 平台(Olympus 核)上观测到:短暂唤醒一个忙碌 core 的空闲 sibling,会把该 core 上另一个 sibling 的单线程性能拉低,且干扰不会随被唤醒 sibling 进入 WFI 立即结束——需要 sibling 空闲满 10 Ki cycles(约 1 万周期)才恢复满血;反复的短唤醒即使几乎没有真正重叠也会维持干扰。
系统层面:ILB 选择未感知 SMT 拓扑
find_new_ilb() 在 nohz.idle_cpus_mask ∩ housekeeping 里按 CPU 顺序取第一个 idle_cpu()。它不区分「该 CPU 空闲」与「该 CPU 所在 core 完全空闲」——两者在 SMT 系统上是不同的。若选中的 ILB 恰是忙碌 core 的空闲 sibling,激活它做均衡就会抢占共享执行资源,干扰正在跑计算的那个 sibling。
场景层面:计算密集负载 + 超线程拓扑放大干扰
负载特征:GEMM(通用矩阵乘法)等 HPC/深度学习核心算子,CPU 密集、对单核峰值算力极度敏感——每个 SMT core 跑一个计算任务时,任何 sibling 唤醒都会直接换算成吞吐损失。硬件特征:NVIDIA Vera 的 Olympus 核与常见 x86 SMT 不同,sibling 短暂唤醒后需要 ~10 Ki cycles 的空闲资格期才恢复满血单线程性能,于是「ILB 每次均衡唤醒 sibling → busy core 掉性能」的代价被显著放大。作者也明确说:大多数 SMT 平台上 ILB 是短命活动,瞬时 sibling 唤醒影响可忽略,本补丁主要针对 Olympus 这类「唤醒后恢复慢」的核。
受影响负载:GEMM 等单核算力敏感的计算密集负载 · 为什么解决此场景:保护 busy sibling 的算力不被 ILB 周期性唤醒拉低

🧩 核心机制

v4 把 find_new_ilb() 重写为「优先完全空闲 core,fallback 兜底」的选核器:在候选集合上逐 CPU 考察,只对真正空闲的 CPU 再问一句「你的 core 是否完全空闲」;是则立即选中,否则把它记为首个 fallback 并批量跳过其 SMT siblings,避免对已知不满足条件的 core 做逐 CPU 重复检查。

从系统层面看
改动集中在 kernel/sched/fair.c 的 find_new_ilb(),是 NOHZ 均衡的「选人」环节,不碰均衡算法本身。核心变化有三:
  • 候选集合本地化:把原来 for_each_cpu_and(...) 的「按顺序取第一个」改成先 cpumask_and 出一份本地 ilb_cpus,复用每-CPU 的 select_rq_mask(关中断保护,无需额外锁)。
  • 两级判定:idle_cpu() 判断「该 CPU 空闲」(可做均衡的最小前提),is_core_idle() 判断「整个 core 空闲」(无干扰的最优前提)。前者是既有条件,后者是 v2 引入、v4 保留的 SMT 感知条件。
  • 批量剪枝:无论遇到 busy CPU 还是「空闲但 core 不空闲」的 CPU,都用 cpumask_andnot(ilb_cpus, cpu_smt_mask(ilb_cpu)) 一次性跳过该 core 的其余 siblings——这是 v2(Prateek 建议)与 v3(Mete 建议)逐步完善的剪枝,v4 保留。
为什么这样能保护算力:ILB 从不落在忙碌 core 上,busy sibling 就不会被周期唤醒,GEMM 任务的单核峰值算力得以保持。fallback 保证「机器上没有任何完全空闲 core 时,仍返回第一个空闲 CPU」,均衡不停滞。
find_new_ilb SMT 感知选核 before/after 对比图
图 1:Before(旧)取第一个空闲 CPU,可能唤醒 busy core 的 sibling;After(v4)优先完全空闲 core 的 CPU,遇到 busy/非全空闲 core 批量跳过其 siblings,并以第一个空闲 CPU 作 fallback
来源:基于 lore 真实补丁 diff(v4)绘制
步骤操作目的
构建候选cpumask_and(ilb_cpus, nohz.idle_cpus_mask, housekeeping_cpumask(HK_TYPE_KERNEL_NOISE))本地化「空闲且 housekeeping」的候选集合,复用每-CPU mask 免锁
busy CPU 剪枝非空闲且已有 fallback → cpumask_andnot(ilb_cpus, cpu_smt_mask)busy 证明该 core 不可能全空闲,跳过其 siblings,减少无效遍历
core 空闲判定空闲但 !is_core_idle() → 记 fallback,跳过 siblings,continue非全空闲 core 的 CPU 只作兜底,优先找全空闲 core
返回全空闲 core 的 CPU 立即返回;循环结束返回 fallback 或 -1优先全空闲 + 兜底保证均衡推进
最核心的代码证据(逐字,v4 diff)
 static inline int find_new_ilb(void)
 {
-	int this_cpu = smp_processor_id();
-	const struct cpumask *hk_mask;
-	int ilb_cpu;
+	struct cpumask *ilb_cpus;
+	int ilb_cpu, fallback = -1;
+
+	lockdep_assert_irqs_disabled();
+
+	/*
+	 * Reuse the per-CPU select_rq_mask, which is protected from concurrent
+	 * use on this CPU by having interrupts disabled.
+	 */
+	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)) {
+			/*
+			 * Once an idle fallback exists, a busy CPU proves that
+			 * this core cannot be fully idle. Skip its siblings.
+			 */
+			if (sched_smt_active() && fallback >= 0)
+				cpumask_andnot(ilb_cpus, ilb_cpus, cpu_smt_mask(ilb_cpu));
+			continue;
+		}

-	hk_mask = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE);
+		/*
+		 * Running the idle load balancer on an idle sibling of a busy
+		 * SMT core can reduce the capacity available to its sibling. Prefer
+		 * a CPU whose entire core is idle, but retain the first idle CPU as
+		 * a fallback so idle balancing can still make progress when no fully
+		 * idle core exists.
+		 */
+		if (sched_smt_active() && !is_core_idle(ilb_cpu)) {
+			if (fallback < 0)
+				fallback = ilb_cpu;

-	for_each_cpu_and(ilb_cpu, nohz.idle_cpus_mask, hk_mask) {
-		if (ilb_cpu == this_cpu)
+			/*
+			 * The core is not idle, so there is no need to check
+			 * any of its other SMT siblings.
+			 */
+			cpumask_andnot(ilb_cpus, ilb_cpus,
+				       cpu_smt_mask(ilb_cpu));
 			continue;
+		}

-		if (idle_cpu(ilb_cpu))
-			return ilb_cpu;
+		return ilb_cpu;
 	}

-	return -1;
+	return fallback;
 }

▲ 为什么这是最核心的:fallback = -1 与 is_core_idle() 分支是「优先全空闲 + 兜底」的全部逻辑,cpumask_andnot(cpu_smt_mask) 是「遇到已知 busy/非全空闲 core 就批量跳过」的剪枝核心;v4 相对 v3 的主要变化是删掉了被 Prateek/Vincent 指为冗余的 this_cpu 变量与 if (ilb_cpu == this_cpu) continue; 分支(find_new_ilb() 的调用方必然不在 nohz.idle_cpus_mask 中,该分支恒不命中,解读(AI 分析))。(其余 diff 读者可自查 lore)

📈 性能影响

提升角度(方法论分类)
off-CPU 算力保护(主类):本补丁不减计算指令、不缩临界区,而是避免 ILB 唤醒干扰 busy sibling——把「被外部唤醒打断」这一 off-CPU 干扰源从 busy core 上移除,等于把「别人打扰我」变成「我去全空闲的 core 上干活」,保住的是计算任务可用的 on-CPU 算力(Brendan Gregg 视角:请求墙钟时间 = on-CPU 计算 + off-CPU 等待/干扰,这里优化的是 off-CPU 干扰项)。开销降低(次类):v2/v3 的批量跳过 siblings 减少宽 SMT 系统上逐 CPU 遍历的无效检查,也顺带降低了 ILB 选择自身的扫描成本。
受益场景识别
复用「改动路径 → 高频触发场景 → 放大成本的配置」框架(解读(AI 分析),基于补丁说明):改动的是 find_new_ilb(),它在系统有多个 NOHZ 空闲 CPU、需要定期触发均衡时被高频调用。收益最大的配置是:SMT 超线程已启用 + 每个 core 上各跑一个 CPU 密集任务(ILB 候选多为忙碌 core 的 sibling)+ sibling 唤醒恢复慢的核(NVIDIA Olympus/Vera)。在这些前提下,ILB 每次选核都避开忙碌 core,busy sibling 不被周期性唤醒,GEMM 这类单核峰值敏感负载的吞吐得以完整释放。
场景/用例运行环境改进前改进后
GEMM 通用矩阵乘法(HPC/深度学习核心算子,CPU 密集,一个 SMT core 一个计算任务)NVIDIA Vera 平台(Olympus 核),SMT 启用~6.2 TFLOP/s~9.4 TFLOP/s(+50%)

说明:数据来自 v4 补丁说明原文(作者用 ad hoc GEMM 基准、在 CPU affinity 掩码内每个 SMT core 跑一个 CPU 密集任务测得)。作者自报,未独立验证,且与 v3 报告的数据完全相同(v4 未新增基准)。补丁未提供更广的多负载/多平台数据;非 SMT 平台补丁明确声明行为不变(继续选第一个空闲 housekeeping CPU)。

find_new_ilb 选核 before/after 对比
图 2:性能收益机理图——GEMM 场景下 ILB 选核避开 busy core 前后对比(6.2 → 9.4 TFLOP/s)
来源:作者自报数据 + 基于 lore v4 diff 绘制

🔄 方案演进

单补丁,v1 → v4 四次迭代,每次都由 review 讨论推动(基于 lore 各版本 changelog 与讨论,不编造):

v1 → v2 → v3 → v4 演进脉络
v1(07-28,+18/-5):初版——优先选完全空闲 core 的 CPU 作 ILB。
v2(07-29,+39/-9):Prateek Nayak(AMD)建议——引入 is_core_idle() 判定,并在宽 SMT 系统上通过剪掉「部分忙碌 core 的其余 siblings」避免重复 core-idle 检查。
v3(07-31,+47/-9):Mete Durlu(IBM)建议——找到 idle fallback 后,一旦遇到 busy CPU 就跳过其全部 siblings,避免对已知忙碌 core 做逐 CPU 遍历。
v4(08-04,+44/-11):Prateek Nayak + Vincent Guittot(Linaro)意见——移除冗余的 this_cpu 检查;并新获 Mete Durlu Reviewed-by。
设计权衡 / 讨论推进(每条带来源 message-id)
  • v1 讨论:AMD Prateek Nayak(message-id)质疑仅按「完全空闲 core」裁剪是否覆盖所有调用路径;Peter Zijlstra(message-id)质疑 idle_cpu() 判断 sibling 影响不够严谨——推动 v2 引入 is_core_idle()。
  • v3 → v4:Vincent Guittot 回复 v3(message-id)与 Prateek 共同指出 this_cpu 检查冗余,v4 将其删除(删除后功能不变,见核心机制)。
  • Mete Durlu:v3 时提出剪枝改进(见演进脉络),v4 补上 Reviewed-by(回复 v3 的消息:message-id)。
  • v4 发布后:IBM Shrikanth Hegde 在 v4 发布后数分钟有跟进回复(message-id),讨论仍在继续。

注:v1/v2/v3 的版本规模(+N/-N)取自对应版本 lore 消息的 patch_stats,供横向对比演进幅度。

⚠️ 风险与局限

收益成立的前提
收益只在 sched_smt_active() 的超线程平台上成立;非 SMT 平台 sched_smt_active() 为假,行为与旧版完全一致(no-op 路径,选第一个空闲 housekeeping CPU)。即使在 SMT 平台上,作者也说明大多数平台 ILB 的瞬时 sibling 唤醒影响可忽略——GEMM +50% 是 NVIDIA Olympus 核「唤醒后需 ~10Ki cycles 恢复」这一特性的产物,不能直接外推到所有 SMT 平台。
生产落地影响
  • 可能的唤醒延迟 / 能耗代价:作者在 v4 说明中明确指出——偏好可能去唤醒一个完全空闲的物理 core,而非用忙碌 core 的空闲 sibling,这在某些架构上可能增加 ILB 唤醒延迟或能耗(空闲 core 需从深 idle 状态唤醒)。
  • 扫描更多 CPU:为找完全空闲 core 可能多看几个候选(正是 v2/v3 的批量跳过在压低这个代价),极端情况下选核路径变长。
  • 兜底不失效:全机器无完全空闲 core 时返回 fallback(第一个空闲 CPU),均衡推进不会停滞——这是防退化设计。
生态/兼容性
改动完全在 kernel/sched/fair.c 内部,不新增 sysctl/计数器,不改用户态 ABI;复用每-CPU select_rq_mask(依赖关中断保护),对其它子系统无接口影响。唯一需要关注的是与 nohz_full / isolcpus 组合时的行为(housekeeping 集合内选择),本补丁未改变「只在 housekeeping CPU 里选 ILB」的既有约束。
review 质疑(若有)
  • 已解决:Prateek 的调用路径覆盖质疑(→ v2 is_core_idle)、Peter 的 sibling 语义质疑(→ v2)、Mete 的逐 CPU 遍历浪费(→ v3 批量跳过)、Prateek/Vincent 的 this_cpu 冗余(→ v4 删除)。
  • 遗留观察点:v4 发布后 Shrikanth Hegde 有跟进回复(message-id),是否提出新质疑尚未见作者回应(解读(AI 分析),消息正文未能从归档取回,仅记录其存在)。
严重度:MINOR(选核偏好优化,非正确性改动;fallback 兜底保证不回归)· 落地场景:最需关注的是「收益前提 = SMT + sibling 唤醒恢复慢的核」,以及「可能增加 ILB 唤醒延迟/能耗」这两点

🔗 交叉引用

📌 关联工作
08-03 旧报告:v3 完整分析 — 本报告是它的进展更新(v3 → v4),机制与性能数据不变,差异在版本演进与评审标签
SMT-aware asymmetric CPU capacity (v5) — 同一作者(NVIDIA)的姊妹系列,解决 SMT 算力高估问题,与本补丁同属「调度器感知 SMT 拓扑」方向
🔄 版本演进链接
v1(07-28,+18/-5)→ v2(07-29,+39/-9)→ v3(07-31,+47/-9)→ v4(08-04,+44/-11),四版均由 Prateek / Peter / Mete / Vincent 的 review 逐步打磨
⚠️ 免责声明

本站内容均由 AI 基于公开知识辅助生成,仅供学习参考,请勿直接引用作为依据。作者不对信息的准确性、完整性及适用性作保证,亦不对因使用本站内容产生的任何损失承担责任。