DeepSeek Harness Windows 每次 spawn 多 4 秒:分段计时定位 pwsh 包装
echo 1 在终端里是毫秒级,在 DSH 里端到端要 10 秒:4.4 秒是钩子,另外约 6 秒是从钩子结束到工具结果。 这条基线一旦建立,问题范围就被锁死在「每次 spawn 都要付一笔固定成本」,而不是模型或网络(#10608)。下面按四段计时逐步逼近。
DeepSeek Harness 实验准备:用 echo 1 做基线
选一条最不可能慢的命令,量它的端到端时间,再拆开看时间花在哪一段。
- 在会话里让 Agent 执行
echo 1,记录tool/call到tool/result的墙上时间。 - 社区结果:约 10 秒,其中 PreToolUse 钩子 4.4 秒,钩子结束到结果约 6 秒。
- 同一条命令在终端里跑,几乎立刻返回。
- 结论:慢的是调用链路,不是命令内容。
DSH插件 第一段:先量「不 spawn 的工具」
read 和 grep 不启动子进程,如果它们也慢,说明开销在调用前后,而不是在工具内部。
- 统计一段时间的工具耗时:社区样本里
read49 次、均值 4,860 ms;grep11 次、均值 4,309 ms。 - 对照钩子均值:PreToolUse 324 次、均值 4,046 ms。
- 两者几乎重合,说明 read/grep 的时间基本就是钩子的时间。
pwsh均值 10,621 ms,多出来的部分是 pwsh 进程自身启动。mcp__…save均值 4,251 ms,MCP 子进程也吃到同一笔固定成本。
| 工具 | 是否 spawn | 均值 |
|---|---|---|
read | 否 | 4,860 ms |
grep | 否 | 4,309 ms |
pwsh | 是 | 10,621 ms |
mcp__…save | 是 | 4,251 ms |
这一段就把「工具实现慢」排除掉了:不 spawn 的工具一样慢。
DeepSeek Harness 第二段:把钩子摘出来单独量
把钩子脚本改成第一行就退出,如果耗时不变,就证明成本不在脚本里。
- 用
hooks.json里的四条钩子做统计:SessionStart、UserPromptSubmit、PreToolUse、PostToolUse。 - 记录 DSH 自己报的
hook/result.durationMs:PreToolUse 均值 4,046 ms,PostToolUse 4,024 ms,UserPromptSubmit 4,310 ms。 - 同样的脚本在终端手工跑:206 ms、51 ms、51 ms。
- 再做一个对照实验:让
block_delete.js第一行process.exit(0),什么都不做。 - DSH 记录的耗时仍是 4,298~4,429 ms,8 次连续调用波动只有 131 ms。
- 结论:这是固定成本,与脚本工作量无关。
DeepSeek插件 第三段:量 shell 本身的启动时间
如果 shell 启动只要 0.2 秒,而调用要 4 秒,那么多出来的部分在 shell 之外的包装与进程创建里。
- 在终端里分别计时:
pwsh -NoProfile -Command exit约 190 ms,pwsh -Command exit约 198 ms。 powershell.exe5.1 约 132149 ms,48 ms。cmd /c exit约 31 ms,node -e 0约 38-NoProfile只省 8 ms,所以「profile 或模块自动加载拖慢」这个假设被排除。- 也就是说:没有任何一次单纯 shell 启动接近 4 秒。
- 成本 = 那次启动 + runner 在它周围做的所有事,每次都付。
DeepSeek Harness 第四段:看进程树,找中间层
用任务管理器或进程查看器打开命令行列,看 node.exe 的父进程是谁——这一步直接暴露包装层。
- 触发一次钩子调用,同时观察进程树。
- 社区发现:
node.exe不是 DSH 的直接子进程,而是 pwsh 包装层下的孙进程。 - 结合源码形状:DSH 把命令统一包成
pwsh -NoLogo -NoProfile -NonInteractive -Command "<command>"。 - 对「绝对路径 + 参数」这类命令,包装层是纯开销。
- 至此四段计时闭合:工具本身没问题,钩子脚本没问题,shell 启动也没问题,问题在包装层。
DSH插件 修法:命令形状简单时直接 CreateProcessW
社区补丁让执行器的 argv() 先尝试简单拆分,能直接调 CreateProcessW 就跳过 pwsh;其余情况回退原路径。
- 只有「绝对路径 + 参数」形状的命令走直连。
- 含管道、重定向、
$、反引号、#、换行、智能引号、不配对引号、相对首 token、.cmd/.bat/.ps1、--%的,一律回退。 - 超时、缓冲与退出码处理都不动。
- 补丁后同机同版本实测:PreToolUse 从 4,046 ms 降到 240
255 ms;177 ms;read降到 144grep362~395 ms;一个 python MCP 调用 151 ms。 - 注意 240~255 ms 只是 spawn 加脚本执行,不含等待输出流关闭的约 200 ms,整个钩子约 440 ms。
- 仍有约 1.3 秒的等待停在
NtCreateUserProcess,属于 JS 层以下,这个补丁够不到。
版本状态与你能做的事
rc.3 上包装层仍在,所以走 shell 语法的命令依旧 4.2~4.7 秒;你只能减少触发它的次数。
- 0.2.1-rc.3 上
argv()仍无条件返回 pwsh 包装,社区实测直接 spawn 路径 0.40.6 秒、走 shell 的 4.24.7 秒。 - 含引号参数的
python.exe -c "…"会被扫描器判回 shell,实测仍是 4,506 ms。 - 能做的:减少钩子数量与触发频率,尤其是 PreToolUse。
- 钩子命令尽量写成「绝对路径 + 参数」,避免管道与重定向。
- 别去调 Defender 排除项——社区没试过也已经不需要,真正的原因是包装层。
- 想彻底解决,需要上游让钩子走
ctx.subprocess,或在命令已是 argv 时跳过 shell。
注意事项
- 别把固定延迟当网络抖动。8 次调用只波动 131 ms,这是固定成本。
- 别先怀疑 profile。
-NoProfile只省 8 ms。 - 别只看钩子。
pwsh工具本身还叠加了自己的启动。 - 补丁不是发布版。报问题时标清版本与是否打了补丁。
- 记录进程树。它是区分「工具慢」与「包装慢」最快的一张图。
如果你的机器上是「偶发几秒」而不是「每次都几秒」,那更可能是沙箱或 ACL 相关,换个方向查。相关排查见 dsh 启动很慢、启动卡住怎么办?DeepSeek Harness 启动耗时的原因与加速排查。
常见问题
是 DeepSeek Harness 的工具层慢,不是模型。社区用 echo 1 做基线,端到端花了 10 秒,其中约 4.4 秒是 PreToolUse 钩子,另外约 6 秒是从钩子结束到工具结果。echo 1 本身是毫秒级。
因为 DeepSeek Harness 在每次调用前都要过一遍 PreToolUse 钩子,而钩子本身要 spawn 进程。社区测到 read 平均 4,860 ms、grep 4,309 ms,与钩子的 4,046 ms 几乎重合。
把 DeepSeek Harness 的钩子脚本改成第一行就 process.exit(0)、什么都不做,DSH 记录到的耗时仍是 4,298~4,429 ms;而同样的脚本在终端手工跑只要 51~206 ms。差值在包装层,不在脚本。
是 pwsh 包装层,这是 DeepSeek Harness 在 Windows 上额外加的一层。DSH 把每条命令包成 pwsh -NoLogo -NoProfile -NonInteractive -Command "<command>",而 node.exe 实际是 pwsh 的孙进程。去掉这层包装后,钩子降到 240~255 ms。
官方版本没修,社区补丁只在 DeepSeek Harness 的自有构建上验证过。0.2.1-rc.3 上 argv() 仍然无条件返回 pwsh 包装,走 shell 语法的命令依旧 4.2~4.7 秒,而直接 spawn 的路径是 0.4~0.6 秒。
相关术语
- PreToolUse 钩子
- PreToolUse 钩子是兼容 Claude Code 风格的钩子类型,在每次工具调用前执行。它由 DSH 通过 shell 运行,因此会引入额外的进程启动开销。— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
- pwsh 包装层
- pwsh 包装层是 DSH 在 Windows 上把命令统一包成 pwsh -Command 的形式再交给执行器的中间层。对已经是「绝对路径 + 参数」的命令,这一层是纯开销。— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
- CreateProcessW
- CreateProcessW 是 Windows 创建进程的底层 API。社区补丁在命令形状简单时直接调用它,绕过 shell;否则回退到原有包装路径。— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
- NtCreateUserProcess
- NtCreateUserProcess 是 CreateProcessW 内部实际执行进程创建的系统调用。社区观察到一个仍有约 1.3 秒的等待停在这里,属于 JS 层以下。— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
来源
- #10608 — Every subprocess spawn costs 4-6s on Windows· deepseek-ai(GitHub Discussions)
- deepseek-ai/deepseek-harness(源码仓库)· GitHub