Jelajahi Sumber

test(perf): calibrate tool continuation for hosted CI

Tianyi Cui 1 bulan lalu
induk
melakukan
2f0088357d

+ 2 - 2
.agents/notes/implemented/testing/2026-09-06-backend-continuation-performance.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-09-06-backend-continuation-performance.md
-2026-09-06-backend-continuation-performance.md: c247c7b9c4e2612603e0fb04b4f1a03b80314407
-2026-09-06-backend-continuation-performance.zh.md: bba1b4fc8a742ee261be6d09f61dd754568ebe0b
+2026-09-06-backend-continuation-performance.md: 619b583d3d0c9f035b2e78012d1a138adcc4b9f2
+2026-09-06-backend-continuation-performance.zh.md: 2008480ef4d51d1c276776043787a50aaea4e611

+ 4 - 2
.agents/notes/implemented/testing/2026-09-06-backend-continuation-performance.md

@@ -37,7 +37,7 @@ The implementation reference is `925e012340f033f0521e802ba8569ce6dd7ef1ac` on Ap
 | Tool continuation | 358.953, 324.790, 318.861, 320.119, 322.896 | 324.280, 321.952, 340.409, 325.470, 324.312 | 322.896 / 324.312 | 340 | 850 |
 | Child catalog | 318.730, 309.006, 311.404, 308.565, 310.105 | 308.670, 310.030, 280.086, 303.084, 284.829 | 310.105 / 303.084 | 320 | 800 |
 
-Continuation retains approximately 22.295 MiB; its source expectation is 23 MiB and its budget is 28.75 MiB. Request-history, tool-continuation, and SDK time expectations use the existing [calibration helper](../../../../benchmarks/support/calibration.ts): 2× shared CI time scale and 1.25× variance headroom. Memory uses only 1.25× headroom. The scale is inherited from the existing lane's calibration, not a new Linux measurement of these cases; CI evidence remains necessary when runner characteristics change. Baseline budgets protect the measured implementation; tighter budgets belong with a measured behavior-preserving fix.
+Continuation retains approximately 22.295 MiB; its source expectation is 23 MiB and its budget is 28.75 MiB. Request-history and SDK time expectations use the existing [calibration helper](../../../../benchmarks/support/calibration.ts): 2× shared CI time scale and 1.25× variance headroom. Memory uses only 1.25× headroom. The scale is inherited from the existing lane's calibration, not a new Linux measurement of these cases; CI evidence remains necessary when runner characteristics change. Baseline budgets protect the measured implementation; tighter budgets belong with a measured behavior-preserving fix.
 
 A separate plain-Node request-history CPU profile attributes 132.876 ms of sampled self time to deepFreeze called by buildRequest during a 211.300 ms operation. This identifies repeated traversal of already-frozen history as a focused investigation target, not a proven optimization result. Catalog first/repeat timings remain separate because a second listing still reads body-bearing seeded children after observations are released.
 
@@ -45,7 +45,9 @@ The shipped SDK variant completes 100 turns, 200 requests, and 800 real file rea
 
 The first Linux x64 CI measurement at commit `1dc3296eba631d51fbb3bb50e249bf3cc0fce9f6` ran on `VM-7-113-ubuntu-ci-10` with Node 24.18.1 ([run 34017868081, attempt 1, job 101444810498](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34017868081/job/101444810498)). The SDK median was 2,753.441 ms against its 4,250 ms budget, and tool-continuation retained-heap median was 22.274 MiB against 28.75 MiB. Request-history and tool-continuation time budgets failed: 785.498 ms against 550 ms and 1,077.285 ms against 850 ms, respectively. The unchanged Session-reopen open phase also failed at 31.6 ms against 30 ms. [Attempt 2, job 101447076381](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34017868081/job/101447076381) passed every benchmark on the same commit and unchanged budgets, but used `VM-7-113-ubuntu-ci-29` with Node 24.19.0. The gate runner suppressed successful child output, so that attempt supplies a passing verdict rather than raw medians. The changed runner and Node version prevent attributing the difference solely to contention or claiming stable repeated CI calibration; neither the budgets nor the shared scale are changed on this evidence.
 
-Catalog uses an explicit 900 ms expected CI duration and only the existing 1.25× headroom, yielding 1,125 ms without applying the reference-machine scale again. The standard two-CPU hosted `ubuntu-24.04` [run 34033336380, job 101487280801](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34033336380/job/101487280801) reports five unchanged-catalog totals of 797.374, 883.157, 858.364, 790.569, and 904.579 ms: median 858.364 ms exceeds the historical 800 ms budget. The 320 ms M4 expectation above remains historical evidence, not a CI measurement. This follows the explicit-CI calibration used by Session reopening (50 ms expected CI); no shared factor, other scenario budget, workload, timing endpoint, or product implementation changes. Deterministic controls use the same assertion as the measured verdict: the unrounded recorded median passes 1,125 ms and fails 800 ms, while a synthetic 1,400 ms median fails 1,125 ms. A passing run on a faster host does not calibrate the standard hosted runner.
+Catalog uses an explicit 900 ms expected CI duration and only the existing 1.25× headroom, yielding 1,125 ms without applying the reference-machine scale again. The standard two-CPU hosted `ubuntu-24.04` [run 34033336380, job 101487280801](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34033336380/job/101487280801) reports five unchanged-catalog totals of 797.374, 883.157, 858.364, 790.569, and 904.579 ms: median 858.364 ms exceeds the historical 800 ms budget. The 320 ms M4 expectation above remains historical evidence, not a CI measurement. This follows the explicit-CI calibration used by Session reopening (50 ms expected CI); shared factors, workloads, timing endpoints, and product implementations remain unchanged. Deterministic controls use the same assertion as the measured verdict: the unrounded recorded median passes 1,125 ms and fails 800 ms, while a synthetic 1,400 ms median fails 1,125 ms. A passing run on a faster host does not calibrate the standard hosted runner.
+
+Tool continuation also uses a 900 ms expected CI duration with 1.25× headroom (1,125 ms). At unchanged implementation `79c052ab29`, standard two-CPU hosted [run 34034524265, job 101490056074](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34034524265/job/101490056074) reports totals of 917.007, 892.091, 887.839, 905.659, and 898.252 ms: median 898.252 ms exceeds the historical 850 ms budget. The 340 ms M4 expectation remains historical evidence. The same measured-verdict assertion accepts the recorded unrounded median under 1,125 ms, rejects it under 850 ms, and rejects a synthetic 1,400 ms regression. Workload, timing, product code, and the 28.75 MiB retained-heap budget remain unchanged.
 
 ## Alternatives considered
 

+ 4 - 2
.agents/notes/implemented/testing/2026-09-06-backend-continuation-performance.zh.md

@@ -37,7 +37,7 @@ Status: implemented
 | 工具续聊 | 358.953, 324.790, 318.861, 320.119, 322.896 | 324.280, 321.952, 340.409, 325.470, 324.312 | 322.896 / 324.312 | 340 | 850 |
 | 子会话目录 | 318.730, 309.006, 311.404, 308.565, 310.105 | 308.670, 310.030, 280.086, 303.084, 284.829 | 310.105 / 303.084 | 320 | 800 |
 
-续聊保留约 22.295 MiB;源码期望值为 23 MiB,预算为 28.75 MiB。请求历史、工具续聊及 SDK 时间期望值使用现有[校准辅助函数](../../../../benchmarks/support/calibration.ts):2× 共享 CI 时间比例和 1.25× 波动余量。内存只使用 1.25× 余量。比例继承现有通道的校准,并非这些用例的新 Linux 实测值;runner 特征变化时仍需 CI 证据。基线预算保护实测实现;更紧预算属于有测量依据且保持行为的修复。
+续聊保留约 22.295 MiB;源码期望值为 23 MiB,预算为 28.75 MiB。请求历史及 SDK 时间期望值使用现有[校准辅助函数](../../../../benchmarks/support/calibration.ts):2× 共享 CI 时间比例和 1.25× 波动余量。内存只使用 1.25× 余量。比例继承现有通道的校准,并非这些用例的新 Linux 实测值;runner 特征变化时仍需 CI 证据。基线预算保护实测实现;更紧预算属于有测量依据且保持行为的修复。
 
 独立的纯 Node 请求历史 CPU profile 在一次 211.300 ms 操作中,将 132.876 ms 采样自身时间归因于 buildRequest 调用的 deepFreeze。这把重复遍历已冻结历史定位为聚焦调查目标,不是已证实的优化结果。目录首次/重复时间分别保留,因为观察释放后第二次列举仍读取带种子子会话的正文。
 
@@ -45,7 +45,9 @@ Status: implemented
 
 提交 `1dc3296eba631d51fbb3bb50e249bf3cc0fce9f6` 的首次 Linux x64 CI 测量使用 `VM-7-113-ubuntu-ci-10` 和 Node 24.18.1([run 34017868081,attempt 1,job 101444810498](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34017868081/job/101444810498))。SDK 中位数为 2,753.441 ms,预算为 4,250 ms;工具续聊保留堆中位数为 22.274 MiB,预算为 28.75 MiB。请求历史与工具续聊时间预算失败:分别为 785.498 ms 对 550 ms、1,077.285 ms 对 850 ms。未修改的 Session 重开 open 阶段也以 31.6 ms 对 30 ms 失败。[Attempt 2,job 101447076381](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34017868081/job/101447076381) 在同一提交和未修改预算下通过全部基准,但使用 `VM-7-113-ubuntu-ci-29` 和 Node 24.19.0。门禁运行器隐藏成功子进程的输出,因此该次运行只提供通过结论,不提供原始中位数。Runner 与 Node 版本同时变化,不能把差异仅归因于资源争用,也不能宣称已获得稳定的重复 CI 校准;这些证据不改变预算或共享比例。
 
-目录用例使用显式的 900 ms CI 期望时间,仅乘现有 1.25× 余量,得到 1,125 ms,不再应用参考机器比例。标准双 CPU 托管 `ubuntu-24.04` 的 [run 34033336380,job 101487280801](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34033336380/job/101487280801) 报告未修改目录实现的五个总时间为 797.374、883.157、858.364、790.569 和 904.579 ms:中位数 858.364 ms 超出历史 800 ms 预算。上表 320 ms M4 期望保留为历史证据,不是 CI 测量。此方法与 Session 重开使用的显式 CI 校准一致(CI 期望为 50 ms);共享系数、其他场景预算、负载、计时终点和产品实现均不改变。确定性对照与实测判定使用同一断言:未经舍入的录制中位数通过 1,125 ms 并被 800 ms 拒绝,合成的 1,400 ms 中位数则被 1,125 ms 拒绝。更快主机上的通过结果不能校准标准托管 runner。
+目录用例使用显式的 900 ms CI 期望时间,仅乘现有 1.25× 余量,得到 1,125 ms,不再应用参考机器比例。标准双 CPU 托管 `ubuntu-24.04` 的 [run 34033336380,job 101487280801](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34033336380/job/101487280801) 报告未修改目录实现的五个总时间为 797.374、883.157、858.364、790.569 和 904.579 ms:中位数 858.364 ms 超出历史 800 ms 预算。上表 320 ms M4 期望保留为历史证据,不是 CI 测量。此方法与 Session 重开使用的显式 CI 校准一致(CI 期望为 50 ms);共享系数、负载、计时终点和产品实现均不改变。确定性对照与实测判定使用同一断言:未经舍入的录制中位数通过 1,125 ms 并被 800 ms 拒绝,合成的 1,400 ms 中位数则被 1,125 ms 拒绝。更快主机上的通过结果不能校准标准托管 runner。
+
+工具续聊同样使用 900 ms CI 期望时间与 1.25× 余量(1,125 ms)。未修改实现的 `79c052ab29` 在标准双 CPU 托管 [run 34034524265,job 101490056074](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34034524265/job/101490056074) 中报告总时间为 917.007、892.091、887.839、905.659 和 898.252 ms:中位数 898.252 ms 超出历史 850 ms 预算。340 ms M4 期望保留为历史证据。与实测判定相同的断言在 1,125 ms 下接受未经舍入的录制中位数,在 850 ms 下拒绝它,并拒绝合成的 1,400 ms 回退。负载、计时、产品代码和 28.75 MiB 保留堆预算均不改变。
 
 ## 考虑过的替代方案
 

+ 2 - 2
benchmarks/agent-continuation/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write benchmarks/agent-continuation/README.md
-README.md: 0544489a5a5af14d74b349926eaa3f0c0bc9580d
-README.zh.md: fbe42a23502bdb72ca45b706dfdf345ee0690ca0
+README.md: 7c5ed357e2089f6d7238667b3b8310570983c77c
+README.zh.md: bcbbff353e9faf5e181e1e75896200363f5c626b

+ 1 - 1
benchmarks/agent-continuation/README.md

@@ -18,7 +18,7 @@ Measure long-history request processing, cold tool-heavy continuation, and repea
 
 From the repository root, build the libraries and workers with `pnpm run build:bench`, then run `pnpm exec vitest run --config vitest.bench.config.ts benchmarks/agent-continuation/agent-continuation.bench.ts`. Do not overlap timing runs with builds or other benchmarks.
 
-The test reports all five fresh-process samples and enforces reviewed median budgets. Catalog uses a 900 ms standard hosted CI expectation with 1.25× headroom (1,125 ms); other time budgets use reference-machine scaling. A failed worker reports its exit, signal, timeout, and stderr; temporary roots are removed even on failure. The required benchmark lane discovers this file automatically.
+The test reports all five fresh-process samples and enforces reviewed median budgets. Catalog and tool continuation each use a 900 ms standard hosted CI expectation with 1.25× headroom (1,125 ms); other time budgets use reference-machine scaling. A failed worker reports its exit, signal, timeout, and stderr; temporary roots are removed even on failure. The required benchmark lane discovers this file automatically.
 
 <a id="measurements"></a>
 

+ 1 - 1
benchmarks/agent-continuation/README.zh.md

@@ -18,7 +18,7 @@
 
 在仓库根目录使用 `pnpm run build:bench` 构建库和 worker,然后运行 `pnpm exec vitest run --config vitest.bench.config.ts benchmarks/agent-continuation/agent-continuation.bench.ts`。不要让计时运行与构建或其他基准重叠。
 
-测试报告全部五个新进程样本,并约束经审查的中位数预算。目录用例使用标准托管 CI 的 900 ms 期望值与 1.25× 余量(1,125 ms);其他时间预算使用参考机器缩放。worker 失败时报告退出状态、信号、超时和 stderr;失败时也会删除临时根目录。必需基准通道自动发现此文件。
+测试报告全部五个新进程样本,并约束经审查的中位数预算。目录和工具续聊用例均使用标准托管 CI 的 900 ms 期望值与 1.25× 余量(1,125 ms);其他时间预算使用参考机器缩放。worker 失败时报告退出状态、信号、超时和 stderr;失败时也会删除临时根目录。必需基准通道自动发现此文件。
 
 <a id="measurements"></a>
 

+ 19 - 3
benchmarks/agent-continuation/agent-continuation.bench.ts

@@ -14,14 +14,17 @@ import { WORKLOAD } from './workload.ts'
 const ATTEMPTS = 5
 const WORKER_TIMEOUT_MS = 60_000
 /** M4 Pro / Node 24.19 baseline expectations, before shared CI scaling and variance headroom. */
-const EXPECTED_MS = { 'request-history': 220, 'tool-continuation': 340, 'profile-continuation': 1_700 } as const
+const EXPECTED_MS = { 'request-history': 220, 'profile-continuation': 1_700 } as const
+/** Standard two-CPU hosted CI tool-continuation median is 898.252 ms. */
+const EXPECTED_TOOL_CONTINUATION_CI_MS = 900
+const TOOL_CONTINUATION_BUDGET_MS = Math.ceil(EXPECTED_TOOL_CONTINUATION_CI_MS * PERFORMANCE_BUDGET_HEADROOM)
 /** Standard two-CPU hosted CI catalog median is 858.364 ms; 900 ms is the rounded expectation. */
 const EXPECTED_CATALOG_CI_MS = 900
 const CATALOG_BUDGET_MS = Math.ceil(EXPECTED_CATALOG_CI_MS * PERFORMANCE_BUDGET_HEADROOM)
 const EXPECTED_RETAINED_HEAP_MB = 23
 const WORKERS = join(import.meta.dirname, '..', '.dsh-build', 'agent-continuation')
 
-type Scenario = keyof typeof EXPECTED_MS | 'catalog'
+type Scenario = keyof typeof EXPECTED_MS | 'catalog' | 'tool-continuation'
 type Report = ContinuationReport | CatalogReport | ProfileReport
 
 function workerName(scenario: Scenario): string {
@@ -66,6 +69,18 @@ describe('standard hosted catalog calibration', () => {
   })
 })
 
+describe('standard hosted tool-continuation calibration', () => {
+  it('accepts recorded two-CPU samples but rejects a material regression', () => {
+    const recordedMedian = median([917.006744, 892.091482, 887.838867, 905.6594390000001, 898.2517579999999])
+
+    expect(recordedMedian).toBe(898.2517579999999)
+    expect(() => expectTotalWithinBudget(recordedMedian, 850)).toThrow()
+    expectTotalWithinBudget(recordedMedian, TOOL_CONTINUATION_BUDGET_MS)
+    expect(TOOL_CONTINUATION_BUDGET_MS).toBe(1_125)
+    expect(() => expectTotalWithinBudget(1_400, TOOL_CONTINUATION_BUDGET_MS)).toThrow()
+  })
+})
+
 describe('continuing tool-heavy Sessions with large histories', () => {
   let scratch: string | undefined
   const sources = new Map<Scenario, string>()
@@ -94,7 +109,8 @@ describe('continuing tool-heavy Sessions with large histories', () => {
         finally { await rm(root, { recursive: true, force: true }) }
       }
       const totalMs = samples.map(sample => sample.totalMs)
-      const budgetMs = scenario === 'catalog' ? CATALOG_BUDGET_MS : ciTimeBudget(EXPECTED_MS[scenario])
+      const budgetMs = scenario === 'catalog' ? CATALOG_BUDGET_MS
+        : scenario === 'tool-continuation' ? TOOL_CONTINUATION_BUDGET_MS : ciTimeBudget(EXPECTED_MS[scenario])
       const retainedHeapBudgetMb = EXPECTED_RETAINED_HEAP_MB * PERFORMANCE_BUDGET_HEADROOM
       console.log(JSON.stringify({
         benchmark: 'agent-continuation/' + scenario, workload: WORKLOAD,