0/8 已展开

LLM 分析

sched/fair:偏向 waker CPU 的 reciprocal sync wakeup 优化

系列概况

  • 标题: [PATCH v2] sched/fair: Prefer waker CPU for reciprocal sync wakeups
  • 作者: Shubhang Kaushik (Ampere Computing)
  • 版本: v2(thread 末尾出现 v3 链接 20260727-b4-sched-sync-wakeup-v3-1-90cf481dbd85@gentwo.org
  • 规模: 单 patch 系列,1 文件,+28 / -0
  • 修改文件: kernel/sched/fair.c
  • 代码统计: 新增辅助函数 prefer_sync_pair_cpu()(23 行)+ select_task_rq_fair() 内 5 行 early-return
  • Message-ID: 20260722-b4-sched-sync-wakeup-v2-1-f1164560b24b@gentwo.org
  • 完整性: thread 共 8 封邮件,覆盖 AMD/ARM/IBM 三位 reviewer 的 4 轮反馈与作者的 3 轮回应,节奏清晰、信息齐全

补丁目的

解决 pipe-style ping-pong 工作负载被 handoff 成本主导的性能问题。
两个任务 A、B 互相以 WF_SYNC 唤醒(A→B→A→B…)时,调度器仍然走 select_idle_sibling() 把 wakee 拉到"看似空闲"的远端 CPU,反而带来更高的迁移/cache 重建开销。

目标:在 SD_WAKE_AFFINE 域允许的前提下,对窄 reciprocal WF_SYNC 唤醒直接把 wakee 留在 waker CPU。
实测:80 核 Ampere Altra 上 perf bench sched pipe -l 1000000 平均提升约 30%,hackbench / schbench / SPECjBB 无明显回归。

旧流程的问题

SD_WAKE_AFFINE 分支只是给 wake_affine() 算一个 affine target,
最终仍会进入 select_idle_sibling(),后者只要发现一个 idle CPU 就会覆盖 affine 选择。
在 ping-pong 场景下,"找一个空闲 CPU"的代价(IPI、cache miss)远大于"共用 runqueue"。

新流程

识别窄 reciprocal WF_SYNC 唤醒(A 唤醒 B,紧接着 B 又唤醒 A),仅当满足三个条件时直接返回 waker CPU:

  1. waker CPU 上"恰好只有 waker 这一个 CFS 任务";
  2. wakee 允许在该 CPU 上运行;
  3. asym_cpucap 系统上 wakee 也能 fit 该核容量。

不满足就继续走原有 wake_affine() / select_idle_sibling() 路径。

Patch 概览

唯一 hunk 落在 select_task_rq_fair()SD_WAKE_AFFINE 入口,插入一次 early-return:

if (sync &&
    READ_ONCE(p->last_wakee) == current &&
    prefer_sync_pair_cpu(p, cpu))
    return cpu;

同时新增 prefer_sync_pair_cpu() 辅助函数。

关键实现

static bool prefer_sync_pair_cpu(struct task_struct *p, int cpu)
{
    struct rq *rq = cpu_rq(cpu);

    if ((rq->nr_running - cfs_h_nr_delayed(rq)) != 1)
        return false;

    if (!cpumask_test_cpu(cpu, p->cpus_ptr))
        return false;

    if (sched_asym_cpucap_active()) {
        sync_entity_load_avg(&p->se);
        if (!task_fits_cpu(p, cpu))
            return false;
    }

    return true;
}

逐条解读:

  • nr_running - cfs_h_nr_delayed(rq) == 1:剔除 deadline/delayed 等非 fair 任务后,waker CPU 上只剩 waker 一个可运行 CFS 任务,这是"没人抢"的硬性条件。
  • cpumask_test_cpu():常规 affinity 兜底;Prateek 指出挪进 SD_WAKE_AFFINE 分支后已被 want_affine 覆盖,可删。
  • sched_asym_cpucap_active():大/小核系统上需要先把 PELT 同步到当前时间,再判断任务"能不能 fit",避免重任务被塞到小核。
  • 调用点的 READ_ONCE(p->last_wakee) == current:识别 reciprocal 模式;Prateek 认为 p 此时已离队,last_wakee 稳定,READ_ONCE 多余。

类比

把 A、B 想象成乒乓球双打搭档:
旧调度器每次击球都换球桌(迁去"看似空闲"的 CPU),但来回跑路比两人共用一张桌子慢得多。
新调度器识别出"我刚给你传球,你马上又传回来"这种 reciprocal 模式后,把两人固定在一张桌子上——只要没人抢,就不换桌。

ASCII 流程图:

   Pipe workload:
     A on cpu0  --WF_SYNC-->  B on cpu1
     ^                          |
     |------WF_SYNC-------------|   (reciprocal, ping-pong)

   select_task_rq_fair(p=Wakee, prev_cpu=waker_cpu)
     |
     +-- SD_WAKE_AFFINE branch ---------------+
     |   if (sync &&                          |
     |       p->last_wakee == current &&      |  <-- reciprocal?
     |       prefer_sync_pair_cpu(p, cpu))    |
     |       return cpu;        (STAY)        |
     |   else                                 |
     |       wake_affine() -> SIS             |
     |           (may pick a different idle)  |
     +---------------------------------------+

   prefer_sync_pair_cpu(p, cpu):
     rq = cpu_rq(cpu)
     ok  = (nr_running - delayed) == 1          <-- only waker here
     ok &= cpumask allows p
     ok &= !asym || task_fits_cpu(p, cpu)
     return ok

Highlight:风险与注意点

  • SMT 语义不匹配:Vineeth 指出 POWER10/11 是 SMT8,把一对任务都堆在同一条线程上、7 个兄弟线程空转并共享 LLC,反而是浪费。该 patch 在 SMT 场景下未做特别处理。
  • 可清理项READ_ONCE(p->last_wakee) 在 p 离队后稳定,可去掉;affinity 检查被 want_affine 覆盖,可删除。
  • SIS 改造路径分歧:Prateek 建议把 early-return 挪进 select_idle_sibling();Shubhang 担心这样会丢掉 v2 修复的 SD_WAKE_AFFINE/isolcpus 语义,并且 SIS 改动若只覆盖 SMT 路径,Altra 这种非 SMT 收益会消失。
  • WA_IDLE 相似性:helper 的结构风格与 WA_IDLE wake-affine idle 路径高度相似,未来有合并的可能。
  • 验证缺口:thread 缺 SMT 上的 perf bench sched pipe 数据,Christian 表示愿意补跑;v3 应在合并前给出 Altra 之外平台的回归数据。

版本变化

  • v1 → v2:把 reciprocal handoff 偏好移到既有 SD_WAKE_AFFINE 域检查之内,避免绕过 isolcpus 等拓扑约束;去掉 changelog 里对 futex 的提及;重新跑 perf bench sched pipe 数据。
  • thread 内 v3 预告:作者计划采纳 Prateek 的清理(去掉 READ_ONCE 和冗余 affinity 检查),并把范围限定在非 SMT 路径;同时等待 Christian/Prateek 的 SMT 跑分,再决定是否在本系列里补 SMT 处理。

一句话总结

对窄 reciprocal WF_SYNC 唤醒,让 wakee 留在 waker CPU,可把 pipe-style 负载提速约 30%;代码清理、SMT 适用性与 SIS 改造路径是 v3 的主要拉锯点。