Commit Graph

2 Commits

Author SHA1 Message Date
c7da8fde82 perf(webui): 星图力导向布局改网格近邻 + Web Worker,消除主线程 10.9s 阻塞
生产库 3754 块时,buildChatStarmapGraph 在内联脚本里同步跑 50 轮全对全
(O(n²))斥力:每轮 704 万次配对,50 轮共 3.52 亿次。CDP Profiler 实测
单次阻塞主线程 10.9 秒,页面启动首帧 11.8 秒;PerformanceObserver 在
40 秒窗口统计到累计 40,005ms 长任务。

两条独立问题叠加,本提交分别解决:

① O(n²) → 网格近邻(O(n·k))
   斥力体里本来就有 `if (d < 25)` —— 只对近邻施力,全对全是白算。
   改为 3×3×3 网格分桶,只遍历邻近格。实测 3754 节点 7016ms → ~120ms。

② 同线程 → Web Worker
   即使算量合理也不该占主线程。计算整体移入
   static/starmap-layout-worker.js,主线程零阻塞。

配套:迭代数按 n 自适应(小图跑满 50 轮,大图退到 14 轮)+ 迭代间早停
(阈值取平均间距的 0.2%,随规模自适应)。

★ 串行回退(file://、老浏览器、Worker 构造失败)必须用**同一算法**。
   若回退走旧全对全,两条路径会给出不同布局 —— 「有 Worker 和没 Worker
   看到的图不一样」极难排查。故两边各写一份逐字对应的实现。

★ 另修:renderChatStarmap 必须在容器不可见时提前返回(offsetParent)。
   starmapActiveContainer 会 fallback 到聊天面板,导致未打开星图也被迫
   建图,把聊天页签一起拖死。

判据 TestStarmapNoPerFrameFullScan 同时覆盖两处实现,经三向变异验证:
  ① 回退改回 Object.keys 全对全 → FAIL
  ② Worker 去掉 CUTOFF 网格法 → FAIL
  ③ 删除 Worker 文件 → FAIL
2026-10-13 10:12:57 +08:00
8336509bfa perf(webui): 修星图形变活跃集的泄漏 + 加回归判据
上一提交(3e201dd4)把 starmapTickMorph 从「每帧全量遍历」改成
starmapMorphSet 活跃集,但我漏了一处:buildChatStarmapGraph 重建图时
只重置了 starmapNodeMeshes / starmapEdgeLines,**没清空 starmapMorphSet**。

后果:旧 mesh 已从场景摘掉,却留在活跃集里,于是
- 集合随每次重建单调增长、永不归零 ⇒「空集时 O(1) 返回」的快速路径
  从此失效,正是这次优化想省掉的那笔开销又回来了
- 每帧遍历一批已经不在场景里的废弃 mesh

同时补 starmap_perf_test.go。为何用源码断言而非运行时测量:
dashboard.js 是嵌入的浏览器脚本(依赖 three.js / DOM),Node 跑不了;
而这几条都是**结构性**缺陷(循环扫全表、循环不停、每帧重建),
源码里能可靠判定,回归时立刻红。

判据可杀变异(逐个实测过,不是装饰):
- 节流常量改名 → FAIL「缺少节流」
- morph 早退条件删掉 → FAIL「必须提前返回」
- clear() 改成 if (false) 或整行删除 → FAIL「必须无条件清空」

第三处特意断言在**重建函数体内**且是 `(?m)^\s*if \(starmapMorphSet\) ...`
(先写成全仓搜 `starmapMorphSet.clear()`,变异测试显示 `if (false)` 能骗过
它 —— 弱判据当场修强)。
2026-10-08 12:40:43 +08:00