Replies: 2 comments 1 reply
|
顶一顶,打字还是打了一会的~ |
|
顺着你这份 profile 往下挖了一层。在 Linux + npm 10 上复现了一遍(你和 #3786 都是 Windows + npm 11),然后把 peer 图从 registry 元数据里整个拉出来数了一遍。两个结论:#3786 猜的版本错配可以排除,而真正贵的那个东西有一个很具体的形状,正好对得上你 profile 里那 28%。 一、复现:不是 Windows 的事,也不是 npm 11 的事Linux / Node v24.16.0 / npm 10.8.2,
差距至少 94 倍,内存 9 倍。卡死期间单核 100%、零网络;我每 18 秒采一次样,RSS 从 2.80 GB 稳定爬到 3.23 GB(约 4.5 MB/s)一直到被杀——跟 #3786 描述的 80 MB → 700 MB 是同一个形状,只是 Linux 上没有那个内存上限,于是爬得更高。 你测出 rc.7 默认约 4 分钟能完成、rc.1/rc.2 无响应;我这里 rc.7 和 rc.2 都跑不完。npm 版本不同常数不同,但撞的是同一堵墙。 二、版本错配这条可以划掉#3786 猜的是「rc.7 根 + rc.8 子包」的混合树触发回溯。这个混合是真的存在——上表第 4 行解出来的锁文件正好长这样: 但同一张表第 1、2 行是现在的 latest,版本齐得很: 版本齐了,默认解析照样跑不完,而且慢得一模一样。 所以错配是真的,但不是原因。 三、peer 图长什么样从
peer 边比普通依赖边多一倍还多,而且几乎全是内部互指。再往里两个数,就跟你的 profile 接上了: (1) 869 条内部 peer 边里,同时也写在 一条都没有。对 npm 来说这 869 条是纯约束:没有对应的依赖边可以顺着走、把搜索空间剪掉, (2) 869 条里 847 条(97.5%)的区间是 caret 套预发布号。 caret 碰上预发布版本走的是 semver 里最贵的那条路:每个候选都要构造 SemVer 对象、按预发布规则逐段比。869 条约束 × 199 个节点 × 每次比较都不便宜 × 没有依赖边可剪枝——你 profile 里那 28% self time 在 另外那 59% 在 URL 解析也说得通:Arborist 每考虑一个摆放位置都要拼一遍节点路径,反复摆放同一批节点,URL 解析跟着翻倍。 四、这解释了为什么那两个规避都灵
五、要从根上修的话我不清楚这些包为什么用 peer 而不是 dep,以下纯属外部观察: 同一个 monorepo、同一个版本号、同批发布的包之间写 不动结构也有便宜的缓解:
复现命令,都不执行任何包脚本,只发 GET: npm i @deepseek-ai/dsh@0.1.1-rc.2 --package-lock-only --ignore-scripts # 600 s 未完成
npm i @deepseek-ai/dsh@0.1.1-rc.2 --package-lock-only --ignore-scripts --legacy-peer-deps # 6.4 speer 那几个数是直接读 registry packument 数的,谁都能复核。 |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
省流 | TL;DR
概述
嗨!今天我尝试用npm更新dsh,发现npm直接崩溃。在 #176 #3786 #3710 #3845 提到的 npx @deepseek-ai/dsh web 首次启动长时间无输出问题基础上,我进一步做了一些排查。问题似乎和网络慢没啥关系,主要是 npm 的 peer dependency 解析阶段。
一个比较直观的量化结果来自我的CNB“云原生开发”:使用 npm 时,DSH单次构建持续 8 分 19 秒被 SIGKILL;切换到 pnpm 后,同一套构建可以正常完成,pnpm install 约 45–50 秒。
进一步分析
本地进一步用
--package-lock-only --ignore-scripts排除构建脚本和实际解包影响后:这说明单纯的依赖数量或网络速度很难解释 npm 的分钟级耗时:同一依赖图在禁用 npm peer dependency resolution 后,解析时间可以稳定下降到约 30 秒。
我也做了CPU profile。在 npm 长时间无输出的阶段,进程实际上仍在持续占用 CPU;采样中约59% self time 位于 Node URL 解析、28% 位于
semver.SemVer,调用栈主要来自 Arborist 的 peer placement / dependency validation因此目前更倾向于:DSH 较重的 peer dependency graph 触发了 npm Arborist 异常昂贵的重复 peer-placement validation
改为 pnpm 和采用
--legacy-peer-deps都能显著缓解这一现象。For English
Hi there, I did some further investigation into the very long first-run installation time reported in #176.
This does not appear to be just a lack of progress output or slow network access. In my tests, npm spends most of the silent period doing CPU-bound peer dependency resolution.
Reproduction
Environment:
@deepseek-ai/dsh0.1.1-rc.1 / rc.2A normal npm install can remain silent for several minutes around dependency placement/resolution.
After this point there may be no new npm log output for minutes.
This is also reproducible in our CNB packaging workflow. With npm, one build spent 8m19s in this phase before the npm process was eventually killed with SIGKILL(OOS)
I tested dependency resolution separately using:
The interesting result is what happens when npm peer dependency resolution is disabled:
The pnpm build resolves roughly 500 packages and completes normally, so dependency count alone does not seem to explain the multi-minute npm behavior.
The --legacy-peer-deps result seems particularly significant: with the same npm, registry and DSH release, resolution drops from minutes to about 30 seconds when peer dependency processing is disabled.
CPU profile
I then captured a CPU profile while npm was in the silent resolution phase.
In a ~20.5 second sample, the largest self-time hotspots were approximately:
Following the caller stacks, this work originates predominantly from Arborist dependency validation underneath peer-placement checks, roughly along this path:
There are nested/repeated CanPlaceDep -> canPlacePeers -> ... -> CanPlaceDep checks in the sampled stacks.
So although CanPlaceDep itself has little self CPU time, the work triggered underneath peer placement repeatedly performs dependency-spec / semver validation and dominates the profile.
This also explains why the process looks completely hung: it is not necessarily blocked on network or I/O; npm is still actively doing resolution work without producing user-visible progress.
Current hypothesis
My current hypothesis is that DSH's unusually peer-heavy package graph triggers pathological/redundant peer-placement validation in npm Arborist.
This seems consistent with all three observations:
normal npm resolution takes minutes;
--legacy-peer-deps reduces it to ~30 seconds;
pnpm resolves and installs the same large graph much faster.
I don't know yet whether this should ultimately be considered an npm/Arborist performance bug, or whether the published DSH package graph can be structured differently to avoid triggering it.
It may still be worth adding first-run progress feedback as suggested in #176, but that would only address the UX symptom rather than the underlying installation cost.
Happy to provide the .cpuprofile, graph-diff script/results, or run additional targeted tests if useful.
All reactions