Skip to content

[perf] 证据驱动的性能改进总跟踪 #121

Description

@SunSi12138

目标

建立一套先证明问题、再允许实现的性能改进流程。这个跟踪项不预设任何优化一定值得做,也不把“代码看起来有 allocation / timer / scan / atomic / copy / 大方法”当成性能缺陷。

执行基线必须以实际工作分支 dev 的当前 HEAD 为准;开始任何测量或实现前都记录:

git rev-parse HEAD
dotnet --info
uname -a

不得使用历史 main@201b162... 或某个旧 dev SHA 作为当前实现事实。历史 baseline 只能用于决定“值得重新测什么”,不能代替当前 Go/No-Go。


第一性原理

任何性能优化必须依次回答以下问题:

  1. 用户真正付出的成本是什么? CPU、managed allocation、GC、tail latency、吞吐、memory bandwidth、scheduler wakeup、lock/cache contention、JIT/instruction footprint,必须至少有一个可观测量。
  2. 成本发生在目标 workload 吗? synthetic benchmark 中存在不等于真实 RPC 会频繁发生。
  3. 成本能否归因到具体机制? 不能只看到总 QPS 下降就猜原因;需要 profiler、allocation stack、事件计数、对照组或隔离 benchmark。
  4. 候选方案减少的是总成本还是移动成本? 少一个 allocation 但多一个 lock、少一次 scan 但每请求多维护 scheduler node、少一次 copy 但多很多 syscall,都可能是负优化。
  5. 正确性是否比性能更重要? frame 顺序、deadline 不提前、single terminal、capacity hard bound、shutdown、flow-control、trace/context、用户 interceptor/context 可观察语义都不能为了数字放宽。
  6. 结果是否可复现? base/head 必须同机交替,多轮运行;小于噪声的变化不得宣称收益。
  7. 能否独立回退? 测量、candidate、correctness hardening 分 commit;失败时撤回 candidate,保留有价值的测量基础设施。
  8. 是否已经有别的 issue 拥有这个状态/成本? 先检查 Runtime Architecture、现有 perf issue 和正在合并的 PR;不能重复重构即将被删除的状态。

因此每个调查只有两类合法结果:

No-Go: 证据不足 / 收益不足 / 风险过高 -> 记录结论并停止
Go: 成本已证明且候选达到预先门槛 -> 另开独立实现 issue/PR

测量 issue 本身不以产生 production optimization 为完成条件。得出可信的 No-Go 同样算完成。

#129#134 的正文为了让低能力实现者不在 Go 后重新猜设计,提前包含了 candidate、correctness、rollback 实施手册;这些 candidate 章节是条件式手册,不是实施授权。调查达到 Go 后,仍按本总跟踪规则创建具体 implementation issue,并把已经命中的 candidate 章节复制/收敛过去;No-Go 时不得因为手册已经写好就修改 production。


依赖规则

正式性能数据硬前置:#122

#122 是其他调查做正式 macro Go/No-Go 判断的硬前置。

原因:旧 LoadTest 的共享 latency histogram 会在每个成功请求上执行多个原子操作,并且正式 histogram 与 realtime histogram 同时记录。若 recorder 本身对 c128/c512 有明显干扰,后续 1%-5% 的差异没有决策价值。

所有调查在 #122 完成前可以提前完成:

  • correctness inventory;
  • instrumentation 设计;
  • microbenchmark;
  • profiler/allocation stack;
  • IL/native disassembly;
  • test-only evidence runner;
  • throwaway prototype 的局部验证。

但最终正式 macro gate 必须使用 #122 的 formal recording 模式和新 baseline。

架构依赖

部分性能问题必须等架构 owner 稳定后才能实施最终 candidate:

依赖图

#122 可信性能工具与基线
  ├──> #124 CallContext / Metrics / Tracing 正式 Go/No-Go
  ├──> #125 SendPump timed batching 正式 Go/No-Go
  ├──> #126 Deadline Scheduler 正式 Go/No-Go
  ├──> #129 Default Unary baseline cost 正式 Go/No-Go
  ├──> #130 Server immediate admission 正式 Go/No-Go
  ├──> #131 Client retry/admission/breaker attempt-state 正式 Go/No-Go
  ├──> #132 Client/Server interceptor pipeline 正式 Go/No-Go
  ├──> #133 outbound frame staging copy 正式 Go/No-Go
  └──> #134 Server dispatch hot/cold layout 正式 Go/No-Go

#81 counter/terminal ownership
  ├──> #129 production candidate re-audit
  ├──> #130 production candidate re-audit
  ├──> #131 production candidate re-audit
  └──> #134 final hot-path rebaseline

#124 CallContext/Telemetry
  └──> #132 必须排除/避免重复归因 AsyncLocal/Telemetry 成本

#125 SendPump timer coordination
  └──> #133 必须把 timer/batching coordination 与 memcpy 成本分开

#84 engine/public surface
  ├──> #132(仅当 candidate 触及 generated/runtime boundary)
  ├──> #133 public transport capability(若 Go)
  └──> #134 final layout rebaseline

#84 -> #133(public SPI only, if Go) -> #86

#129 / #130 / #132 若先产生已验证 production 改动
  └──> #134 必须在它们合并后的 dev 上重新采集 native layout

任一调查 Go
  └──> 新建该具体机制的 implementation issue

任一调查 No-Go
  └──> 记录证据并关闭,不创建实现 issue

与现有性能 issue 的关系

现有:

它们与 #124#134 不重复。

#122 落地后,#90#93 如需真实 workload macro 证据,应优先使用新的 formal recording 模式;不要求为此重写它们已经正确的第一性原理或 correctness 门禁。

#92 已经证明“减少 copied bytes”本身不是合并理由:更激进 growth policy 即使局部更快,只要造成不可接受的 capacity waste,也应拒绝。这个判例同样适用于 #133 的 zero-copy/gather 实验:总 CPU/latency/memory/syscall 才是最终目标。


当前明确不立项的方向

Endpoint Selection

当前 dev 已有 EndpointSelectionKernel 和 endpoint-count/strategy benchmark,既有 evidence 未显示 endpoint 数量产生稳定的整体性能方向性退化。因此现在不创建 Endpoint Selection / atomic reservation 优化 issue

只有未来 profiler/hardware counters 证明 selection 或 exact global counter 占目标 workload 总 CPU至少约 3%-5%,才重新立项;不能因为存在 Interlocked 就进行 reservation/LongAdder 类实验。

Heartbeat shared scheduler

当前不创建共享 heartbeat scheduler / timer-wheel 类性能 issue。连接与 cluster 资源已有明确 hard bound,目前没有证据证明 per-connection heartbeat timer/task 是目标 workload 的瓶颈。只有未来大量 MultiCluster × idle connection 场景的 profiler/resource evidence 命中,再独立立项。


调查 issue

性能测量基础

已有重点调查

新增:默认与可选能力固定成本

新增:数据搬运与代码布局


通用测量纪律

基线与 candidate

正式比较必须:

  • 固定同一自托管机器;
  • 固定 CPU governor / runtime / SDK;
  • base/head 同机交替至少 5 轮;
  • alternating order,避免温度/JIT/后台负载方向性偏差;
  • 记录 exact SHA;
  • 记录 raw samples,不只保留平均值;
  • 同时看 throughput、P50/P99/P99.9、CPU/op、allocated B/op、failure count;
  • 热点归因需要 profiler/allocation/hardware counter 中至少一种适合该假设的证据。

不得:

  • base 在一台机器、head 在另一台机器;
  • 用一次 QPS 结果判定;
  • 只看 mean 不看 tail;
  • 为让 candidate 获胜而改变 payload、concurrency、transport 或 recorder 模式;
  • 把 warmup/JIT/first-call 计入 steady-state(除非 issue 专门测 startup);
  • 把 diagnostic instrumentation 留在正式 timing path 后仍宣称 production performance;
  • 因为 candidate 代码已经写好就降低 Go/merge 门槛。

正确性优先

任何 candidate 出现以下任一情况立即 No-Go / revert:

  • wire bytes / frame ordering 改变;
  • deadline 提前触发或遗漏;
  • single terminal 被破坏;
  • hard capacity overshoot;
  • shutdown/task/timer 泄漏;
  • flow-control credit 泄漏;
  • stale callback 命中新 lifecycle;
  • buffer/pooled owner early return、double return 或 use-after-return;
  • trace/context/interceptor 可观察语义在未明确设计的情况下改变;
  • retry/admission/breaker outcome/report exactly-once 被破坏;
  • NativeAOT 或支持 transport 出现 correctness regression。

实现 issue 创建规则

测量 issue 达到 Go 条件后,不要直接在同一个 PR 顺手改 production。先在测量 issue 中固定:

  • baseline SHA;
  • profiler/evidence;
  • candidate 假设;
  • 预期收益;
  • correctness invariants;
  • 预先定义的 merge/revert gate。

然后另开实现 issue,标题描述具体机制,而不是笼统“optimize X”。

#129#134 中已经写好的 candidate 手册应作为实现 issue 的输入,但只复制被证据选中的那一部分;不要把所有候选一次实现。

如果测量 No-Go,则关闭调查,不创建实现 issue。


总完成定义

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions