首页
/ Flutter 引擎 AOT 快照体积对比方法:用 gen_snapshot 指令尺寸报告做字节级归因

Flutter 引擎 AOT 快照体积对比方法:用 gen_snapshot 指令尺寸报告做字节级归因

2026-09-06 15:59:05作者:毕习沙Eudora

本文介绍如何在 Flutter 引擎(Flutter Engine)中准备一份“AOT 快照体积差异”的表格化汇总:通过 flutter build aot--verbose 输出捕获 gen_snapshot 调用,借助 --print-instructions-sizes-to 选项把每个方法编译后的指令字节数导出为 JSON,再用对比工具对“变更前/变更后”两份 JSON 做差分。读完后,你将掌握一套可复制的流程,用于量化某次 Dart 代码改动、或某次引擎(gen_snapshot 二进制)改动,究竟为 AOT 产物增加或减少了多少字节,并定位到具体库与具体方法。

适用前提与前置条件

这套方法的目标是“比较两个 AOT 快照(AOT snapshot)的大小差异”,适用场景包括:

  • 评估某次 Dart 代码变更(例如新增 API、调整库实现)对 AOT 产物体积的影响;
  • 评估引擎侧改动——重新构建 gen_snapshot 二进制——后对产物体积的影响。

使用该方法前,需要满足两个前提(来自 Comparing-AOT-Snapshot-Sizes.md 的原文说明):

  1. 宿主机上已完成 Flutter 引擎环境的搭建(见 Setting-up-the-Engine-development-environment.md),并在下文所有命令中用环境变量 FLUTTER_ENGINE 指向该引擎源码目录。
  2. 如果要配合本地构建的引擎做测量,flutter build 还支持 --local-engine--local-engine-host 两个标志(说明见 Debugging-the-engine.md 的“Running a Flutter app with a local engine”一节)。

流程一:捕获 flutter build aot 中的 gen_snapshot 调用

第一步是为应用构建 AOT 快照,即执行 flutter build aot,并传入 --verbose 标志。目的不是完成构建本身,而是要在冗长输出中找到工具实际发起的 gen_snapshot 调用,以便稍后加上额外选项重跑它。

需要重跑的核心选项是 --print-instructions-sizes-to:它让 gen_snapshot 把“各方法指令尺寸”的汇总写入一个 JSON 文件。在本文流程中,该 JSON 文件的路径用 $SUMMARY_LOCATION 表示。

文档给出的调用示例如下(注意保留原有参数、只增加 --print-instructions-sizes-to 一项):

$FLUTTER_ENGINE/out/host_debug_unopt/gen_snapshot                    \
  --print-instructions-sizes-to=$SUMMARY_LOCATION                    \
  --causal_async_stacks                                              \
  --packages=.packages                                               \
  --deterministic                                                    \
  --snapshot_kind=app-aot-blobs                                      \
  --vm_snapshot_data=build/aot/vm_snapshot_data                      \
  --isolate_snapshot_data=build/aot/isolate_snapshot_data            \
  --vm_snapshot_instructions=build/aot/vm_snapshot_instr             \
  --isolate_snapshot_instructions=build/aot/isolate_snapshot_instr   \
  build/aot/app.dill

几点解读(结合仓库内其他引擎文档):

  • 示例以 out/host_debug_unopt 构建目录下的 gen_snapshot 为例,实际应使用你本地引擎构建出的那个二进制;
  • build/aot/app.dill 是 AOT 编译链路中的 kernel 输入,产物是四个快照文件(VM/Isolate 的 data 与 instructions 各两份)。Custom-Flutter-Engine-Embedding-in-AOT-Mode.md 也说明了 gen_snapshot 一次成功调用会生成这四个二进制 blob;
  • --deterministic 保证快照内容可复现,这对“前后两次测量可比”很重要;
  • 文档同时提醒:AOT 编译在调用 gen_snapshot 之前还有一步 kernel 编译(见 Custom-Flutter-Engine-Embedding-in-AOT-Mode.md 的“Generating the kernel snapshot”一节)。这意味着改了 Dart 代码后,必须重新走完整的 flutter build aot 来刷新 kernel,不能只重跑 gen_snapshot

执行完示例命令后,把 $SUMMARY_LOCATION 指向的 JSON 文件保存为 before.json,作为基线。

流程二:制造差异并重新测量

基线确定后,制造你关心的差异,二选一(文档原文如此描述):

  • 修改 Dart 代码,引入你想观察效果的变更;或
  • 重新构建引擎,得到一个更新后的 gen_snapshot 二进制。

然后执行两轮重新构建:

  1. 重新运行 flutter build aot。文档特别强调这一步的重要性:因为 AOT 编译在 gen_snapshot 之前存在 kernel 编译步骤,只有重跑完整构建,app.dill 才会反映你的代码改动;
  2. 重新运行 gen_snapshot 调用(与流程一相同的命令,包括 --print-instructions-sizes-to),并把这次生成的 JSON 保存为 after.json

至此,你手上有了两份结构相同的尺寸汇总文件:before.json(变更前)与 after.json(变更后)。

流程三:运行 compare_size.dart 生成差异表格

对比工具 compare_size.dart 接受两个 JSON 文件,输出方法级的字节差值汇总表。文档给出的调用形式为:

$FLUTTER_ENGINE/out/host_debug_unopt/dart-sdk/bin/dart \
  before.json \
  after.json

即使用引擎构建目录中随附的 Dart SDK 来运行该脚本。需要如实说明:在当前仓库内检索不到 compare_size.dart 文件本身,文档将其作为工具引用(它随 Dart SDK/引擎工具链分发,并非本仓库中的源码),因此在实际执行前需要在你的工具链环境中确认该工具的位置与名称。

如何读懂输出:方法级 Diff 与 Stub 行

文档给出的示例输出形如(节选):

+------------+----------------------------------------------------------+--------------+
| Library    | Method                                                   | Diff (Bytes) |
+------------+----------------------------------------------------------+--------------+
| dart:async | new ZoneSpecification.from                               |        +2136 |
| dart:async | runZoned                                                 |        +1488 |
| dart:async | new _CustomZone                                          |         +927 |
...
|            | [Stub] Type Test Type: class 'PopupMenuEntry'            |         -128 |
|            | [Stub] Type Test Type: class '_SyncIterator@0150898'     |         -128 |
| dart:io    | new Directory                                            |         -211 |
| dart:io    | new Link                                                 |         -211 |
+------------+----------------------------------------------------------+--------------+
Total change +24036 bytes.

表格的三列含义:

含义
Library 方法所属库(如 dart:asyncdart:io),Stub 行该列为空
Method 具体方法名;[Stub] 前缀表示编译器生成的桩代码(示例中为 Type Test 类型检查桩),@0150898 之类的后缀是实例可区分的类型哈希
Diff (Bytes) 该方法指令字节数相对基线的差值,正数为增大、负数为缩小

表尾的 Total change 给出整体 AOT 指令体积的净变化(示例为 +24036 字节)。读表时的两个要点:

  • 正负行可能同时出现(示例中 dart:async 系列大量增长,而部分 Type Test 桩与 dart:io 构造函数在缩小),需要同时看净变化与结构性变化,才能判断体积增长的来源是“方法本身膨胀”还是“生成的桩/类型检查结构变化”;
  • 差值是“方法级指令字节”层面的归因,与最终 .so/ELF 文件在磁盘上的大小不完全等同(还受链接、打包等影响),适合做改动间相对比较,而非绝对体积承诺。

gen_snapshot 参数与 AOT 产物背景

从示例调用中还能看到一组反复出现的关键参数,值得单独说明:

  • --snapshot_kind=app-aot-blobs:生成 AOT 应用快照的四个 blob;--vm_snapshot_data / --isolate_snapshot_data / --vm_snapshot_instructions / --isolate_snapshot_instructions 分别指定四个输出路径;
  • --causal_async_stacks:开启因果异步栈,属于影响产物内容/体积的编译选项之一——如果你在对比“开关某编译选项”的体积影响,这类开关项就是变量之一;
  • --deterministic:确定性输出,保证两次测量之间差异只来自你刻意引入的变更。

此外,仓库中 Fuchsia 组件的 AOT 构建配置 flutter_dart_component.gni 展示了同一选项在正式构建系统里的用法:其中 gen_snapshot 被以 --deterministic--snapshot_kind=app-aot-elf--print-instructions-sizes-to=<stats json> 的方式调用,即引擎构建流程本身也会把指令尺寸汇总落盘为 JSON。可以推断,本文档所描述的对比流程与构建系统内置的统计能力出自同一机制:JSON 汇总由 --print-instructions-sizes-to 产生,差异分析在其之上进行。

关于产物的通用背景,可参阅:

  • Custom-Flutter-Engine-Embedding-in-AOT-Mode.mdgen_snapshot 的构建方式、host/target 架构匹配(例如用 --version 校验二进制对应的宿主/目标平台)、kernel 快照的生成;
  • Flutter-engine-operation-in-AOT-Mode.md:四个 AOT 产物(VM/Isolate 的 data 与 instructions 快照)的用途,以及“同一次调用生成的四个文件不可混用不同 gen_snapshot 变体的产物”等约束。

完整操作流程速查

把三条流程串起来,完整的最小操作序列是:

  1. 搭建引擎环境,FLUTTER_ENGINE 指向引擎源码目录(参考 Setting-up-the-Engine-development-environment.md);
  2. flutter build aot --verbose,从输出中抄录 gen_snapshot 调用;
  3. 加上 --print-instructions-sizes-to=$SUMMARY_LOCATION 重跑该调用,JSON 存为 before.json
  4. 修改 Dart 代码或重建引擎的 gen_snapshot
  5. 重新执行完整 flutter build aot(刷新 kernel);
  6. 再次重跑 gen_snapshot,JSON 存为 after.json
  7. dart compare_size.dart before.json after.json 生成方法级 Diff 表格与 Total change

该流程的价值在于:把“体积变大了”这种笼统结论,落实到“哪个库、哪个方法、变化多少字节”的粒度,为 Dart SDK/框架代码瘦身与引擎编译器调优提供可核查的依据。适用限制同样明确:它依赖本地构建的引擎与 --verbose 构建日志,且 JSON 汇总由 gen_snapshot 在特定编译选项下生成——对比前后应保持除目标变更外其余编译参数(如 --causal_async_stacks)一致,否则差分中会混入无关变量。

登录后查看全文
热门项目推荐
相关项目推荐