Skip to Content
PerformanceACP extension watcher:首次会话测量

ACP extension watcher:首次会话测量

本次变更跳过了 ACP 进程中的交互式扩展文件 watcher。守护进程已经拥有自己的资源刷新生命周期。交互式终端会话仍然会继续构建 watcher。这是首次会话优化的第一阶段;它并未将首次新建会话的耗时降至 500 ms 以下。

变更与来源

基线为 main 提交 2985fef4e9b9393601141c16031b2ba039181158;被测候选版本为仅含 watcher 变更的提交 2aadc4b127ec24074e0e5ddfd402e978a0c43f5d。生产变更是额外增加的一条 ACP 模式守卫。--acp 与 --experimental-acp 均被覆盖,同时包含一个为交互模式构建 watcher 的对照组。不涉及 Core 初始化、资源选择、prompt、鉴权、MCP 策略或预热实现方面的改动。

两个 bundle 均使用 CLI 版本 0.27.2。调用的 TypeScript 包为 @qwen-code/sdk 0.1.32。每次运行前后均记录了包版本与 SDK 入口哈希。完整的基线、候选及编译后的 SDK 文件树摘要也在测量批次前后被冻结并校验。候选版本在独立的 worktree 中构建,使用锁定的 pnpm 11.24.0 lockfile 进行安装、完整构建、类型检查与打包。

工作负载与计时边界

私有已安装工作负载包含 31 个资源:14 个扩展套件与 17 个独立 Skill,共 90 个自定义 Skill、3,944 个文件、81,931,741 字节。每个样本在区分大小写的本地文件系统上物化出完全相同的资源清单。资源名称、源文件、业务内容、清单、本地路径、标识符与原始日志均保持私有。此处仅发布聚合计数与计时数据。

该 profile 保留 hook 并将 MCP 白名单设为空列表。不发送 prompt、模型请求或业务工具调用,也不连接已配置的外部 MCP 服务器。原生激活状态在不受计时的 fixture 准备阶段完成 seeding。这代表一个已安装并激活的资源集,使用全新进程与会话状态;并不声称所有源自资源的缓存均为空。Fixture 创建、文件复制、原生激活 seeding、bundle 构建以及 SDK 模块导入均在两个时钟启动之前完成。

每个样本拥有全新的 HOME、运行时目录、TMPDIR、持久化会话根目录,以及初始为空的 Node 编译缓存目录。守护进程通过其真实的嵌入式 HTTP-bridge 入口启动,并设置 preheatBridge: false。驱动等待 runtimeReady,然后在首次 SDK 调用前验证业务会话数为零且无活跃的 ACP channel。在该调用之前不存在 HTTP 就绪或能力请求。一个全新的 UUID 请求新建线程;结果必须以 attached: false 回显该 UUID。后续两次全新 UUID 调用用于验证后续会话行为。

就绪请求边界在 SDK 客户端构造与调用方 UUID 生成之前被观测;二者均不在主 create 时钟之内。

主时钟包裹完整的 await client.createOrAttachSession(...):能力发现、会话 POST、响应 JSON 解析以及 SDK 身份校验。首个 HTTP 请求为 GET /capabilities;其单独耗时并非新建会话的测量指标。请求记录保留了三次调用的实际顺序;不得假设后续调用省略了能力检查。

报告中还展示了会话 POST、守护进程 spawn 到守护进程就绪请求边界,以及守护进程 spawn 到首个完成的 SDK 结果的耗时。等待守护进程并不会创建 ACP 进程:每个被记录的 ACP spawn 必须发生在 SDK 调用开始与首次 HTTP 请求之后。守护进程状态必须报告预热为 not_scheduled。正式样本中不包含 CPU 分析器、显式编译缓存刷新、禁用子进程编译缓存、人为 sleep 或启动探测。

正式结果

首次完整 SDK create 的中位数从 1096.1 ms 降至 772.9 ms:降低 323.2 ms / 29.48%。候选版本在所有样本中仍高于 500 ms。

以下所有数值单位为毫秒,以中位数(最小值–最大值)展示,每个条件进行 8 次独立的首次会话。两个条件均完成 8/8 次尝试,共创建 24/24 个新会话。无正式样本被排除。

时钟基线仅 Watcher
首次完整 SDK create(主)1096.1 (1078.3–1129.3)772.9 (758.4–824.1)
首次会话 POST + JSON1075.7 (1058.8–1109.2)754.1 (739.3–798.5)
守护进程 spawn → 观测到监听器336.7 (322.5–341.5)329.2 (323.1–358.5)
守护进程 spawn → 就绪请求边界630.6 (618.7–644.5)605.3 (591.8–621.0)
守护进程 spawn → 首次 SDK 结果1731.0 (1709.6–1748.0)1384.5 (1355.5–1427.0)
首次 capabilities GET + JSON18.7 (17.7–28.1)18.3 (18.1–25.1)
首次 HTTP → ACP spawn14.2 (13.9–22.3)14.2 (14.1–24.6)
第二次完整 SDK create71.1 (65.1–79.6)74.0 (65.2–80.0)
第三次完整 SDK create56.9 (53.7–63.7)54.4 (53.4–56.6)

按配对轮次排列的首次 create 样本:

轮次基线仅 Watcher
11098.5758.4
21078.3771.1
31129.3807.8
41110.2760.1
51082.6767.9
61089.2774.8
71093.7789.0
81110.5824.1

公开数值摘要包含每个正式样本的均值、中位数、范围、编译/构建树摘要以及通过/失败计数。它由显式白名单构建;不包含私有资源清单、源内容、本地路径、UUID、token 或原始日志。

早期的一组 6 次尝试批次因临时 fixture 卷空间不足而中止(5 个完成样本与 1 个会话初始化失败)。失败日志在运行时目录创建时出现 ENOSPC。所有 6 个进程正常关闭;整个批次(包括已完成的计时)均被排除。在重启正式批次之前,准备了更大的独立区分大小写卷与 16 个全新 fixture。失败证据保留在本地;不会被静默重试后并入结果。

8 个配对轮次串行运行,按 A/B 再 B/A 的顺序交替。所有 fixture 在独占测量窗口之前完成准备。在此任务下没有构建、fixture 复制、其他基准测试或测试并发运行。操作系统文件缓存未被清除;这些是在单台主机上的全新进程、全新会话测量,而非冷盘测量。每个条件 8 个样本不足以建立生产环境的尾延迟保证。

主机:macOS 26.6.2,arm64;Node.js v26.7.0。本本地隔离重建使用私有的预生产安装清单;并非生产或实时预生产的延迟测量。500 ms 目标适用于首次完整 SDK create,仍是本 PR 之外的后续工作。

功能验证与限制

独立的功能运行使用冻结的基线与仅 watcher 的 bundle,配合相同的完整已安装清单。它们不属于正式计时批次:健康检查与 helper 调用先于部分功能检查。

  • 两个条件均通过 41/41 项外部变更断言与 18/18 项激活/生命周期断言。
  • 8 个 helper 快照、helper 之后的 8 个已有会话菜单、8 个新建会话菜单以及 11 个生命周期快照在 fixture 路径与 UUID 对应关系校验后均匹配。
  • 常规读取保留了观测到的传播差异:完整的工作区 Skill 信封在 29/32 个样本中匹配,Skill 内容在 31/32 个样本中匹配,已有会话命令列表在 31/32 个样本中匹配。候选版本在某一观测点出现更早的原子替换;基线中文件重建短暂出现更早。后续样本或常规 helper 趋于一致。在三秒观测窗口内,两个条件下的扩展 Skill 编辑/恢复均保持为旧值,并在 helper 之后变为正确。
  • 两侧均发出 8 个公开 SSE 事件,具有相同的 type/phase 序列。前 7 个对应 payload 匹配;最后一个 reload payload 刷新相同的 9 个会话,但完成顺序不同。检查相等性时保留原始数组顺序。
  • 新会话响应在 UUID、创建时间与客户端身份上自然不同。其他响应字段匹配;实际创建通过 UUID 回显与 attached: false 独立断言。

仅对 fixture 与 bundle 根目录、由这些根目录派生的工作区标识符以及 UUID 对应关系进行归一化。对于单独报告的工作区 Skill 业务比较,排除观测时间戳与时效;原始信封保持完整。缓存过期、刷新标志、激活状态、generation、所有 Skill/命令字段以及原始数组顺序均不会被抹除以制造相等。这些观测不能证明传播延迟相同或常规读取字节级一致。

每个功能组均收到正常关闭确认并以退出码零退出,无信号、无强制回退。最终的限定范围 OS 观测未发现匹配的基准测试进程。正式计时样本分别强制执行自身的正常关闭确认、零退出状态、无强制回退,以及每个被记录的 ACP 子进程 PID 消失。

聚焦的 CLI 单元测试套件通过 129/129,完整构建、类型检查、打包、变更文件 ESLint 与 Prettier 检查均通过。交互式 watcher 构建由单元测试对照组验证;交互式终端 UI 端到端运行、Windows、Linux、模型执行、hook 在 prompt 上执行以及外部 MCP 行为不在本次验证范围内。

使用公开合成资源复现

公开生成器编写通用资源,不读取或导出私有安装内容。它使用 14 个套件、17 个独立 Skill 与 90 个 Skill 清单,但其字节数、目录结构、独立 skill 布局、hook 定义与激活准备均不相同。其计时可以复现流程并暴露扩展行为;无法复现或替代上述私有工作负载的数值。

使用 corepack pnpm install --frozen-lockfile、npm run build 与 npm run bundle 从各自提交构建冻结的基线与冻结的候选版本。公开复现使用 Node.js v26.7.0 验证。保持 SDK 构建固定,在测量前停止构建与资源复制,并串行交替两个 bundle。从此 checkout 中,每个全新输出目录运行一个样本:

node scripts/benchmarks/acp-first-session/fixture.mjs /tmp/acp-synthetic-a /absolute/frozen-baseline/dist node scripts/benchmarks/acp-first-session/fixture.mjs /tmp/acp-synthetic-b /absolute/frozen-candidate/dist node scripts/benchmarks/acp-first-session/run.mjs /tmp/acp-synthetic-a/fixture.json node scripts/benchmarks/acp-first-session/run.mjs /tmp/acp-synthetic-b/fixture.json

在测量批次之前准备所有全新 fixture。一次全新的合成冒烟测试完成全部 3 次 create 以及无预热、身份、关闭、子进程退出与已记录输入哈希检查。它验证了流程,不向私有结果贡献任何样本。每次运行打印一个无路径的数值/布尔摘要,并将详细本地证据保留在请求的输出位置。合成资源不包含 SessionStart hook;不发送 prompt,因此编写的 UserPromptSubmit hook 不会执行。至少使用 8 个交错配对,保留失败,检查每个预热/会话/spawn/关闭断言,并报告所有时钟。不要提交本地生成的 fixture 树或原始结果。

如果已记录的 ACP PID 在关闭后仍然存在,公开运行器将结果标记为失败并记录清理错误以供人工检查。PID 复用使其无法证明进程身份,因此自动终止仅限于其自身的 wrapper 进程句柄。

剩余的 sub-500 ms 工作必须减少首次 SDK 调用之后执行的初始化,使用相同的无预热边界与功能检查。它与本单行 watcher 变更有意分离。

Last updated on