0/6 已展开

LLM 分析

sched/fair:保留非 SMT 互反同步唤醒的 wake-affine CPU(v4)

系列概况

  • 标题:[PATCH v4] sched/fair: Preserve wake-affine CPU for non-SMT reciprocal sync wakeups
  • 作者:Shubhang Kaushik (Ampere) sh@gentwo.org
  • 版本:v4(迭代自 v1/v2/v3)
  • 规模:单 patch
  • 修改文件kernel/sched/fair.c
  • 代码统计:1 file changed, 26 insertions(+)
  • Message-ID20260803-b4-sched-sync-wakeup-v4-1-52333b0cfb79@gentwo.org
  • 完整性:包含完整 commit message、diff、性能测试数据、版本演进说明;基线 commit 5186ef36909c(tip:sched/core);thread 包含 6 封邮件(1 patch + 5 reply),讨论覆盖 SMT 路径、WF_SYNC 语义、基准覆盖面

补丁目的

CFS wakeup 路径里 wake_affine() 可能选中 waker CPU 作为目标,但接下来 select_idle_sibling() 又会去搜索空闲 CPU。pipe ping-pong 这种 A 与 B 互相唤醒对方的窄互反场景,把 wakee 挪走反而比贴住 waker CPU 更贵(cache miss、IPI、跨核搬运)。本 patch 限定非 SMT 系统上当 waker rq 上没有其他 fair 任务时,让 select_task_rq_fair() 在 idle-sibling 之前直接返回 waker CPU。

旧流程的问题

对 WF_SYNC 唤醒:

  1. wake_affine() 选中 waker CPU(亲和路径已经算出 wake_affine_idle)。
  2. 控制流继续进入 select_idle_sibling(),把目标换成搜索到的 idle CPU。
  3. A <-> B 互反唤醒里 wakee 本来就贴 waker 跑,被挪走导致 cache 失效、runqueue 重新 attach、IPI 等待。

新流程

WF_SYNC wakeup
      |
      v
wake_affine() picks waker CPU
      |
      v
select_task_rq_fair()
      |
      |-- sync && !SMT && new_cpu == cpu
      |   && p->last_wakee == current
      |   && prefer_sync_pair_cpu(p, cpu) ---> return cpu
      |
      +-- otherwise continue select_idle_sibling()

判定条件:

  • sync(WF_SYNC)
  • !sched_smt_active()(非 SMT 系统)
  • p->last_wakee == current(A <-> B 互反)
  • prefer_sync_pair_cpu() 真:
    • waker rq 上 (nr_running - cfs_h_nr_delayed) == 1(无其他 fair 任务)
    • sched_asym_cpucap_active(),还需 task_fits_cpu(p, cpu)

Patch 概览

新增 helper prefer_sync_pair_cpu() 并在 select_task_rq_fair() 早返回 waker CPU;不动 wake_affine()、不动 SMT 路径、不动通用 WF_SYNC 路径。

关键实现

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 (sched_asym_cpucap_active()) {
        sync_entity_load_avg(&p->se);
        if (!task_fits_cpu(p, cpu))
            return false;
    }

    return true;
}

调用点(位于 select_task_rq_fair() 中 wake_affine 已选/保留 waker CPU 之后):

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

性能数据(80 核非 SMT Ampere Altra,tip:sched/core 5186ef36909c 基线,perf bench sched pipe -l 1000000,20 轮):

  • default:均值 3.985 -> 3.187 usec/op(约 20% 提升),中位数 4.026 -> 3.181(约 21%)
  • taskset -c 78,79:均值约 18.4%、中位数约 17.4% 提升
  • taskset -c 79:均值慢约 1.9%、中位数慢约 2.1%(单 CPU,噪声范围)
  • hackbench 1/2/4/8 组 process/thread pipe 在 -1.8% 至 +3.7% 噪声内;schbench 8/40/80/240 normal 与 1/2/4/8 pipe 无显著退化

类比

把 CPU 想象成办公桌,pipe ping-pong 是 A 与 B 不断来回传纸条:

  • 旧流程:办公室主任(wake_affine)说"给离 B 最近的桌子",紧接着前台(select_idle_sibling)看到有更空的桌子,又把 B 挪过去。每次换桌都要重摆文具、重认路、cache 冷启动。
  • 新流程:前台先确认"这张桌子确实只剩 A 在用"且没有 SMT 双胞胎时,才把 B 固定在 A 桌边。SMT 系统相当于同桌两人,原规则继续由前台找空位处理。

另一个类比:WF_SYNC 像接力赛传棒,下一棒的人贴着接棒人起跑比跑到下一条空闲赛道更快。新 patch 只在确认是一对一的接力(A <-> B)且非 SMT 时才强制贴棒。

Highlight:风险与注意点

  1. WF_SYNC 语义模糊:Prateek 指出 anon_pipe_read()wake_up_interruptible_sync_poll() 唤醒写者,但读者拿到数据后未必立刻阻塞;networking 代码也各处乱用,导致依赖 waker/wakee 是否同节点、wake_wide() 结果产生不一致延迟/吞吐。
  2. 文档化优先于局部 fix:Shrikanth 主张先在 Documentation/scheduler/ 把 WF_SYNC 语义写清楚(hint-only vs waker-only-task 强制),否则"一个 benchmark 受益、一个受损"的循环不会停。
  3. SMT 路径被刻意跳过:v3/v4 把优化限制在 !sched_smt_active()。Shrikanth 质疑 SMT 是否真有本质差异——若 SMT 上也跑 pipe ping-pong,问题应同样存在。
  4. 版本节奏过快:Shrikanth 批评 v3 回复未到即发 v4,且 v4 没有解决 v3 的核心问题(语义文档化、SMT 路径、基准覆盖面)。
  5. 单 CPU 略退化taskset -c 79 出现约 2% 退化,虽属噪声,但提示 patch 在某些场景下并非单调更优。
  6. 基准覆盖面窄:只跑 pipe/hackbench/schbench,未覆盖 messaging。Prateek 提议跑 perf bench sched messaging(threads + pipes)以验证 SMT 与多 LLC 场景。
  7. 维护者态度:Peter、Ingo、Vincent、Mel 等被点名叫来表态——本 thread 的关键瓶颈不是 patch 本身,而是上层 WF_SYNC 语义未定。

版本变化

  • v4 -> v3
    • 仅在 wake-affine 路径已选/保留 waker CPU 时才偏好 waker CPU
    • 明确 WF_SYNC 仍是 hint,不是通用 placement 规则
    • SMT 系统保留走 select_idle_sibling()
    • 基于 tip:sched/core 重测
  • v3 -> v2
    • 把"非 SMT 偏好 waker CPU"限制在 !sched_smt_active()
    • 去掉冗余 affinity 检查(want_affine 已检查)
    • 用普通 p->last_wakee 读取代 READ_ONCE()
    • 在 v7.2-rc5 基础上 rebase
  • v2 -> v1
    • 把互反 handoff 偏好移到现有 SD_WAKE_AFFINE 域检查下
    • 从 changelog 动机里去掉 futex
    • rebase 后刷新 pipe 性能数据

一句话总结

v4 在非 SMT 系统的窄互反 WF_SYNC 唤醒中保留 waker CPU、绕过 idle-sibling 搜索;维护者更关心的是 WF_SYNC 整体语义未定,应先在 Documentation/scheduler/ 文档化再谈局部优化。