sched discussion
[PATCH] sched/fair: Prefer waker CPU for reciprocal sync wakeups
LLM 分析
sched/fair:互反 sync 唤醒优先留在 waker CPU
系列概况
| 字段 | 内容 |
|---|---|
| 标题 | [PATCH] sched/fair: Prefer waker CPU for reciprocal sync wakeups |
| 作者 | Shubhang Kaushik (Ampere) |
| 版本 | v1(单 patch) |
| 规模 | 1 file changed, 30 insertions(+), 1 deletion(-) |
| 修改文件 | kernel/sched/fair.c |
| 基线 | v7.2-rc4(mainline origin/master @ b95f03f04d47) |
| Message-ID | 20260721-b4-sched-sync-wakeup-v1-1-dc94f184e27f@gentwo.org |
| 完整性 | 标题、commit message、diffstat、性能数据完整;含 Christian Loehle 的两轮评审,作者在 message 3 已承认两项需修改 |
补丁目的
pipe 类两进程轮流传数据负载会形成稳定 ping-pong(A 唤醒 B,B 再唤醒 A…)。当前 select_task_rq_fair() 在 WF_SYNC 唤醒时倾向把 wakee 投到"看起来更闲"的 CPU,让 task 对反复跨越 runqueue,cache 冷启动与迁移成本压过 idle 收益。
补丁要做的:识别 narrow 互反同步唤醒,让 wakee 继续留在 waker CPU,绕过 wake_affine + select_idle_sibling 的强制再分布。
旧流程的问题
A (running) on cpu0 idle CPU = cpu2
| ^
| wake B (WF_SYNC) |
v |
select_task_rq_fair() ------ pick cpu2 ------+
|
v
B migrates to cpu2 -> cache cold, ping latency high
|
B wakes A -> A pushed to cpu3 by wake_affine / load_balance
|
cross-CPU handoff cost dominates pipe throughput
只要 B 落到空闲 CPU,下一轮 A 又会被 SDF_WAKE_AFFINE 推到旁边。两 task 在两颗 CPU 间来回弹跳。
新流程
A (running) on cpu, p->last_wakee == current (B)
|
| wake B (WF_SYNC, !wake_wide)
v
select_task_rq_fair()
|
| sync && !wide &&
| READ_ONCE(p->last_wakee) == current &&
| prefer_sync_pair_cpu(p, cpu)
| yes
+-----------> return cpu (Christian points out:
this branch must move
BELOW SD_WAKE_AFFINE
check, otherwise it
bypasses isolcpus)
| no
v
want_affine + select_idle_sibling (existing path)
prefer_sync_pair_cpu() 三道闸门:
static bool prefer_sync_pair_cpu(struct task_struct *p, int cpu)
{
struct rq *rq = cpu_rq(cpu);
/* (1) waker CPU must have no other runnable fair task */
if ((rq->nr_running - cfs_h_nr_delayed(rq)) != 1)
return false;
/* (2) wakee must be allowed on this CPU */
if (!cpumask_test_cpu(cpu, p->cpus_ptr))
return false;
/* (3) on asymmetric-capacity systems, wakee must fit */
if (sched_asym_cpucap_active()) {
sync_entity_load_avg(&p->se);
if (!task_fits_cpu(p, cpu))
return false;
}
return true;
}
关键实现
调用点在 select_task_rq_fair(),改造片段:
int wide = 0;
...
if (wake_flags & WF_TTWU) {
record_wakee(p); /* record who woke current */
wide = wake_wide(p); /* hoist, avoid recomputation */
...
}
...
/* NOTE: per Christian's review, this must move BELOW the
* SD_WAKE_AFFINE check; early-return here bypasses isolcpus */
if (sync && !wide &&
READ_ONCE(p->last_wakee) == current &&
prefer_sync_pair_cpu(p, cpu))
return cpu;
want_affine = !wide && cpumask_test_cpu(cpu, p->cpus_ptr);
record_wakee() 前一轮 try_to_wake_up 已经写过 p->last_wakee;再用 READ_ONCE 读回对比 current(waker),就把"A↔B 互为唤醒"恢复出来。wake_wide() 基于前几次 wakeup 间隔判定是否 wide,本补丁取其反向信号。
类比
把调度器想成乒乓球馆里的球童。两人对打时,球童不该每次都把球送隔壁空球桌——搬运时间反而盖过打球时间。同一张球桌继续打最快:先确认这张桌没别人在用(nr_running - cfs_h_nr_delayed == 1),再确认对方能在这张桌上打(cpus_ptr 检查),再确认桌子的尺寸装得下(不对称算力检查)——满足就保留,不满足才去找空桌。
Highlight:风险与注意点
WF_SYNC覆盖范围:commit message 写"futex ping-pong",但 Christian 指出 futex 唤醒通常不带WF_SYNC,实际不命中 fast path。message 中要么把覆盖范围限定在"kernel 内部同步原语",要么给出真实命中集合。- early-return 位置错误会绕开 isolcpus:新分支必须后移到
SD_WAKE_AFFINE之后;当前实现先 short-circuit,会无视isolcpus=/ cpuset 钉死的 CPU 列表,破坏部署策略。Christian 明指,作者已在 message 3 确认要挪位。 nr_running - cfs_h_nr_delayed == 1偏窄:仅统计 CFS、扣除 delayed 任务,没算 RT、IRQ、kworker。若 waker CPU 挂着 RT 而 CFS 视图"剩一个",唤醒后实际争用仍存在;可考虑rq->nr_running == 1整盘判定或额外排除 RT。wake_wide()启发式滞后:依赖前几次 wakeup 间隔统计,对突变敏感;ping-pong 节奏一旦被破就退回老路径,性能回退呈阶跃式而非平滑。- 跨 LLC / NUMA 不感知:补丁对同 LLC 上的 pair 友好;跨 LLC 或跨节点时 cache 收益很快被摊薄,目前没有按拓扑距离的二次过滤。
- 测试覆盖偏窄:仅在 80 核 Ampere Altra 上跑
perf bench sched pipe(+21%),hackbench/schbench/SPECjBB 测无回归。还需在 SMT、小核混合、不同 cache 拓扑下复测,避免只在 Altra 上"看起来好"。 - 与 wake_affine 既有机制的张力:是少在原 CPU 的反向偏好,与
select_idle_sibling在 narrow pair 上互为制约,需要互斥策略文档化以免被并入 future task 时语义漂移。
版本变化
仅有 v1。作者在 message 3 已承认 futex 范围描述偏宽、early-return 位置需后挪;可预期 v2 调整实现位置、改写 commit message,并把 wide = wake_wide(p) 改成 READ_ONCE()。
一句话总结
这是一条 bugfix 性质的提交:在 narrow 互反 WF_SYNC 唤醒中让 wakee 留在 waker CPU、对抗 ping-pong handoff cost,但评审指出 futex 实际不带 WF_SYNC、early-return 必须后移以免绕过 isolcpus。primary_category = bugfix 因其修正"反复迁移导致 latency 退化"这一可重现性能缺陷;is_important = true 因为涉及核心 select_task_rq_fair 路径且已有 maintainer 评审跟进。