dsh-large-proj-perf 1.10.1 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +353 -5
- package/README.md +83 -19
- package/docs/ARCHITECTURE.md +104 -22
- package/docs/COMPATIBILITY.md +29 -14
- package/docs/HARNESS-COMPARISON.md +8 -3
- package/docs/MEASUREMENTS.md +52 -20
- package/docs/PUBLISHING.md +7 -4
- package/docs/RENDER-ANALYSIS.md +300 -0
- package/docs/VERSIONING.md +74 -0
- package/lib/api.js +29 -9
- package/lib/client.js +1126 -0
- package/lib/config.js +24 -3
- package/lib/constants.js +11 -6
- package/lib/dsh-root.js +30 -0
- package/lib/history-budget.js +140 -0
- package/lib/index.js +19 -10
- package/lib/render-timing.js +215 -0
- package/lib/runtime.js +12 -15
- package/lib/stats.js +38 -2
- package/lib/ws-timing.js +73 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +9 -5
- package/scripts/link-deps.ps1 +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
[语义化版本](https://semver.org/lang/zh-CN/);补丁点的上游变化详见
|
|
8
8
|
[docs/COMPATIBILITY.md](docs/COMPATIBILITY.md) 的版本矩阵。
|
|
9
9
|
|
|
10
|
+
> **历史数字按发布时记录。** 当前有效的冷读/RSS/fork 数字见
|
|
11
|
+
> [docs/MEASUREMENTS.md](docs/MEASUREMENTS.md),渲染数字见
|
|
12
|
+
> [docs/RENDER-ANALYSIS.md](docs/RENDER-ANALYSIS.md)。下文提到的
|
|
13
|
+
> `RUNTIME-ANALYSIS.md` / `MEMORY-ANALYSIS.md` 已于 1.10.0 并入 MEASUREMENTS,
|
|
14
|
+
> 路径只作文物引用。
|
|
15
|
+
>
|
|
16
|
+
> **版本号规则见 [docs/VERSIONING.md](docs/VERSIONING.md)**:版本在发布时决定,
|
|
17
|
+
> 开发中的变更先记在 `[Unreleased]`,不逐次改 `package.json`。
|
|
18
|
+
|
|
10
19
|
## 重要版本速览
|
|
11
20
|
|
|
12
21
|
标题带 **⚠️ 重要** 的版本对使用者有实际影响。判定标准只有三条:
|
|
@@ -17,17 +26,351 @@
|
|
|
17
26
|
|
|
18
27
|
| 版本 | 类型 | 一句话 |
|
|
19
28
|
|---|---|---|
|
|
20
|
-
| [1.
|
|
21
|
-
| [1.
|
|
22
|
-
| [1.
|
|
23
|
-
| [1.
|
|
24
|
-
| [1.
|
|
29
|
+
| [1.16.0](#v1-16-0) | **文档归一 + 修复** | 测量文档单一叙事(一个数字一个 owner);诊断设施生命周期/所有权修复;新增 vm bundle 与文档测试 |
|
|
30
|
+
| [1.15.0](#v1-15-0) | **测量补全** | 响应腿拆到毫秒(写队列/deflate/网络);30k turn 跳 turn 1 **实测渲染进程 OOM 重载**;滚轮路径校验 |
|
|
31
|
+
| [1.14.0](#v1-14-0) | **测量补全** | 500 ms 链路归因(服务端 encode + 写队列,不是浏览器);markdown op 级成本;30k 跳转曲线(外推后由 1.15 推翻) |
|
|
32
|
+
| [1.13.0](#v1-13-0) | **测量补全** | 四条缺口全部动手:重会话生成、回合跳转(+1375 行 / 阻塞 1.7 s)、打开路径分段归因、滚动跳变 |
|
|
33
|
+
| [1.12.0](#v1-12-0) | **性能里程碑** | 首次测浏览器渲染;离屏行 containment 让批量翻页阻塞约 −47% |
|
|
34
|
+
| [1.11.0](#v1-11-0) | **破坏性** | 支持范围收紧到 `>=0.2.2-rc.1 <0.3.0-0`;更旧的 dsh 上不再加载 |
|
|
35
|
+
| [1.10.0](#v1-10-0) | **破坏性** | 移除三项在 0.2.2 上恒不生效的能力;配置键 22 → 17;修掉「改配置不生效」的晚绑定 bug |
|
|
36
|
+
| [1.9.0](#v1-9-0) | **性能里程碑** | 事件缓存改为按字节限容,稳态 RSS 1324 → 690 MB |
|
|
37
|
+
| [1.8.0](#v1-8-0) | **性能里程碑** | 冷读 21.2 s → 3.0 s(7×),瓶颈是 `turnOutline` 的 O(n²) 折叠 |
|
|
38
|
+
| [1.3.0](#v1-3-0) | **破坏性** | 声明 `peerDependencies`,不满足 0.2.0 门禁的旧 profile 会被跳过加载 |
|
|
39
|
+
| [1.0.0](#v1-0-0) | **首个版本** | 合并 fork-perf 与 projection-warmup 两个插件 |
|
|
25
40
|
|
|
26
41
|
未列入的版本是兼容性跟随(dsh 小版本升级)或文档/元数据修补,无需额外动作。
|
|
27
42
|
纯文档修补不标重要——例如 **1.10.1 不标**,它只修文档事实错误,行为零变化。
|
|
28
43
|
|
|
29
44
|
## [Unreleased]
|
|
30
45
|
|
|
46
|
+
<a id="v1-16-1"></a>
|
|
47
|
+
## [1.16.1] — 2026-11-14
|
|
48
|
+
|
|
49
|
+
文档与元数据修补:补上渲染优化的能力/效果描述,并把版本号策略写进文档。
|
|
50
|
+
**行为零变化。**
|
|
51
|
+
|
|
52
|
+
### 文档
|
|
53
|
+
|
|
54
|
+
- 新增 [docs/VERSIONING.md](docs/VERSIONING.md):**版本号只在发布时决定**;记录
|
|
55
|
+
npm 序列 `1.4.0 → 1.10.1 → 1.16.0` 的跳跃原因;`engines.dsh` 收紧一律算 MAJOR。
|
|
56
|
+
- `docs/PUBLISHING.md`、README、本文件指向该策略;`tests/verify_docs.mjs` 增加
|
|
57
|
+
`package.json` ↔ CHANGELOG 首条版本的一致性断言。
|
|
58
|
+
|
|
59
|
+
### 元数据
|
|
60
|
+
|
|
61
|
+
- `package.json` 的 `description` 与 `locale/{zh,en}.json` 补上浏览器侧离屏行
|
|
62
|
+
containment 的效果(批量翻页阻塞约 −47%)与渲染耗时诊断。
|
|
63
|
+
- README 的「最近重要更新」补 1.12.0 的渲染里程碑与 1.16.0;「能力与效果」表补
|
|
64
|
+
containment 的最长任务指标,并明确 `clientContainment` 是唯一有实测收益的
|
|
65
|
+
渲染优化、`renderTiming` 只采集数据。
|
|
66
|
+
|
|
67
|
+
<a id="v1-16-0"></a>
|
|
68
|
+
## [1.16.0] — 2026-11-13 — ⚠️ 重要(文档归一 + 结构债)
|
|
69
|
+
|
|
70
|
+
**没有新增用户可见能力,也没有改变补丁语义。** 这一版把 1.10–1.15 叠加出来的
|
|
71
|
+
测量文档收敛成单一叙事,并修掉诊断设施的 P0 正确性问题。
|
|
72
|
+
|
|
73
|
+
### 文档
|
|
74
|
+
|
|
75
|
+
- `docs/RENDER-ANALYSIS.md` 重写为 12 节 / 300 行:固定 Q1–Q6、热/冷五段同表、
|
|
76
|
+
跳转边际斜率 1.51→0.86→0.48、turn-1 OOM(n=1)、撤回表与数字归属表。
|
|
77
|
+
- **一个数字一个 owner**:渲染数字只归 RENDER,服务端只归 MEASUREMENTS,
|
|
78
|
+
ARCHITECTURE 改为零数字 + 链接;README 的头条各带链接。
|
|
79
|
+
- `MEASUREMENTS` 7 个 H1 → 1;`HARNESS-COMPARISON` 加时间快照横幅并更正 29 单元;
|
|
80
|
+
`COMPATIBILITY` 的 warmup 段落改为历史;README 把「投影主导」限定到长会话。
|
|
81
|
+
- CHANGELOG 速览表改用显式锚点,去掉 7.4 s / 60–70 s 头条。
|
|
82
|
+
|
|
83
|
+
### 修复
|
|
84
|
+
|
|
85
|
+
- `installSendTiming` 拆到 `lib/ws-timing.js`;disposer 只在仍是我们装的 wrapper
|
|
86
|
+
时还原;安装移到 controller 能力门禁之前,并新增 `patches.renderTimingSend`。
|
|
87
|
+
- `lib/dsh-root.js` 统一安装根解析(版本探针与 ws 共用)。
|
|
88
|
+
- `createStats().reset()` 成为唯一复位清单(含 `memoryBudget`),`stats.reset`
|
|
89
|
+
端点只调它;`memoryBudget` 关闭时恒为 `null`。
|
|
90
|
+
- `pages` 的 jump 条目改用 `recordsTotal` / `bytesTotal`,不再与单页同名字段混用。
|
|
91
|
+
- 渲染诊断与窗口预算合并进一个 `ctx.inject`,顺序确定。
|
|
92
|
+
|
|
93
|
+
### 客户端
|
|
94
|
+
|
|
95
|
+
- `__dshPerf.stop()` + `pagehide`:停采集、还原 `$stream` / `socket.send`、
|
|
96
|
+
卸载 highlighter root;`createStreams` 中途抛错自动回滚。
|
|
97
|
+
- `$stream` / `send` / scroll 监听都加了所有权检查;容器换节点时重新挂监听。
|
|
98
|
+
- 删死代码(`MAX_SCROLL` / `rowsPerTurn` / 重复 `flush` / `FOLD_GUARD_ITERATIONS`),
|
|
99
|
+
去掉模块级 `self` 与 `containment` 单例;`createWindows` 抽出 `createScrollTracker`。
|
|
100
|
+
打开/跳转窗口仍共享状态,四路拆分留到下一版。
|
|
101
|
+
|
|
102
|
+
### 工具与测试
|
|
103
|
+
|
|
104
|
+
- 新增 `tests/test_client_bundle.mjs`(`node:vm` 驱动真实 bundle,50 条断言)与
|
|
105
|
+
`tests/verify_docs.mjs`(链接/锚点/配置表/stats 形状,8 条)。
|
|
106
|
+
- `scenarios.mjs` 删除 warmup 死路径;C 场景改为 resume 冷读 + 投影热读;
|
|
107
|
+
`report.md` 对 I/J 有通用兜底;未知 `--scenario` exit 1。
|
|
108
|
+
- `bench-encode` 修帧形状与 UTF-8 字节;`make-session` 的 `stat` 移到 `ctx.stop`
|
|
109
|
+
之前,`--events` 强制 8 的倍数。
|
|
110
|
+
- `npm test`:**13 套件 / 349 条断言**(以运行器末行为准)。
|
|
111
|
+
|
|
112
|
+
<a id="v1-15-0"></a>
|
|
113
|
+
## [1.15.0] — 2026-11-13 — ⚠️ 重要(测量)
|
|
114
|
+
|
|
115
|
+
把 1.14 剩下的三条继续往下压,其中两条拿到了**决定性结论**。
|
|
116
|
+
|
|
117
|
+
### 响应腿拆到毫秒:deflate 才是冷读的大头
|
|
118
|
+
|
|
119
|
+
新增 `installSendTiming`:从 dsh 安装根解析 `ws`,包装
|
|
120
|
+
`WebSocket.prototype.send` 的回调(三条候选路径:ESM 解析 → `process.argv[1]`
|
|
121
|
+
→ dsh-session 反推),只记录 ≥64 KiB 的帧。配合客户端 `event.timeStamp`:
|
|
122
|
+
|
|
123
|
+
| 情形 | 写队列等待 | deflate + 写入 | 网络 + inflate |
|
|
124
|
+
|---|---:|---:|---:|
|
|
125
|
+
| 冷读(6.84 MB,刚折叠完 1.94 s) | 73 ms | **1 493 ms** | ~190 ms |
|
|
126
|
+
| 热读(6.84 MB,紧跟 control) | **174 ms**(等 control) | 207 ms | ~100 ms |
|
|
127
|
+
|
|
128
|
+
- **冷读后 deflate 慢 7 倍**:孤立 benchmark 214 ms、热读 207 ms、冷读后
|
|
129
|
+
1 493 ms。线程池与刚结束的折叠/GC 抢 CPU。
|
|
130
|
+
- **写队列跨流串行**:热读时 control 的 send 回调一结束,follow 的 send 才被调用,
|
|
131
|
+
174 ms 队列等待精确对齐 control 的写耗时尾部。
|
|
132
|
+
|
|
133
|
+
`stats.render.sends` 记录 `{t, sentAt, bytes}`;`stats.reset` 一并清空。
|
|
134
|
+
|
|
135
|
+
### 30 000 turn 跳到 turn 1:实测崩溃
|
|
136
|
+
|
|
137
|
+
在干净页面上直接 `jumpToTurn(1)`:服务端**成功**(300 页 / 239 800 事件 /
|
|
138
|
+
83.4 MB,聚合 85 ms),客户端卡住约 **140 秒**后**渲染进程被 OOM 杀掉并重载**
|
|
139
|
+
(`pageNow` 回到 31 s、样本清零、行数回到 100)。上一轮的外推「60–70 s / 2 GB」
|
|
140
|
+
方向正确,但真实结局是崩溃而不是「慢几分钟」。
|
|
141
|
+
|
|
142
|
+
### 滚轮路径校验 + 误报修正
|
|
143
|
+
|
|
144
|
+
用 CUA 发真实滚轮事件(3 × -1500 px):第一版判定「单帧 > 半个视口」把用户滚动
|
|
145
|
+
也记成 `jump`。现在要求**没有近期输入意图**才计为意外跳变:
|
|
146
|
+
|
|
147
|
+
| 手势 | maxFramePx | intentional | jump |
|
|
148
|
+
|---|---:|---|---|
|
|
149
|
+
| 打开后自动滚到底 | 16 683 | false | true |
|
|
150
|
+
| 用户滚轮 -1500 ×3 | 3 146 | true | false |
|
|
151
|
+
|
|
152
|
+
### 验证
|
|
153
|
+
|
|
154
|
+
- `verify_compat.mjs` 88 → 90 条:`ws` send 回调形状、插件记录 `sentAt`。
|
|
155
|
+
- `npm test` 278 条全绿。
|
|
156
|
+
|
|
157
|
+
<a id="v1-14-0"></a>
|
|
158
|
+
## [1.14.0] — 2026-11-13 — ⚠️ 重要(测量)
|
|
159
|
+
|
|
160
|
+
继续调查 1.13 之后仍开放的四条。**结论多数是「此前的猜测不成立」**:
|
|
161
|
+
|
|
162
|
+
### 500 ms 链路开销:不是浏览器,是服务端
|
|
163
|
+
|
|
164
|
+
用 `remote.streams.socket` 上的 `message` 监听器取 `event.timeStamp`(真正的
|
|
165
|
+
帧到达时刻;监听器自身可能被应用解析大帧后的微任务检查点推迟),按同机
|
|
166
|
+
`Date.now()` 把打开拆成五段:
|
|
167
|
+
|
|
168
|
+
| 段 | 实测 |
|
|
169
|
+
|---|---:|
|
|
170
|
+
| 请求腿(流打开 → 服务端开始) | 43–76 ms |
|
|
171
|
+
| 服务端产出 | 3–52 ms(热)/ 2 495 ms(冷) |
|
|
172
|
+
| 响应腿(服务端完成 → 帧到达) | **658 ms**(热,6.8 MB)/ 2 313 ms(冷) |
|
|
173
|
+
| 本地派发(帧 → item) | **7–35 ms** |
|
|
174
|
+
| 组装挂载(item → 首行) | 100–260 ms |
|
|
175
|
+
|
|
176
|
+
响应腿再拆:新增 `tools/runtime-probe/bench-encode.mjs` 量同载荷的
|
|
177
|
+
`JSON.stringify` + deflate(level 1):长会话 **265 ms**(51.5 + 213.8),
|
|
178
|
+
重会话 **36.8 ms**。剩余约 300 ms 来自 **`session/control` 基线堵在串行写队列**:
|
|
179
|
+
给 WS 帧加上 `streamId → endpoint` 归属后,热读长会话看到
|
|
180
|
+
`session/control` 6 830 981 B 与 `session/follow` 6 835 203 B 两帧相邻,
|
|
181
|
+
follow 比 control 晚 299 ms——`stream-server` 的 `this.writes` 是跨逻辑流的
|
|
182
|
+
串行队列,control 基线本身就是 6.8 MB 量级。
|
|
183
|
+
|
|
184
|
+
浏览器解析 6.8 MB JSON 只要 20–30 ms(V8 很快)。**旧的「解压假设」被推翻。**
|
|
185
|
+
|
|
186
|
+
### markdown / KaTeX:op 级隔离
|
|
187
|
+
|
|
188
|
+
`__dshPerf.benchMarkdown` 在页面里分别跑四个引擎(1 476 字符 / 4 围栏 / 4 公式):
|
|
189
|
+
|
|
190
|
+
| 操作 | 每次 |
|
|
191
|
+
|---|---:|
|
|
192
|
+
| GFM 解析 | 7.46 ms |
|
|
193
|
+
| 建元素 + commit(分离容器,无布局) | **23.9 ms** |
|
|
194
|
+
| KaTeX | 1.28 ms / 式 |
|
|
195
|
+
| shiki tokenize | 3.5 ms / 145 字符 |
|
|
196
|
+
|
|
197
|
+
单条 1.5 KB 消息的 render 是 24 ms,其中解析只占 7.5 ms——**大头是 React 元素
|
|
198
|
+
创建与 commit**。离屏代码块永不进入视口门控,所以分离容器的 render 不含 shiki;
|
|
199
|
+
高亮单独用 `useCodeHighlighter` 回调量(结果里带 `renderIncludesHighlight: false`)。
|
|
200
|
+
|
|
201
|
+
### 30k 跳转:不是慢,是大概率崩
|
|
202
|
+
|
|
203
|
+
新增 `__dshPerf.jumpToTurn(n)`,阶梯式跳长会话(30 000 turn):
|
|
204
|
+
|
|
205
|
+
| 目标 | 行数 | 最长任务 | 阻塞 | 堆 |
|
|
206
|
+
|---|---:|---:|---:|---:|
|
|
207
|
+
| 29 900 | 100 → 500 | 576 ms | 603 ms | — |
|
|
208
|
+
| 29 500 | 500 → 2 100 | 1 782 ms | 1 982 ms | — |
|
|
209
|
+
| 29 000 | 2 100 → 4 100 | 2 293 ms | 2 940 ms | — |
|
|
210
|
+
| 25 000 | 4 100 → **20 100** | — | — | **728 MB** |
|
|
211
|
+
|
|
212
|
+
斜率 ≈1.0–1.15 ms 阻塞/行、≈36 KB 堆/行。外推 turn 1:**60–70 s 阻塞、约 2 GB
|
|
213
|
+
堆**,默认 2 GB heap 上大概率 OOM。按计划的停止条件(rows > 20 000)在
|
|
214
|
+
20 100 行处停止,没有继续冒险。
|
|
215
|
+
|
|
216
|
+
### 方差:已量化,且发现了测量污染
|
|
217
|
+
|
|
218
|
+
同一 tiny 会话三次:首行 1 745 / 4 679 / 1 497 ms。中间那次继承了上一个
|
|
219
|
+
20 100 行页面留下的 **797 MB 堆 + 2 988 ms GC 任务**。**复用标签页会污染下一次
|
|
220
|
+
测量**;干净读数需要新标签页或 GC 静置。
|
|
221
|
+
|
|
222
|
+
### 验证
|
|
223
|
+
|
|
224
|
+
- `verify_compat.mjs` 82 → 88 条:WS socket 是普通运行时属性、帧从监听器同步
|
|
225
|
+
接收、服务端 stringify + 串行写队列、客户端 WS 帧/`benchMarkdown`/`jumpToTurn`。
|
|
226
|
+
- 新增 `tools/runtime-probe/bench-encode.mjs`。
|
|
227
|
+
|
|
228
|
+
<a id="v1-13-0"></a>
|
|
229
|
+
## [1.13.0] — 2026-11-13 — ⚠️ 重要(测量)
|
|
230
|
+
|
|
231
|
+
**把 1.12 留下的四条测量缺口逐条闭合**,并修掉采集器里三个会污染结论的缺陷。
|
|
232
|
+
|
|
233
|
+
### 修复的测量缺陷
|
|
234
|
+
|
|
235
|
+
- **阻塞时长改用 LoAF 的 `blockingDuration`**(规范值:各长任务 -50 的累加 +
|
|
236
|
+
渲染段)。此前用 `duration - 50`,会系统性高估。
|
|
237
|
+
- **首行时间不再经过 120 ms 防抖**:在 `MutationObserver` 回调(变更后的微任务)
|
|
238
|
+
里同步记录,防抖只留给 settle 判定。
|
|
239
|
+
- **采集 `renderStart` / `styleAndLayoutStart` / `scripts[]`**,把长帧拆成
|
|
240
|
+
脚本 / 渲染 / 布局,并给出脚本 URL 归属。
|
|
241
|
+
|
|
242
|
+
### 新增测量能力
|
|
243
|
+
|
|
244
|
+
- **分段计时**:`performance.mark` 打在流打开 / 首个 item / 首行 / 首帧 / 稳定,
|
|
245
|
+
样本带最多 16 条时间线;配 100 ms 心跳(`heartbeatMaxLagMs`)区分「填异步等待」
|
|
246
|
+
与「大量短任务」。
|
|
247
|
+
- **逻辑流标签**:包装 `ctx.remote.$stream` 并保留 `options.name`,区分
|
|
248
|
+
`session event stream` / `session control stream`,记录「打开 → 首个 item」。
|
|
249
|
+
只在 `renderTiming=true` 时安装(它会改写应用所有逻辑流的公共入口)。
|
|
250
|
+
- **回合跳转窗口**:服务端把间隔 < 1 500 ms 的连续分页聚合成 `kind:'jump'`
|
|
251
|
+
(页数/事件数/字节/耗时);客户端记录行数与 turn 范围的变化。
|
|
252
|
+
- **滚动跳变**:逐帧采样 `scrollTop`,记录单帧最大位移、方向反转、锚点行漂移、
|
|
253
|
+
`scrollHeight` 变化与输入意图。
|
|
254
|
+
- **`window.__dshPerf`**:`state()` / `jumpToFirst()` / `scrollTest({rounds})` / `flush()`。
|
|
255
|
+
- `perf.report` 改用独立的 64 KiB body 上限;非 200 时只丢最旧一半,不再清空整队。
|
|
256
|
+
|
|
257
|
+
### 新增素材
|
|
258
|
+
|
|
259
|
+
- `make-session-cli.mjs` 支持 `--turns` / `--profile plain|markdown|tools` /
|
|
260
|
+
`--tool-bytes` / `--seed`。`tools` 档位按真实编码 agent 的字节分布生成
|
|
261
|
+
bash 日志、文件读与 unified diff。
|
|
262
|
+
- **读回自检**:生成后用真实后端重新读出,核对事件数、turn 数、工具配对;
|
|
263
|
+
失败直接退出 1。顺带修正了生成器注释里的错误——工具生命周期由
|
|
264
|
+
`assistant/message` 的 `tool-call` 内容块声明,不是 `request/header.tools`。
|
|
265
|
+
|
|
266
|
+
### 实测结论
|
|
267
|
+
|
|
268
|
+
| 缺口 | 结果 |
|
|
269
|
+
|---|---|
|
|
270
|
+
| 消息体太小 | 重会话首屏 **1.73 MB**(长会话 74 KB 的 23 倍),投影只 68 KB——两轴分开 |
|
|
271
|
+
| 回合跳转 | 跳到 turn 1:**+1 375 行、阻塞 1 723 ms、最长任务 1 575 ms**、服务端一次发布 18.3 MB |
|
|
272
|
+
| 打开路径 | 拆成服务端首帧(热 52–188 ms)+ **约 500 ms 链路** + 100–260 ms 组装挂载;旧的「2.9 s 未归因」大部分其实是服务端时间 |
|
|
273
|
+
| 滚动跳变 | containment 开/关的锚点漂移都是 **22 px**,未测到额外跳变;默认维持开启 |
|
|
274
|
+
|
|
275
|
+
完整数据、剩余开放项与上游建议见 [docs/RENDER-ANALYSIS.md](docs/RENDER-ANALYSIS.md)。
|
|
276
|
+
|
|
277
|
+
### 验证
|
|
278
|
+
|
|
279
|
+
- `tests/test_render_timing.mjs` 30 → 40 条:`startedAt`/`sessionId`/`beforeSeq`、
|
|
280
|
+
jump burst 聚合(3 页合成 1 条)、单页不记跳转。
|
|
281
|
+
- `verify_compat.mjs` 72 → 82 条:客户端新增标记、`$stream` 契约、
|
|
282
|
+
`loadThrough` 单次发布、`data-chat-history` / `aria-busy` /
|
|
283
|
+
`data-conversation-scroll` 三个 DOM 契约。
|
|
284
|
+
- `tools/runtime-probe` 新增 **scenario I**(重会话载荷 + control 基线量级)与
|
|
285
|
+
**scenario J**(连续回翻 → 聚合为一次 jump)。
|
|
286
|
+
|
|
287
|
+
<a id="v1-12-0"></a>
|
|
288
|
+
## [1.12.0] — 2026-11-13 — ⚠️ 重要(性能)
|
|
289
|
+
|
|
290
|
+
**首次把测量推进到浏览器侧。** 此前所有数字都是服务端的冷读/RSS,渲染从未被观测。
|
|
291
|
+
|
|
292
|
+
### 新增
|
|
293
|
+
|
|
294
|
+
- **`lib/render-timing.js`(`renderTiming`,默认关)**:包装
|
|
295
|
+
`sessionProjections.snapshot` 与 `sessionController` 的
|
|
296
|
+
`webController.follow` / `historyController.events`,记录每次打开/翻页的
|
|
297
|
+
耗时、事件数与序列化字节;浏览器样本落在同一个 `stats.render` 下。
|
|
298
|
+
- **`lib/client.js`(首个浏览器端 bundle)**:`dsh.client = { platform: "web" }`
|
|
299
|
+
\+ `exports["./client"]`,普通 `__ModuleLoader__.load` 脚本,无需构建。
|
|
300
|
+
采集 `long-animation-frame` / `longtask` 与「导航 → 首行 / 稳定」,回传
|
|
301
|
+
`perf.report`。
|
|
302
|
+
- **`lib/history-budget.js`(`maxInitialMessages` / `maxPageBytes`,默认关)**:
|
|
303
|
+
首屏消息上限(同时压 `turnWindow.minMessages`)与单页字节上限。
|
|
304
|
+
- **`clientContainment`(默认开)**:长转录(>80 行)给第 30 行之后的离屏行加
|
|
305
|
+
`content-visibility: auto`。
|
|
306
|
+
|
|
307
|
+
### 实测结论
|
|
308
|
+
|
|
309
|
+
- **一次打开 = 74 KB 消息 + 6.76 MB 投影。** 投影是消息的 **91 倍**,
|
|
310
|
+
`turnOutline` 一个单元装着全部 30 000 个 turn。把首屏从 50 条压到 25 条只省
|
|
311
|
+
0.5% 的字节——**首屏消息窗口不是瓶颈**,这是本轮最重要的否证。
|
|
312
|
+
- **导航 → 首行 7.4 s**:页面装载 1.6 s + 服务端首帧 2.5 s + 客户端阻塞 0.44 s
|
|
313
|
+
\+ 未归因等待 2.9 s。
|
|
314
|
+
- **批量翻页才是「分钟级」的来源**:快速连续翻 10 页时客户端合并发布,
|
|
315
|
+
一次性挂载上千行。无 containment:11 个长任务 / 2836 ms 阻塞 / 最长 416 ms;
|
|
316
|
+
有 containment:18 个长任务 / **1489 ms 阻塞(-47%)** / 最长 244 ms。
|
|
317
|
+
- 稳态翻页很便宜:每页 200 事件 / 70 KB / 服务端 0.3–0.6 ms,单页无长任务;
|
|
318
|
+
但 DOM 与堆单调增长(≈73 KB/行,600 行涨 44 MB)。
|
|
319
|
+
|
|
320
|
+
完整数据、归因与上游建议见新增的 [docs/RENDER-ANALYSIS.md](docs/RENDER-ANALYSIS.md)。
|
|
321
|
+
|
|
322
|
+
### 验证
|
|
323
|
+
|
|
324
|
+
- 新增 `tests/test_render_timing.mjs`(30 条):配置门禁、计时点、窗口截断、
|
|
325
|
+
字节上限、dispose 还原、安装时快照语义。
|
|
326
|
+
- `verify_compat.mjs` 新增 14 条:客户端清单(声明 + 文件存在 + 包名 id +
|
|
327
|
+
工厂形状 + 配置门禁)、**上游渲染形状**(`ChatFlow` 仍无虚拟化、分页仍无字节
|
|
328
|
+
上限、首屏仍是 500、`snapshot` 仍是同步)。
|
|
329
|
+
- `tools/runtime-probe` 新增 **scenario H**:量首屏/分页的字节数与服务端耗时,
|
|
330
|
+
对比 `maxInitialMessages` 截断。
|
|
331
|
+
- `0.2.2-rc.2` 登记进 `KNOWN_DSH_VERSIONS`(`npm test` 对 rc.2 的真实包源码全绿)。
|
|
332
|
+
|
|
333
|
+
<a id="v1-11-0"></a>
|
|
334
|
+
## [1.11.0] — 2026-11-11 — ⚠️ 重要(破坏性)
|
|
335
|
+
|
|
336
|
+
**支持范围收紧到 `>=0.2.2-rc.1 <0.3.0-0`。** 更旧的 dsh 上插件不再加载。
|
|
337
|
+
|
|
338
|
+
### Changed
|
|
339
|
+
|
|
340
|
+
- **`engines.dsh` 从 `>=0.1.0-rc.6 <0.3.0-0` 收紧为 `>=0.2.2-rc.1 <0.3.0-0`**,
|
|
341
|
+
`peerDependencies` 里 `@deepseek-ai/dsh` 与 `@deepseek-ai/dsh-session` 同步收紧。
|
|
342
|
+
理由:v1.10.0 删掉三项依赖旧结构的能力后,0.1.x 上实际还能工作的只剩零拷贝 fork
|
|
343
|
+
与分片 materialize,而这两项在 UI 链路上要么不被调用(零拷贝 fork,见 README
|
|
344
|
+
「两条必须知道的限制」),要么只影响内存峰值不影响吞吐。让旧版本装上一个近乎
|
|
345
|
+
空转的插件,比让门禁明确拒绝更糟。
|
|
346
|
+
- **`KNOWN_DSH_VERSIONS` 收敛为 `['0.2.2-rc.1']`**。历史上的 0.1.x / 0.2.0 / 0.2.1
|
|
347
|
+
适配记录保留在 `docs/COMPATIBILITY.md` 的版本矩阵里(它们解释了代码中
|
|
348
|
+
`0.1.3 eventState` 协议、legacy `encodeMaterialization` 签名等历史分支的来历),
|
|
349
|
+
但不再进入运行时「已验证」清单——那些版本已进不来。
|
|
350
|
+
- **README 标题改为 `DSH Large Project Performance`**,开头新增「最近发生重要更新」
|
|
351
|
+
提示块(破坏性变更 + 性能里程碑 + 升级指引)。dsh 版本徽章改为
|
|
352
|
+
`>=0.2.2-rc.1`。
|
|
353
|
+
|
|
354
|
+
### 新增的验证
|
|
355
|
+
|
|
356
|
+
收紧声明范围不是改字符串就完事——声明错了会让插件在支持版本上被静默拒绝。为此加了
|
|
357
|
+
两条断言(`tests/verify_compat.mjs`):
|
|
358
|
+
|
|
359
|
+
| 断言 | 防的是什么 |
|
|
360
|
+
|---|---|
|
|
361
|
+
| `lib/runtime.js` 与 `tests/verify_compat.mjs` 的已验清单逐项相同 | 两处清单漂移后,「实测过」与「运行时告警」会互相矛盾 |
|
|
362
|
+
| 已验清单里每个版本都落在 `engines.dsh` 内 | 实测通过新版本、却忘了同步 `engines.dsh` 时,宿主门禁会跳过加载,而运行时清单还在说「已验证」 |
|
|
363
|
+
|
|
364
|
+
另外用 dsh 自带的 `evaluatePluginCompatibility`(与宿主同一份实现)实测了门禁行为:
|
|
365
|
+
|
|
366
|
+
| 运行时 dsh | `>=0.2.2-rc.1` | `>=0.2.2-rc.1 <0.3.0-0` |
|
|
367
|
+
|---|---|---|
|
|
368
|
+
| 0.2.2-rc.1 | ACCEPT | **ACCEPT** |
|
|
369
|
+
| 0.2.1-rc.1 | SKIP | SKIP |
|
|
370
|
+
| 0.1.7-rc.2 | SKIP | SKIP |
|
|
371
|
+
| 0.2.3 | ACCEPT | ACCEPT |
|
|
372
|
+
| 0.3.0-rc.1 | ACCEPT(需要上界) | SKIP |
|
|
373
|
+
|
|
31
374
|
## [1.10.1] — 2026-11-11
|
|
32
375
|
|
|
33
376
|
文档全面重写。不是功能版本,但修掉了多处会让读者得出错误结论的文档缺陷。
|
|
@@ -88,6 +431,7 @@ v1.10.0 的 CHANGELOG 说「保留该状态是因为未来某个补丁退役时
|
|
|
88
431
|
核实:**当前没有任何代码路径会设置它**。已在 ARCHITECTURE §5.3 写明这一点,并注明
|
|
89
432
|
「如果长期无人使用,可以在未来的大版本里删掉」——不再把它当作已有功能的描述。
|
|
90
433
|
|
|
434
|
+
<a id="v1-10-0"></a>
|
|
91
435
|
## [1.10.0] — 2026-11-11 — ⚠️ 重要(破坏性)
|
|
92
436
|
|
|
93
437
|
**结构调整版。没有新增优化能力,只做减法与去重——并修掉一个藏了两轮的 bug。**
|
|
@@ -175,6 +519,7 @@ v1.10.0 的 CHANGELOG 说「保留该状态是因为未来某个补丁退役时
|
|
|
175
519
|
- **没有合并 `backfill` 与 `cold-writeback` 的折叠循环**。两者的数据来源不同
|
|
176
520
|
(前者逐行流式解析日志,后者已有事件数组),强行合并会引入条件分支而不减少复杂度。
|
|
177
521
|
|
|
522
|
+
<a id="v1-9-0"></a>
|
|
178
523
|
## [1.9.0] — 2026-11-11 — ⚠️ 重要(性能)
|
|
179
524
|
|
|
180
525
|
**空间维度的第一轮:把事件缓存从「按条数限容」改成「按字节限容」。**
|
|
@@ -242,6 +587,7 @@ dsh 有 **5 处**会持有完整事件数组 / 整棵事件树,其中 3 处按
|
|
|
242
587
|
会改变客户端跳转语义。本轮只在文档里记录这个成本。
|
|
243
588
|
- **空闲时主动驱逐**:属于「按需回收」,比「限容」复杂且需要触发时机,留待后续。
|
|
244
589
|
|
|
590
|
+
<a id="v1-8-0"></a>
|
|
245
591
|
## [1.8.0] — 2026-11-11 — ⚠️ 重要(性能)
|
|
246
592
|
|
|
247
593
|
**冷读 21.2 秒 → 3.0 秒(7×)。** 前两轮都在猜瓶颈且猜错;这一轮先做归因,
|
|
@@ -631,6 +977,7 @@ JS、无平台限定,保持不声明。
|
|
|
631
977
|
从 `ctx.sessionPersistence` 取实例(同时保留 0.2.0 及之前的原型直取路径)。
|
|
632
978
|
- 已知版本列表加入 `0.2.1-rc.1`。
|
|
633
979
|
|
|
980
|
+
<a id="v1-3-0"></a>
|
|
634
981
|
## [1.3.0] — 2026-10-11 — ⚠️ 重要(破坏性)
|
|
635
982
|
|
|
636
983
|
面向 **dsh 0.2.0** 的发布准备版本。
|
|
@@ -701,6 +1048,7 @@ JS、无平台限定,保持不声明。
|
|
|
701
1048
|
> 该版本的变更记录未完整留存;随后 1.2.0 重新记录了本阶段引入的能力
|
|
702
1049
|
> (`stats` 生命周期状态、冷会话 LRU 裁剪、heap 检测),以其为准。
|
|
703
1050
|
|
|
1051
|
+
<a id="v1-0-0"></a>
|
|
704
1052
|
## [1.0.0] — 2026-08 — 首个版本
|
|
705
1053
|
|
|
706
1054
|
### Added
|
package/README.md
CHANGED
|
@@ -1,34 +1,57 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DSH Large Project Performance
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/dsh-large-proj-perf)
|
|
4
4
|
[](https://www.npmjs.com/package/dsh-large-proj-perf)
|
|
5
5
|
[](https://www.npmjs.com/package/dsh-large-proj-perf)
|
|
6
|
-
[]()
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
|
|
9
|
+
> **⚠️ 最近发生重要更新**
|
|
10
|
+
>
|
|
11
|
+
> - **1.10.0(破坏性)**移除三项在 dsh 0.2.2 上恒不生效的能力,配置键从 22 个减到
|
|
12
|
+
> 17 个(`warmupEnabled` / `warmOnCreated` / `fastInitFor` / `preparedCacheTrim` /
|
|
13
|
+
> `preparedCacheSize` 已删除),并修掉「改配置不生效」的晚绑定 bug。
|
|
14
|
+
> - 支持范围收紧为 **dsh >= 0.2.2-rc.1 且 < 0.3.0**。更旧的 dsh 上会被加载门禁
|
|
15
|
+
> 跳过(日志 `skipping profile bundle`)。
|
|
16
|
+
> - 性能里程碑:**1.8.0** 冷读 21.2 s → 3.0 s;**1.9.0** 稳态 RSS 1324 → 690 MB;
|
|
17
|
+
> **1.12.0** 浏览器侧离屏行 containment,批量翻页阻塞 2 836 → 1 489 ms(约 −47%),
|
|
18
|
+
> 首次实测渲染链路。
|
|
19
|
+
> - **1.16.0** 收敛测量文档并修诊断设施的结构债,无用户可见能力变化。
|
|
20
|
+
>
|
|
21
|
+
> 从 1.4.0 或更早升级的用户,升级前请先读
|
|
22
|
+
> [CHANGELOG 的重要版本速览](CHANGELOG.md#重要版本速览)。
|
|
23
|
+
|
|
9
24
|
DeepSeek Harness(dsh)**大会话**性能插件。针对数十万事件的长会话,降低打开时的
|
|
10
25
|
延迟与内存占用。
|
|
11
26
|
|
|
12
|
-
>
|
|
27
|
+
> 包名里的 "large-proj" 是历史遗留;它优化的对象是**会话规模**,与项目大小无关。
|
|
13
28
|
|
|
14
29
|
每项能力都带**源码特征校验 + 协议探测 + 三层回退**(能力探测 / try-catch / 配置
|
|
15
30
|
开关)。dsh 内部结构变化时对应补丁会自动跳过并告警,功能回退到官方实现,不会出错。
|
|
16
31
|
|
|
17
32
|
## 能力与效果
|
|
18
33
|
|
|
19
|
-
数字都是 dsh 0.2.2
|
|
20
|
-
[docs/MEASUREMENTS.md](docs/MEASUREMENTS.md)
|
|
34
|
+
数字都是 dsh 0.2.2 上的实测值,来源与测量边界见
|
|
35
|
+
[docs/MEASUREMENTS.md](docs/MEASUREMENTS.md)(时间/空间)与
|
|
36
|
+
[docs/RENDER-ANALYSIS.md](docs/RENDER-ANALYSIS.md)(浏览器渲染)。
|
|
21
37
|
|
|
22
38
|
| 能力 | 解决什么 | 实测效果 | 默认 |
|
|
23
39
|
|---|---|---|---|
|
|
24
40
|
| **turnOutline 折叠加速** | 该投影单元每次 turn 整数组复制(O(n²)),占冷读 76% | 冷读 **21.2 s → 3.0 s** | 开 |
|
|
25
41
|
| **事件缓存字节预算** | dsh 的缓存只按「会话条数」限容;6 个 18 MB 会话涨到 1.2 GB | 稳态 RSS **1324 → 690 MB** | 开(512 MiB) |
|
|
42
|
+
| **离屏行 containment** | 聊天列表没有虚拟滚动,已加载的每一行都参与布局 | 批量翻页阻塞 **2 836 → 1 489 ms**(约 −47%),最长任务 416 → 244 ms;未测到滚动漂移 | 开 |
|
|
26
43
|
| 分片 materialize | 落盘时把整个会话序列化成一个巨大字符串 | 2 帧 → 6 帧,消除 `RangeError` | 开 |
|
|
27
44
|
| 零拷贝 fork | fork 时逐事件深拷贝整个会话 | 直测 -16% ~ -21%(**但 UI 不走这条路**,见下) | 开 |
|
|
45
|
+
| 渲染耗时诊断 | 打开会话慢在哪一段无从得知 | 五段归因(请求/服务端/响应/派发/挂载)、`ws.send` 拆写队列与 deflate、LoAF 脚本归属、跳转与滚动窗口 | 关 |
|
|
46
|
+
| 历史窗口预算 | 一页只按消息条数切,无字节上限 | 长会话 70 KB/页、重会话 1.7 MB/页;默认不动,按需收紧 | 关 |
|
|
28
47
|
| 冷读回写 | 冷读后把投影状态写成缓存行 | **无耗时收益**,只让列表读看到新数据 | 关 |
|
|
29
48
|
| 冷会话补行 | 磁盘上的大会话没有投影缓存行 | 启动 15 s 后批量补写 | 关 |
|
|
30
49
|
| heap 上限告警 | V8 heap 上限过低时无任何提示 | 启动时检查并告警 | — |
|
|
31
50
|
|
|
51
|
+
> 浏览器侧唯一有实测收益的**优化**是 `clientContainment`;`renderTiming` 只采集
|
|
52
|
+
> 数据,不改变渲染行为。两者的测量条件与边界见
|
|
53
|
+
> [docs/RENDER-ANALYSIS.md](docs/RENDER-ANALYSIS.md) §7/§9。
|
|
54
|
+
|
|
32
55
|
### 两条必须知道的限制
|
|
33
56
|
|
|
34
57
|
**① `stats.patches` 显示 `active` 不等于在真实链路上被调用。**
|
|
@@ -45,7 +68,7 @@ DeepSeek Harness(dsh)**大会话**性能插件。针对数十万事件的长
|
|
|
45
68
|
|
|
46
69
|
## 安装
|
|
47
70
|
|
|
48
|
-
要求 Node **≥ 22.15.0**(需要 `node:zlib` 的 zstd
|
|
71
|
+
要求 Node **≥ 22.15.0**(需要 `node:zlib` 的 zstd 接口)与 **dsh ≥ 0.2.2-rc.1**。
|
|
49
72
|
|
|
50
73
|
```sh
|
|
51
74
|
# 从 npm 安装(推荐)
|
|
@@ -96,6 +119,7 @@ node --max-old-space-size=8192 <dsh 入口> web
|
|
|
96
119
|
|---|---|---|---|
|
|
97
120
|
| `fastTurnOutline` | `true` | — | `turnOutline` 的 O(n²) → O(n) |
|
|
98
121
|
| `memoryBudgetBytes` | `536870912` | 字节 | 事件缓存总预算(512 MiB);`0` 关闭 |
|
|
122
|
+
| `clientContainment` | `true` | — | 长转录离屏行 `content-visibility`(批量翻页阻塞约 −47%) |
|
|
99
123
|
| `heapWarnBytes` | `6442450944` | 字节 | heap 上限告警阈值(6 GiB) |
|
|
100
124
|
| `zeroCopyFork` | `true` | — | 零拷贝 fork |
|
|
101
125
|
| `slowForkWarnMs` | `100` | ms | fork 耗时超过此值打告警 |
|
|
@@ -105,6 +129,9 @@ node --max-old-space-size=8192 <dsh 入口> web
|
|
|
105
129
|
| `chunkSize` | `5000` | 事件 | 分片遍历每片的事件数(下限 1) |
|
|
106
130
|
| `chunkYieldMs` | `0` | ms | 让出方式:0 = `setImmediate`,>0 = `setTimeout` |
|
|
107
131
|
| `keepRecent` | `50` | 条 | 内存保留的最近统计记录数 |
|
|
132
|
+
| `renderTiming` | `false` | — | 渲染诊断:服务端分页/投影计时 + 浏览器首行/长任务 |
|
|
133
|
+
| `maxInitialMessages` | `0` | 条 | 首屏消息上限(同时压 `turnWindow.minMessages`);`0` 不限制 |
|
|
134
|
+
| `maxPageBytes` | `0` | 字节 | 单页估算字节上限;`0` 不限制 |
|
|
108
135
|
| `coldWriteBack` | `false` | — | 冷读后回写投影缓存行(无耗时收益) |
|
|
109
136
|
| `backfillOnBoot` | `false` | — | 启动后补写磁盘冷会话的投影缓存行 |
|
|
110
137
|
| `backfillMaxSessions` | `8` | 个 | 补行扫描会话数上限 |
|
|
@@ -112,8 +139,10 @@ node --max-old-space-size=8192 <dsh 入口> web
|
|
|
112
139
|
| `backfillMaxBytes` | `33554432` | 字节 | 补行的最大文件大小(32 MiB) |
|
|
113
140
|
| `debugApi` | `false` | — | 暴露 `debug.fork` 调试端点(仅用于测量) |
|
|
114
141
|
|
|
115
|
-
**哪些键运行期改得动**:`fastTurnOutline` / `chunkedMaterialize` / `zeroCopyFork`
|
|
116
|
-
|
|
142
|
+
**哪些键运行期改得动**:`fastTurnOutline` / `chunkedMaterialize` / `zeroCopyFork` /
|
|
143
|
+
`maxInitialMessages` / `maxPageBytes` / `renderTiming` / `memoryBudgetBytes` /
|
|
144
|
+
`heapWarnBytes` 只在**安装时**读一次,改了要重启(或用启动期环境变量,见下);
|
|
145
|
+
`clientContainment` 由浏览器在启动时取一次。其余键在下次使用时重读。
|
|
117
146
|
|
|
118
147
|
启动期还可用环境变量覆盖任意配置键(仅用于离线测量):键名是大写蛇形加
|
|
119
148
|
`DSH_PERF_` 前缀,例如 `DSH_PERF_CHUNKED_MATERIALIZE=false`。
|
|
@@ -131,6 +160,7 @@ node --max-old-space-size=8192 <dsh 入口> web
|
|
|
131
160
|
| `stats.reset` | 清零计数(保留 `patches` 与 `dshVersion`) |
|
|
132
161
|
| `config.get` / `config.set` | 读取/修改运行时开关,`set` 同时写 settings 持久化 |
|
|
133
162
|
| `debug.fork` | 仅当 `debugApi=true` 时存在:直接调用 `SessionStore.prototype.fork` |
|
|
163
|
+
| `perf.report` | 仅当 `renderTiming=true` 时存在:浏览器回传首行/稳定/长任务样本 |
|
|
134
164
|
|
|
135
165
|
```sh
|
|
136
166
|
curl -X POST http://127.0.0.1:3080/dsh-large-proj-perf/api/stats.get
|
|
@@ -151,12 +181,29 @@ curl -X POST http://127.0.0.1:3080/dsh-large-proj-perf/api/config.set \
|
|
|
151
181
|
"dshVersion": "0.2.2-rc.1",
|
|
152
182
|
"patches": { "zeroCopyFork": "active", "fastTurnOutline": "active", ... },
|
|
153
183
|
"forks": 0, "zeroCopy": 0, "fallbacks": 0, "forkRecent": [],
|
|
154
|
-
"memoryBudget": { "budgetBytes": 536870912, "cachedEntries": 0, "estimatedBytes": 0 },
|
|
184
|
+
"memoryBudget": { "budgetBytes": 536870912, "cachedEntries": 0, "estimatedBytes": 0 }, // 关闭时恒为 null
|
|
155
185
|
"memoryBudgetTrimmed": 0, "memoryBudgetFreedBytes": 0, "memoryBudgetOverBudget": 0,
|
|
156
|
-
"coldWriteBacks": 0, "backfilled": 0, "backfillSkipped": 0
|
|
186
|
+
"coldWriteBacks": 0, "backfilled": 0, "backfillSkipped": 0,
|
|
187
|
+
"render": {
|
|
188
|
+
"snapshotCount": 0, "snapshotMs": 0, "snapshotMaxMs": 0, "lastUnits": 0,
|
|
189
|
+
"pages": [], "sends": [], "jumps": 0,
|
|
190
|
+
"clients": [], "reports": 0, "dropped": 0
|
|
191
|
+
}
|
|
157
192
|
}
|
|
158
193
|
```
|
|
159
194
|
|
|
195
|
+
`memoryBudget` 在预算关闭/不适用时恒为 `null`——字段始终存在,消费端不必判断键是否
|
|
196
|
+
存在。`render` 只在 `renderTiming=true` 时有值:`pages` 是服务端每页的
|
|
197
|
+
`{kind, ms, startedAt, records, bytes}`(jump 条目用 `recordsTotal` / `bytesTotal`,
|
|
198
|
+
表示跨页合计);`sends` 是 `ws.send` 的 `{t, sentAt, bytes}`;`clients` 是浏览器回传的
|
|
199
|
+
`{kind, rows, firstRowMs, firstFrameMs, settleMs, longTasks, blockingMs, maxTaskMs,
|
|
200
|
+
heartbeatMaxLagMs, streams, timeline}`;`jumps` 是服务端识别出的回合跳转次数
|
|
201
|
+
(连续分页聚合)。读法与实测见 [docs/RENDER-ANALYSIS.md](docs/RENDER-ANALYSIS.md)。
|
|
202
|
+
|
|
203
|
+
诊断模式还会暴露 `window.__dshPerf`:`state()` 看流表、WS 帧与最近样本,
|
|
204
|
+
`jumpToTurn(n)` / `scrollTest({rounds})` / `benchMarkdown({text, iterations})`
|
|
205
|
+
驱动固定动作,`stop()` 停采集并还原 `$stream` / `socket.send`(`pagehide` 也会调用)。
|
|
206
|
+
|
|
160
207
|
判断某能力是否真的在起作用,看命中计数而不是 `patches`:
|
|
161
208
|
|
|
162
209
|
| 想知道 | 看 |
|
|
@@ -164,6 +211,7 @@ curl -X POST http://127.0.0.1:3080/dsh-large-proj-perf/api/config.set \
|
|
|
164
211
|
| 零拷贝 fork 有没有被调用 | `zeroCopy`(长期 0 = 没被调用) |
|
|
165
212
|
| 内存预算有没有在淘汰 | `memoryBudgetTrimmed`(>0 说明生效) |
|
|
166
213
|
| 冷读回写 / 补行有没有跑 | `coldWriteBacks` / `backfilled` |
|
|
214
|
+
| 有没有发生回合跳转 | `render.jumps` / `render.pages` 里的 `kind:'jump'` |
|
|
167
215
|
|
|
168
216
|
### 常见问题
|
|
169
217
|
|
|
@@ -181,16 +229,17 @@ dsh 内部结构已变,对应补丁已自动跳过(功能回退到官方实
|
|
|
181
229
|
|
|
182
230
|
**日志出现 `skipping profile bundle`**
|
|
183
231
|
插件被 dsh 的版本门禁拦下了。从 dsh 0.2.1 起,门禁读插件 `package.json` 的
|
|
184
|
-
`engines.dsh`;本插件声明 `>=0.
|
|
232
|
+
`engines.dsh`;本插件声明 `>=0.2.2-rc.1 <0.3.0-0`。dsh 超出该范围时,要么升级插件,
|
|
185
233
|
要么用 `dsh plugin allow-version <pkg>@<ver> --profile web` 做精确版本豁免(有风险)。
|
|
186
234
|
|
|
187
235
|
**API 请求 404 或连不上**
|
|
188
236
|
端口可能不是 3080。看 `dsh web` 启动日志里打印的 URL,或 `dsh web --help` 确认。
|
|
189
237
|
|
|
190
238
|
**改完配置没反应**
|
|
191
|
-
三种情况:`fastTurnOutline` / `chunkedMaterialize` / `zeroCopyFork`
|
|
192
|
-
|
|
193
|
-
`
|
|
239
|
+
三种情况:`fastTurnOutline` / `chunkedMaterialize` / `zeroCopyFork` /
|
|
240
|
+
`maxInitialMessages` / `maxPageBytes` 只在安装时读一次,改了要重启;
|
|
241
|
+
`clientContainment` 由浏览器在页面启动时取一次,改完刷新页面;`backfillOnBoot`
|
|
242
|
+
相关的补行任务在启动 15 秒后才跑;其余开关通过 `config.set` 修改后即时生效。
|
|
194
243
|
|
|
195
244
|
**没有任何 `[dsh-perf]` 日志且 `stats.get` 也连不上**
|
|
196
245
|
插件没被加载。检查 `dsh --profile web --dump-config` 里是否有 `large-proj-perf`
|
|
@@ -202,10 +251,19 @@ dsh 内部结构已变,对应补丁已自动跳过(功能回退到官方实
|
|
|
202
251
|
建议配合 [dsh-fresh-start](https://github.com/orangeofcarl0-sys/dsh-fresh-start)
|
|
203
252
|
主动控制会话规模。
|
|
204
253
|
- **零拷贝 fork 在 dsh 0.2.2 的 UI fork 链路上不生效**(原因见上)。
|
|
205
|
-
-
|
|
206
|
-
|
|
207
|
-
materialize
|
|
208
|
-
|
|
254
|
+
- **只验证过 dsh 0.2.2**。`engines.dsh` 声明为 `>=0.2.2-rc.1 <0.3.0-0`——更旧的
|
|
255
|
+
dsh 上插件能力已基本为零(v1.10.0 移除三项依赖旧结构的能力后,0.1.x 只剩零拷贝
|
|
256
|
+
fork 与分片 materialize),因此直接由门禁拒绝,而不是让用户装上一个近乎空转的
|
|
257
|
+
插件。0.3.x 上会被同样拒绝,届时需要新版本插件。
|
|
258
|
+
- **打开一个会话的字节大头取决于会话类型**:长会话(30 000 turn)是投影主导——
|
|
259
|
+
实测 74 KB 消息 + **6.76 MB 投影**(`turnOutline` 装着全部 turn);重会话
|
|
260
|
+
(300 turn / 620 次工具调用)反过来,**1.73 MB 消息 + 68 KB 投影**。两种都不是
|
|
261
|
+
插件能削减的部分,诊断数据与上游建议见
|
|
262
|
+
[docs/RENDER-ANALYSIS.md](docs/RENDER-ANALYSIS.md) §5。
|
|
263
|
+
- **浏览器侧没有虚拟滚动**(上游 `ChatFlow` 直接 `entries.map()`)。插件只能用
|
|
264
|
+
`content-visibility` 减轻离屏行的布局代价,不能减少挂载数量;向上翻页会持续
|
|
265
|
+
累积 DOM 与堆(实测 600 行区间约 73 KiB/行)。**回合跳转是最极端的路径**:一次
|
|
266
|
+
跳到早期 turn 会挂载上千行(实测 +1 375 行、主线程阻塞 1.7 秒)。
|
|
209
267
|
- **`retired` 补丁状态当前无使用者**:v1.10.0 移除了唯一会设置它的能力。保留该状态
|
|
210
268
|
是因为它是「上游吸收能力后补丁主动让位」的唯一表达。
|
|
211
269
|
|
|
@@ -215,7 +273,7 @@ dsh 内部结构已变,对应补丁已自动跳过(功能回退到官方实
|
|
|
215
273
|
# 前置:把全局 dsh 的嵌套依赖 junction 到仓库(仓库本身不含 node_modules)
|
|
216
274
|
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\link-deps.ps1
|
|
217
275
|
|
|
218
|
-
# 全套
|
|
276
|
+
# 全套 13 个套件(断言总数以运行器末行输出为准,不要手写)
|
|
219
277
|
npm test
|
|
220
278
|
```
|
|
221
279
|
|
|
@@ -223,6 +281,11 @@ npm test
|
|
|
223
281
|
`e2e_real_components` 用真实实现类(原型链驱动)验证补丁装上后的行为;其余套件
|
|
224
282
|
覆盖逻辑与边界。
|
|
225
283
|
|
|
284
|
+
**版本号只在发布时决定**:开发轮次的变更先记入 `CHANGELOG.md` 的 `[Unreleased]`,
|
|
285
|
+
不逐次改 `package.json`。选号规则与发布清单见
|
|
286
|
+
[`docs/VERSIONING.md`](docs/VERSIONING.md);`tests/verify_docs.mjs` 会断言
|
|
287
|
+
`package.json` 与 CHANGELOG 首条版本一致。
|
|
288
|
+
|
|
226
289
|
这三层都**不验证真实 dsh 进程里的调用链**。那需要起一个隔离实例并驱动 HTTP RPC,
|
|
227
290
|
工具在 [`tools/runtime-probe/`](tools/runtime-probe/README.md):
|
|
228
291
|
|
|
@@ -237,6 +300,7 @@ node tools/runtime-probe/run.mjs --home <隔离 home> --agents <agents home> \
|
|
|
237
300
|
|---|---|---|
|
|
238
301
|
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | 维护者 | 问题分类、各能力的探测与实现、装配契约、如何新增能力 |
|
|
239
302
|
| [docs/MEASUREMENTS.md](docs/MEASUREMENTS.md) | 维护者 | 时间/空间两维度的实测数据、测量边界、已移除能力的记录 |
|
|
303
|
+
| [docs/RENDER-ANALYSIS.md](docs/RENDER-ANALYSIS.md) | 维护者 | 浏览器渲染链路:静态机制、真实浏览器实测、瓶颈排序与上游建议 |
|
|
240
304
|
| [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md) | 维护者 | 版本矩阵、0.2.2 的五处破坏性变更、插件清单规范 |
|
|
241
305
|
| [docs/HARNESS-COMPARISON.md](docs/HARNESS-COMPARISON.md) | 参考 | 其他 coding agent harness 怎么处理长会话 |
|
|
242
306
|
| [docs/PUBLISHING.md](docs/PUBLISHING.md) | 发布者 | 发布流程与约定 |
|