DeepSeek-Reasonix 扩展运行时 v2 性能基线:增量重建、前缀缓存与 Sidecar 生命周期治理
【免费下载链接】DeepSeek-ReasonixDeepSeek-native AI coding agent for your terminal. Engineered around prefix-cache stability — leave it running.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Reasonix
扩展运行时 v2(Extension Runtime V2)是 DeepSeek-Reasonix 面向插件生态的核心运行时层,负责把 system prompt、tool schemas、interceptor、UI、provider 与 MCP 等多类贡献聚合为可热更新的运行时快照。本篇基于 docs/EXTENSION_RUNTIME_V2_PERF.zh-CN.md 展开,结合仓库源码讲解其性能基线、增量/全量 rebuild 的判定规则、前缀缓存(prefix-cache)稳定性契约,以及 Sidecar 启动与 drain 的治理细节。读完本文,你将掌握如何复现性能基线测试、理解RebuildFrom子图 patch 的适用边界,并能在开发插件时主动规避触发全量重建的变更。
性能基线:软 CI 阈值与 Benchmark
扩展运行时 v2 为热路径上的三个关键操作定义了软性能基线(soft performance baseline),用于在开发机与 CI 中拦截数量级级别的性能回退(multi-order-of-magnitude regressions),而不是硬性失败。所谓"软",体现在它只在"离谱地慢"时才报错,正常波动不会打断流水线。
| 操作 | N | 软上限 |
|---|---|---|
BuildDependencyGraph | 32 组件 | < 50ms |
DiffRuntimePlanno-op | 同图 | < 20ms |
EffectScope.Dispose | 64 effects | < 50ms |
这些阈值定义在 internal/extension/bench_threshold_test.go 中:
TestGraphAndPlanLatencyBaseline构造 32 个ComponentDescriptor后调用BuildDependencyGraph,若耗时超过 50ms 即t.Fatalf;随后对同一张图执行DiffRuntimePlan(g, g, 1, 2)(no-op diff),阈值 20ms。TestEffectScopeDisposeBaseline向NewEffectScope(1)注册 64 个Reversible类 effect,再调用Dispose,阈值 50ms。
EffectScope.Dispose之所以能维持在线性低延迟,是因为 internal/extension/effectscope.go 的LiveScope.Dispose采用逆注册序、一次性、幂等释放:释放时先把 effects 从持有列表中摘出并置closed=true,再逐个执行Dispose,单个 effect 的失败通过errors.Join聚合且不跳过其余 effect,避免在 64 个 effect 场景下出现二次释放或泄漏。
测量命令:一键复现基线
仓库提供了三条可直接运行的测量命令,分别覆盖图构建/计划 diff 延迟、内核启动吞吐、以及增量 patch 的集成行为:
go test ./internal/extension/ -run 'TestGraphAndPlanLatencyBaseline|TestEffectScopeDisposeBaseline' -count=1 go test ./internal/extension/ -bench 'BenchmarkDependencyGraphAndPlan|BenchmarkExtensionKernelStartup' -benchmem -count=3 go test ./internal/boot/ -run 'TestIntegrationNoOpDoesNotBuildNewController|TestRebuildFromNoOp' -count=1- 第一条验证上文两张基线表(软阈值);
- 第二条跑 internal/extension/benchmark_test.go 中的两个 Benchmark:
BenchmarkExtensionKernelStartup测量不可变快照装配部分(无扩展 vs 64 个 interceptor 贡献),并有意排除进程 spawn 与 sidecar 握手延迟——这两部分属于扩展作者应自行测量的范畴;测试通过ReportMetric额外输出 p50-ns/op 与 p95-ns/op 分位数据。BenchmarkDependencyGraphAndPlan在组件规模 8 / 64 / 256 三个档位下分别测量graph、plan-noop、plan-full三个子场景,其中plan-full通过对所有组件做版本号 bump(1.0.0 → 2.0.0)构造"全量 reload"图再执行 diff,用来对比全量重建与 no-op 的成本差异;
- 第三条来自 internal/boot 的集成测试矩阵(见 integration_matrix_test.go、integration_plan_test.go),验证 no-op 场景下不会构建新的 Controller、
RebuildFrom走增量路径。
增量 vs 全量 rebuild:子图分类驱动
扩展运行时 v2 的核心优化是:按变更类型把 rebuild 划分为增量 patch 与全量重建两类,并通过进程级计数器暴露决策结果(见 internal/extension/metrics.go 中的NoOpRebuilds/SubgraphRebuilds/FullRebuilds三个原子计数器)。
- 增量:no-op、interceptor、UI、provider、MCP-only 变更走
RebuildFrom+ 真子图 patch,不得调用BuildRuntime;对应指标NoOpRebuilds/SubgraphRebuilds。 - 全量:
SubgraphSidecar/SubgraphFull分类触发FullRebuilds计数与完整的BuildRuntime。
分类的逻辑核心在 internal/extension/runtimeplan.go:
SubgraphKind枚举定义了SubgraphNone、SubgraphInterceptorOnly、SubgraphProviderOnly、SubgraphUIOnly、SubgraphMCPOnly、SubgraphSidecar、SubgraphFull七种级别;DiffRuntimePlan对两张依赖图做确定性 diff:按 ID 区分Added/Removed/Reloaded/Unchanged,drain 集只包含 removed + reloaded,且按旧图的DrainOrder(逆依赖序)排序;classifySubgraph扫描变更组件的Intercepts/Replaces/Provides(provider、ui/uiaction、mcp/mcpserver、interceptors/strategies),累加命中种类数:单一种类走对应窄分类,多种类且无"其他"走SubgraphSidecar,含未分类能力或插件空 provides 则回退SubgraphFull。
增量 patch 的执行链在 internal/boot/rebuild_subgraph.go 的tryRebuildSubgraph中可见:先buildRuntimeGraph与DiffRuntimePlan并记录ObserveGraphBuild/ObservePlanDiff耗时指标,随后按plan.Kind计数;窄分类(UI/interceptor/provider/MCP-only)走stageSidecarSubgraph→awaitSidecarsReady→commitControllerExtPatch的stage→ready→commit 三阶段 fail-atomic流程——任何阶段失败都会调用restoreControllerBindings把 Controller 的 dispatcher / provider resolver / UI 绑定恢复到 patch 前状态,绝不留下半提交。集成测试TestIntegrationNoOpDoesNotBuildNewController与TestRebuildFromNoOp(位于 internal/boot/integration_matrix_test.go)即是对该路径的端到端验证。
缓存命中:前缀字节稳定契约
prefix-cache 稳定性是 DeepSeek-Reasonix 的工程重心,扩展运行时 v2 把这一目标落到 CacheHash 契约上:
- no-op、UI/interceptor-only、以及只滚动 backend 的 Provider/MCP 计划会保持 system prompt、tool schemas 与
CacheHash字节稳定; - Provider capability 变化通过
RuntimePlan.ProviderChanged呈现(见 runtimeplan.go),而不会误报PrefixChanged——PrefixChanged是构建后的事实(post-build fact),只有前后两个RuntimeSnapshot都存在且能比较CacheHash时 boot 层才会设置它;MayChangePrefix()也明确约定 interceptor-only 与 UI-only 计划按契约不改变 CacheHash; - MCP schema 新增、删除或改名则归类为全量 rebuild,并有意重新计算 prefix。
classifySubgraph中mcpCapabilitySchemaChanged通过比较变更组件前后SchemaHash形状(mcpCapabilityShape映射 capability key → SchemaHash)来判定:形状不一致立即返回SubgraphFull,因为 provider 可见的 tool 字节不能停留在陈旧状态; - 保留
ReuseAssembly时仍可跳过 skill/command/hook 的 rediscovery,避免把与当前变更无关的装配开销带入热更新。
CacheHash的计算集中在 internal/extension/snapshot.go:computeCacheShape同时产出systemHash、toolsHash与cacheHash三值,其中cacheHashInput以 JSON 对象键规范排序后哈希,保证相同发现状态必然得到相同指纹(adapters_test.go中有"identical discovery state produced different CacheHash"的回归断言);builder.go 在构建时把三者存入 fingerprint,快照对外通过RuntimeSnapshot.CacheHash()暴露。
Sidecar 启动 / drain:收养、排水与超时凭证
Sidecar 生命周期管理遵循"只动该动的"原则:
StartPackagesWithPlan收养(adopt)Unchanged客户端,仅启动Added/Reloaded的原生运行时包(见 internal/boot/rebuild_subgraph.go 中stageSidecarSubgraph对sidecar.StartPackagesWithPlan的调用,以及SidecarAdopts/SidecarStarts指标);- Publish 之后执行
DrainPlan排水;默认 drain TTL 为30s(见 internal/extension/publish.go 的drainTTL: 30 * time.Second,可用WithDrainTTL覆盖),超时后先 fire cancel(取消在途请求)再写入drain-timeout-<gen>receipt; - 冷启动 publish 不创建 watcher;存在 drain 时,快速连续 publish 在每个 runtime owner 上共用一个定时 watcher,且只等待最早 drain 的剩余 TTL——避免连续发布场景下 watcher 数量与等待时间线性膨胀;
- 每个 owner 的过期 generation 标记最多保留256个,超出即被扫掉(
SweepExpiredDrains按now.Sub(started) >= drainTTL淘汰,见 publish.go)。
证据保留与内存边界:保守淘汰,绝不伪证 clean
运行时治理不仅追求快,还要求"快得可证明、可回滚"。为此 v2 对证据(receipt)与回滚信息设置了明确的内存边界与保守策略:
- Receipt 证据仅存在于当前进程,最多保留 32 个 generation、每代 256 条;
- 发生淘汰时按保守策略处理:禁止声称 clean rollback,而不是隐藏证据缺失——宁可放弃回滚结论,也不伪造成功;
- 消息去重键会随对应 receipt 的淘汰而释放,避免陈旧去重状态长期占用并误伤后续消息;
- 文件 prior 每次写入最多保留 8 MiB,每个 runtime owner 合计最多32 MiB;超限的 prior 不保留,并且阻止 clean rollback 判断(prior 不足时不得断言可以干净回滚);
- 已完成的 Provider stream 会立即移除 drain 回调,因此长寿命 generation 只保留活跃 stream 的取消状态,而不是把已完成 stream 的取消信息堆积到 drain 期。
这套边界设计配合EffectScope的Irreversible/Compensatable类 effect(internal/extension/effectscope.go)共同构成可审计的回滚证据链:Irreversible永远记录compensationStatus = "not_applicable",绝不暗示外部动作已被撤销。
小结
扩展运行时 v2 的性能设计可以归纳为三条主线:可量化的软基线(构建、diff、dispose 三条 <50ms 红线)、子图驱动的增量重建(七级SubgraphKind分类 + fail-atomic 三阶段 patch,配合NoOpRebuilds/SubgraphRebuilds/FullRebuilds指标可观测)、以及前缀缓存与证据边界(CacheHash 字节稳定契约、MCP schema 变更强制全量、drain TTL 30s、receipt 32 代 × 256 条与 prior 32 MiB 的保守淘汰)。对于插件作者,最值得记住的实践是:只滚动 backend 且不改 MCP schema 形状的更新走窄分类增量路径,而任何 MCP schema 的新增/删除/改名都会触发全量重建并有意重算 prefix——理解这条分界线,就能在保证 provider 可见字节正确的前提下,把热更新的停顿压到最小。
【免费下载链接】DeepSeek-ReasonixDeepSeek-native AI coding agent for your terminal. Engineered around prefix-cache stability — leave it running.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Reasonix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考