# Atlas Mind Map 性能测试与技术评审报告

> 包：`@lingkkkk/atlas-mindmap@0.1.0`<br>
> 原始数据生成时间：2026-08-29T12:24:30.479Z<br>
> Schema：v2 · Commit：`733e4467c800643b6551f4adfc7d1129559d08ca`

## 执行摘要

本次在真实有界面 Chromium 中完成 9 个规模的生产构建测试，成功测得的最大规模为 **100,000 个节点**。全部请求规模均成功完成，没有浏览器崩溃、fixture 数量错误或控制台错误。

最大规模 100,000 节点：启动同步阶段判定 **阻断**；启动 GC 后 JS heap 29.72 MB；全图 Fit 47.6 FPS / 帧 P95 33.21 ms，判定 **可用**；100% 聚焦编辑 116.7 FPS / 帧 P95 10.51 ms；样式同步 mutation P95 0.47 ms、mutation 至渲染帧 P95 9.03 ms，文本更新 P95 46.97 ms，拖拽改父级成功。聚焦交互判定：**流畅（未见明显卡顿）**；跨启动/总览/交互的总体判定：**阻断**。

启动阻断规模：50,000、100,000；聚焦交互“降级”规模：无；聚焦交互“阻断”规模：无；总体阻断规模：50,000、100,000。启动阻断不等于进入画布后的拖拽卡顿，两者必须分开评审。结论只适用于下述机器、浏览器、视口与数据形状，不外推为所有终端的通用 SLA。

## 测试环境

| 项目 | 值 |
| --- | --- |
| 操作系统 | darwin 23.6.0 (arm64) |
| CPU | Apple M1 Max · 10 logical cores |
| 物理内存 | 32.0 GB |
| Node / pnpm | v22.12.0 / 10.33.0 |
| Chromium | 151.0.7922.34 · headed |
| 视口 / DPR | 1440 × 900 / 1 |
| 采样 | 每视图 120 帧；每操作 7 次 |
| 空白页 JS heap 基线 | 0.57 MB |
| 被测运行时 | packages/atlas-mindmap/dist/index.js bundled into the production playground |
| ESM | 71.72 KiB raw / 15.79 KiB gzip |
| IIFE | 76.00 KiB raw / 16.06 KiB gzip |
| CSS | 5.66 KiB raw / 1.71 KiB gzip |

## 方法与口径

- 数据集：Deterministic normalized XMind-semantic breadth-first 4-ary tree with topic kind, markers, labels, and task data; maximum depth 9; exact requested count. Measures the post-import renderer model, not .xmind ZIP/content.json parsing or attachment decode.
- 内存：Chromium Runtime.getHeapUsage after HeapProfiler.collectGarbage; JS heap only.
- 全图：Fit complete layout, warm 20 frames, then collect requestAnimationFrame and Canvas metrics.
- 聚焦：Center a deepest representative topic at 100% scale, warm 20 frames, then collect metrics.
- 公共 facade 先构建为 `dist/index.js`，production playground 显式 alias 到该产物后再测试；不是直接从私有 workspace runtime 源码采样。
- 节点数据是与 XMind topic 语义等价的规范化后模型（含 kind、markers、labels、task），因此适用于评估导入完成后的布局和 Canvas 渲染；首次渲染时间不包含真实 `.xmind` ZIP 解压、`content.json` 解析、图片或附件解码。
- 每个规模使用新的页面；启动内存采样前显式执行 CDP GC；先预热 20 帧再采样。
- 浏览器真实触发 pointer pan、wheel zoom 和 pointer drag-reparent，并校验 camera、scale、parentId 和总节点数。
- 分级阈值：Smooth >=55 FPS/P95 <=20 ms/edit <=50 ms; usable >=30 FPS/P95 <=50 ms/edit <=100 ms; blocking on a >250 ms synchronous startup/edit operation or failed integrity.
- FPS 受显示器刷新率上限影响；本机是高刷新率环境，因此同时以帧 P95 和同步操作 P95 作为主要判断依据。

## 核心容量指标：首次渲染、堆内存与 DOM

| 节点数 | 首次渲染时间 ms | 强制 GC 后 JS 堆内存 MB | 页面 DOM 元素数 | Topic DOM 元素数 |
| ---: | ---: | ---: | ---: | ---: |
| 100 | 84.10 | 1.49 | 110 | 0 |
| 500 | 66.80 | 1.69 | 110 | 0 |
| 1,000 | 65.90 | 1.85 | 110 | 0 |
| 2,000 | 75.50 | 2.17 | 110 | 0 |
| 5,000 | 104.90 | 3.20 | 110 | 0 |
| 10,000 | 153.70 | 5.23 | 110 | 0 |
| 20,000 | 242.00 | 7.56 | 110 | 0 |
| 50,000 | 565.50 | 15.87 | 110 | 0 |
| 100,000 | 971.40 | 29.72 | 110 | 0 |

- **首次渲染时间**使用页面导航起点的同一 Performance Timeline，并在 Canvas renderer 首次绘制完成时记录 Performance API 时间戳；包含脚本加载、页面启动、fixture 构建、文档规范化、首次布局和首次 Canvas 绘制，不包含浏览器 compositor/present 到显示器的硬件延迟。
- **堆内存**是每个规模使用全新页面，并由 CDP 强制 GC 后读取的 V8 JS heap；不包含 Chromium 进程、GPU surface、字体缓存及操作系统原生内存。
- **DOM 元素数**在 renderer 首次绘制完成后、任何测试交互前，通过 document.querySelectorAll('*') 统计页面中的 Element；其中 Topic DOM 元素数始终为 0。DOM 数量不是思维导图节点数量：节点存放在紧凑模型和 typed arrays 中，由单个 Canvas 按视口绘制，因此 DOM 不随 100–100,000 个节点线性增长。原始 JSON 中另保留交互结束后的 CDP DOM node 总数，二者不混用。

## 启动、布局与内存

| 节点数 | 状态 | Fixture ms | 初始布局 ms | 导航至首帧 ms | JS heap MB | 相对空白页 MB | 启动判定 | DOM 口径 |
| ---: | --- | ---: | ---: | ---: | ---: | ---: | --- | --- |
| 100 | 成功 | 10.90 | 0.80 | 84.10 | 1.49 | 0.92 | 流畅 | Elements 110 / topic 0 |
| 500 | 成功 | 11.30 | 0.60 | 66.80 | 1.69 | 1.12 | 流畅 | Elements 110 / topic 0 |
| 1,000 | 成功 | 15.50 | 1.70 | 65.90 | 1.85 | 1.28 | 流畅 | Elements 110 / topic 0 |
| 2,000 | 成功 | 23.80 | 2.30 | 75.50 | 2.17 | 1.60 | 流畅 | Elements 110 / topic 0 |
| 5,000 | 成功 | 48.50 | 6.20 | 104.90 | 3.20 | 2.63 | 流畅 | Elements 110 / topic 0 |
| 10,000 | 成功 | 87.20 | 8.10 | 153.70 | 5.23 | 4.67 | 流畅 | Elements 110 / topic 0 |
| 20,000 | 成功 | 167.60 | 13.20 | 242.00 | 7.56 | 6.99 | 流畅 | Elements 110 / topic 0 |
| 50,000 | 成功 | 458.60 | 25.10 | 565.50 | 15.87 | 15.30 | 阻断 | Elements 110 / topic 0 |
| 100,000 | 成功 | 813.80 | 46.30 | 971.40 | 29.72 | 29.15 | 阻断 | Elements 110 / topic 0 |

JS heap 是 V8 对页面 JavaScript 对象的统计，不包含 GPU Canvas surface、Chromium 进程本身、字体缓存与操作系统分配，因此不能等同于应用总 RSS。`topic DOM = 0` 表明节点没有一节点一 DOM 的线性膨胀。

## 全图 Fit 渲染

| 节点数 | 缩放 | FPS | 帧 P95 ms | Cull P95 ms | Draw P95 ms | 中位绘制节点/率 | 判定 |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| 100 | 18.2540% | 120.0 | 9.30 | 0.10 | 0.70 | 100 (100.000%) | 流畅 |
| 500 | 3.6378% | 120.0 | 9.30 | 0.10 | 0.40 | 500 (100.000%) | 流畅 |
| 1,000 | 1.8181% | 119.4 | 9.30 | 0.10 | 0.60 | 1,000 (100.000%) | 流畅 |
| 2,000 | 0.9088% | 120.0 | 9.30 | 0.10 | 0.90 | 2,000 (100.000%) | 流畅 |
| 5,000 | 0.3635% | 119.9 | 9.00 | 0.10 | 2.00 | 5,000 (100.000%) | 流畅 |
| 10,000 | 0.1817% | 120.0 | 8.90 | 0.10 | 1.41 | 10,000 (100.000%) | 流畅 |
| 20,000 | 0.0909% | 119.9 | 8.70 | 0.10 | 2.80 | 20,000 (100.000%) | 流畅 |
| 50,000 | 0.0363% | 90.9 | 16.91 | 0.30 | 7.20 | 50,000 (100.000%) | 流畅 |
| 100,000 | 0.0182% | 47.6 | 33.21 | 0.50 | 25.30 | 100,000 (100.000%) | 可用 |

全图视图会把完整布局压缩进视口，LOD 会关闭文字和复杂连线；它代表缩略总览，而不是可读编辑比例。中位绘制节点率用于核对空间索引与 overscan 的实际候选量。

## 聚焦编辑与结构修改

| 节点数 | 聚焦 FPS / 帧P95 ms | 中位绘制节点 | 样式同步/至渲染帧 P95 ms | 文本P95 ms | 新增P95 ms | 移动P95 ms | 实际拖拽 | 综合 | 卡顿判断 |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- | --- |
| 100 | 119.9 / 9.30 | 14 | 0.47 / 9.30 | 1.04 | 0.47 | 0.95 | 成功 / 7.56 ms | 流畅 | 未见明显卡顿 |
| 500 | 119.4 / 9.40 | 14 | 0.51 / 8.87 | 2.41 | 0.81 | 0.74 | 成功 / 5.11 ms | 流畅 | 未见明显卡顿 |
| 1,000 | 114.3 / 11.43 | 14 | 0.50 / 9.50 | 1.48 | 1.12 | 0.95 | 成功 / 3.93 ms | 流畅 | 未见明显卡顿 |
| 2,000 | 116.5 / 9.71 | 14 | 0.40 / 8.87 | 2.51 | 3.06 | 1.38 | 成功 / 5.58 ms | 流畅 | 未见明显卡顿 |
| 5,000 | 120.0 / 9.30 | 14 | 0.44 / 9.50 | 7.44 | 4.82 | 3.74 | 成功 / 10.98 ms | 流畅 | 未见明显卡顿 |
| 10,000 | 117.4 / 9.92 | 14 | 0.40 / 12.65 | 7.99 | 8.72 | 4.76 | 成功 / 6.88 ms | 流畅 | 未见明显卡顿 |
| 20,000 | 114.6 / 12.70 | 14 | 0.67 / 9.19 | 16.15 | 10.07 | 9.41 | 成功 / 14.61 ms | 流畅 | 未见明显卡顿 |
| 50,000 | 118.0 / 9.31 | 14 | 0.47 / 11.21 | 26.80 | 22.24 | 27.86 | 成功 / 32.99 ms | 流畅 | 未见明显卡顿 |
| 100,000 | 116.7 / 10.51 | 14 | 0.47 / 9.03 | 46.97 | 44.66 | 43.58 | 成功 / 41.34 ms | 流畅 | 未见明显卡顿 |

- 样式同步值只统计 `updateTopic()` mutation 返回；“至渲染帧”另统计 mutation 到下一次 Canvas renderer rAF 完成，避免把 0.x ms 误写成可见结果延迟。两者都不替换全量 layout。
- 文本、新增、移动会同步重建布局与空间索引，因此规模增大时它们比换色更容易成为瓶颈。
- 实际拖拽列中的毫秒值只统计 pointer-up 到 parentId 可观察变更完成的时间；拖动手势本身包含人为注入的多步移动，不把手势持续时间当作程序延迟。

## 真实指针操作完整性

| 节点数 | 平移 | 平移帧P95 ms | 缩放 | 缩放帧P95 ms | 拖拽改父级 | 拖拽帧P95 ms |
| ---: | --- | ---: | --- | ---: | --- | ---: |
| 100 | 成功 | 9.06 | 成功 | 9.30 | 成功 | 9.30 |
| 500 | 成功 | 9.20 | 成功 | 9.30 | 成功 | 9.30 |
| 1,000 | 成功 | 9.20 | 成功 | 9.30 | 成功 | 10.36 |
| 2,000 | 成功 | 9.12 | 成功 | 11.54 | 成功 | 9.38 |
| 5,000 | 成功 | 8.94 | 成功 | 9.30 | 成功 | 9.30 |
| 10,000 | 成功 | 8.66 | 成功 | 9.30 | 成功 | 9.20 |
| 20,000 | 成功 | 8.80 | 成功 | 9.30 | 成功 | 11.76 |
| 50,000 | 成功 | 9.26 | 成功 | 9.30 | 成功 | 15.78 |
| 100,000 | 成功 | 9.10 | 成功 | 9.56 | 成功 | 13.48 |

每项“成功”都表示状态实际变化且最终模型节点数保持不变；不是只检查事件是否发出。

## 样式与图标性能评审

节点 fill、border、color、shape、fontWeight 等样式在 Canvas 中只影响可见候选的绘制状态。当前实现已增加视觉更新快路径，颜色/边框/圆角/标记元数据修改不会重建 100,000 节点布局；成本近似 **O(当前视口绘制节点数)**，通常不会成为主要性能问题。`fontSize` 当前也按视觉属性处理并受已有节点框裁剪；如果未来要求字号改变节点几何，应提供显式批量 relayout，而不是每次输入都全量重排。

图标策略决定性能边界：推荐缓存 Canvas Path2D，或把图标一次 decode 后放入有容量上限的 bitmap atlas；按 `icon + size + DPR` 复用；低缩放 LOD 隐藏图标；帧循环内不创建 Image/SVG/Promise。禁止为每个节点挂 DOM/SVG，也不要让 100,000 个远程图片同时解码。v0.1.0 保存 markers/image 等 XMind 数据字段，但**不宣称已完成任意远程图片的高性能图标渲染**；这部分应以受控插件和缓存预算实现。

## 技术结论与上线建议

1. 20,000 节点是主要验收档：应同时满足聚焦编辑、真实拖拽与结构变更阈值；回归测试优先看 frame P95、text/move P95 与 heap，而不是只看平均 FPS。
2. 50,000–100,000 节点应保留 Canvas + typed-array layout + spatial index + LOD；默认打开聚焦分支或折叠层级，避免把可读编辑和全图总览混为一谈。
3. 50,000+ 的 fixture/normalization 已触发 >250 ms 启动阻断线；公司产品上线前应把数据准备移到 Worker、采用分批导入/渐进可见，或先加载折叠骨架，不能用“进入画布后拖拽流畅”掩盖首屏主线程阻塞。
4. 样式面板应合并连续输入：颜色预览可逐帧重绘，几何属性采用 debounce/提交时重排；主题切换只重绘。
5. 图标和用户图片必须设置缓存总量、像素预算和淘汰策略。业务侧导入 XMind 时先只保存元数据，按视口懒加载资源。
6. CI 建议用固定浏览器/机器保存基线；同环境下 JS heap 增长 >20%、聚焦帧 P95 >20 ms、结构编辑 P95 >100 ms、启动同步阶段 >250 ms 或指针完整性失败时阻断发布。

## 限制与适用边界

- 主表是最大深度 9 的确定性 4 叉树；现有 demo 另有 20,001 节点、深度 200 的目录森林用于栈安全和深层导航，但两种拓扑不混入同一增长曲线。
- 单机单次资格测试会受温度、后台进程、显示刷新率和 Chromium 版本影响；正式容量承诺应在目标公司设备上重复至少 3 次并报告中位数/置信区间。
- JS heap 不含 GPU/进程 RSS；Canvas 绘制时间不等同于完整 compositor/present 时间。
- 本测试使用短文本和确定性 markers/task 分布。超长文本测量、公式、大图、实时协作与插件代码需要单独 workload。
- 本测试没有把合成树表述为真实文件导入测试；如需评审“打开 .xmind 文件到首帧”的端到端耗时，应在导入器完成后另建真实文件 corpus，单独统计解压、解析、资源解码和规范化阶段。
- 报告没有为失败或浏览器不支持的指标填估算数；原始 JSON 是唯一数据源，CSV/Markdown 均由它确定性生成。

## 复现

```bash
pnpm install --frozen-lockfile
pnpm benchmark:browser --counts=100,500,1000,2000,5000,10000,20000,50000,100000 --output=docs/performance/results/2026-08-29-atlas-mindmap.json
pnpm benchmark:report --input=docs/performance/results/2026-08-29-atlas-mindmap.json --csv=docs/performance/results/2026-08-29-atlas-mindmap.csv --markdown=docs/performance/2026-08-29-atlas-mindmap-performance-report.md
```
