Skip to content

perf: /artifacts 为每个 artifact 重扫会话导致二次方耗时 #1346

Description

@ai-yang

问题概述

src/commands/artifacts/scanner.ts 每发现一个 artifact tool_use,都会调用 findToolResult(messages, toolUseId) 从会话开头重新扫描全部消息。若会话有 N 条消息和 A 个 artifact,复杂度为 O(A × N);artifact 数量随会话增长时会退化为 O(N²)

这会直接影响长会话打开 /artifacts 列表的响应时间。

环境

  • 代码版本:3bb6b5746238c418138eb96d57765d79012edd96
  • Bun:1.3.13
  • 系统:Linux 5.15.0-186-generic x86_64

对比方案

候选实现保留首个 artifact 的直接查找路径;检测到第 2 个 artifact 后,仅一次遍历用户消息,为 tool_result 建立 first-result-wins 的 Map,后续按 tool_use_idO(1) 查找。结果顺序、重复 ID、result 先于 use、数组 content、error 和 unmatched 行为均保持不变。

基准方法

  • 每个场景先做 60 对交替预热。
  • 3 个独立 trial;每个 trial 120 轮。
  • 每个 trial 严格包含 60 次“旧→新”和 60 次“新→旧”,再按固定 seed 洗牌,避免顺序偏差。
  • 每个实现、每个场景共 360 个样本;短场景使用 batching。
  • 使用 Bun.nanoseconds();报告聚合 median 和 p95。
  • 每轮都对新旧返回值执行 JSON 严格等价检查;另覆盖 duplicate、unmatched、数组内容、错误与 malformed block。

结果

单位:毫秒/次。

会话规模 旧 median 新 median median 加速 旧 p95 新 p95 p95 加速
1k messages / 1 artifact 0.02630 0.02901 0.91× 0.04004 0.04104 0.98×
1k / 5 0.05384 0.03196 1.68× 0.06358 0.04481 1.42×
1k / 10 0.08886 0.03570 2.49× 0.09094 0.03780 2.41×
1k / 25 0.20054 0.05370 3.73× 0.20179 0.05491 3.68×
2k / 200 2.70905 0.28186 9.61× 2.71983 0.29307 9.28×
10k / 500 31.96035 0.91182 35.05× 32.04902 0.95202 33.66×

三个大场景的逐 trial median 加速也保持稳定:

  • 1k / 25:3.742×、3.723×、3.741×;
  • 2k / 200:9.756×、9.523×、9.547×;
  • 10k / 500:34.873×、34.992×、35.061×。

所有规模和边界 fixture 均 equality: true

边界说明

单 artifact 场景增加约 0.0027 ms(2.7 μs)中位耗时;p95 差约 1.0 μs。候选实现已延迟到第 2 个 artifact 才分配索引,绝对回退很小,但不应宣称“所有场景都加速”。从 5 个 artifact 开始 median 与 p95 均有收益,长会话收益明显。

建议优化

  • 第 1 个 artifact 沿用直接查找,避免普通场景无条件分配 Map。
  • 从第 2 个 artifact 开始只构建一次 first-result-wins 索引。
  • 保持 findToolResult() 对重复 ID 取首个结果的语义。
  • 增加多 artifact、duplicate ID 和 result-before-use 回归测试;计时 benchmark 不放进 CI。

查重

已检索开放/关闭 issue 和全部 PR,关键词包括 artifacts performanceartifact scannerfindToolResultquadratic transcript。命中的 #1278 是 artifacts 功能引入 PR,不包含本优化,未发现同因问题。

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