原生大文档性能验证(2026-10-17)
结论
已修复一处会造成大文档持续无响应和内存暴涨的源码行号实现,并降低阅读、导图和多视图的构建成本。优化后六份样本完成 198 项操作,未捕获异常;每项操作均校验源文未改变、未产生脏状态。当前仍未达到性能和完整功能验收条件。
后续已在自定义标题栏、最大化窗口下完成 756 项正文 / 导图演示及多视图往返检查,记录与限制见最大化与导图演示压力测试。下文保留原先 1280×800 基线,不与最大化数据直接计算改善比例。
Windows 源码视图后续采用按行布局,并完成 Skia 后端、源码编辑及 Release 关闭复验,见源码按行布局与渲染后端复验。下文关于全文 TextField 的限制描述对应历史基线。
环境与样本
- Windows x64 原生 Profile,Flutter 3.47.7 / Dart 3.13.5;窗口外框 1280×800,同一机器顺序运行。
- document-1:用户指定的技术方案,144,652 字节。
- document-2:大型混合技术文档,1,163,319 字节,320 章。
- document-3:综合语法与交互,29,254 字节。
- document-4:长代码与横向滚动,621,354 字节。
- document-5:公式与数学符号密集,83,022 字节,含 640 个块公式。
- document-6:边界与容错,45,320 字节。
样本从原位置只读加载,内容不写入仓库。测试使用内存仓储、独立 VM、禁用文件监听,避免改动实际文件和用户偏好。打开计时使用预加载文本,不包含磁盘读取。报告对路径和原文件名作匿名映射。
方法
每份样本循环八种布局,逐个正文视图执行拖动、85% / 15% / 末尾 / 开头跳转;再反向切换六份文档三轮,每次切换三视图、即时+导图、源码+阅读及即时编辑。核对正文对应当前文件,内容及脏状态不变。导图的拖动和缩放另有控件测试;本次批量滚动步骤针对有竖向滚动条的正文视图,未覆盖所有图形交互和代码横向滚动。
记录操作至界面稳定的墙钟耗时、引擎帧构建/栅格最大耗时、操作结束时进程 RSS。引擎计时批量上报,在计时区间外等候 120 ms 收集,写结果文件也在计时区间外。动作包含测试手势和 pumpAndSettle 的等待,不能将整体耗时视为纯计算耗时,也不能据此声称 60 FPS。
优化前后
首份技术方案,单轮观测(毫秒,取整):
| 操作 | 优化前 | 优化后 |
|---|---|---|
| 打开到即时编辑 | 81 | 70 |
| 首次切换源码 | 1585 | 1181 |
| 切换阅读 | 1028 | 226 |
| 切换导图 | 405 | 235 |
| 源码+阅读 | 2587 | 1180 |
| 三视图 | 2664 | 1195 |
优化前三视图打开 document-2 时,超过两分钟没有完成下一项操作,Windows 显示进程无响应,实测工作集约 5.96 GB,手动终止该测试进程。performance-before.json 只有已完成的 18 项,不把缺失项标记为成功或以 0 ms 填充;其中 724 MiB 是已完成操作的采样最大值,不是卡住时的峰值。
优化后 document-2 在三视图中打开约 1011 ms;切换阅读 227 ms、导图 217 ms、三视图 921 ms。六份样本完整运行约 3 分 22 秒,完成 198 项,采样最大 RSS 1095 MiB。这仍是较高内存占用,不能认定内存优化已完成。
实现变更
- 源码行号用缓存的行起点数量,仅绘制可见行;去除循环中反复拆分全文及每行一个文本控件的实现。源码滚动位置在布局切换后保留。
- 阅读和即时编辑使用可变高度的虚拟列表。阅读通过索引定位离屏章节,不再依赖整篇正文所有 GlobalKey 同时存在。
- 生成导图时一次按章节分组内容和子章节,避免每个章节重复遍历全部块及标题。
- 导图按当前视口构建节点及绘制相交连线。两千节点回归测试验证离屏节点未构建、平移后出现且可以选择。
- 引入的
super_sliver_list已同步到实际依赖和原型开源许可声明。
未通过或尚未覆盖的项目
- 源码编辑仍使用全文 TextField,技术方案首次构建帧约 1.11 秒;大型混合文档约 0.76 秒,滚动最慢构建帧约 75 ms,仍会出现卡顿。后续需要支持可见区域布局的源码编辑方案。
- 198 项是切换、滚动和内容完整性验证,不是 Markdown 全语法验收。公式、复杂图表、代码高亮、行内语法编辑、跨块选择仍需独立实现或验证。
- 未对 Android、iOS、macOS、Linux、鸿蒙进行这组原生性能实测;预览 APK 构建成功也不能替代真机验证。
- 当前数据为一台机器的单轮基线及三轮切换,不是可泛化的延迟承诺。没有以大面积空白背景相似度或测试通过数计算 98% 还原率。
复测入口
在应用目录使用 flutter drive --profile -d windows --driver test_driver/integration_test.dart --target integration_test/document_stress_test.dart --no-pub。环境变量 VEX_STRESS_INPUTS 为输入绝对路径 JSON 数组,VEX_STRESS_OUTPUT 为本地 JSON 输出位置,VEX_STRESS_DEEP_SCROLL=1 启用深处往返滚动。
有疑问或建议,欢迎到 GitHub 提交 Issue 参与讨论。