发布时间:2026/9/7 19:45:47
Flutter 渲染速度测试实战:macrobenchmarks + e2e + devicelab 的完整性能基准链路 Flutter 渲染速度测试实战macrobenchmarks e2e devicelab 的完整性能基准链路【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter本篇基于 Flutter 仓库中的 How-to-write-a-render-speed-test-for-Flutter 文档展开面向希望暴露或修复 Flutter SDK框架层或引擎层性能问题的贡献者。全文按照“在 macrobenchmarks 中新增测试页 → 编写 e2e 自动化性能测试 → 接入 devicelab 为每个 commit 自动采集指标 → 设置基准线”的完整链路组织并结合 dev/benchmarks/macrobenchmarks 与 dev/devicelab 的当前源码解释每个步骤背后真正的机制测试是如何被驱动的、指标从哪里来、CI 如何调度。读完你可以独立地为任意新场景编写一条渲染速度基准测试并让它进入 Flutter 的性能监控体系。需要说明适用边界该流程在移动端Android/iOS上验证最充分Flutter Web 与桌面端的性能测试文档仍在演进中仓库中 Web 基准走的是另一条 lib/web_benchmarks.dart 路线见 macrobenchmarks/README.md。另外原文档中提到的dev/devicelab/manifest.yaml在当前仓库已不存在任务注册信息迁移到了仓库根目录的 .ci.yaml本文第 4 节会给出对应的实操方式。1. 整体链路为什么用 macrobenchmarks e2e devicelab 三层组合原文档给出的核心结论是写一个渲染速度测试推荐组合三个组件——e2e或 Flutter driver在真机上驱动被测页面并采集性能指标dev/benchmarks/macrobenchmarks 应用集中承载所有性能测试场景页面的“被测应用”dev/devicelab为 Flutter 的每一个 commit 自动运行测试把指标上报到 Flutter 的性能看板flutter/cocoon 体系从而快速发现回归或提速。“macro”一词值得注意它表示基准测试的对象是整个 Flutter 框架 引擎的大系统而不是某个微小的 Dart 或 C 函数后者属于 microbenchmark仓库中对应 dev/benchmarks/microbenchmarks。macrobenchmarks 的设计哲学是**“只加页面不加应用”**它本身就是一个包含大量页面、每个页面对应一个特定性能场景的 Flutter 应用并提供了样板代码和自动生成文件。当需要测试一个新场景时贡献者只需往仓库里添加一个页面和少量文件而不必新建一个带几十个自动生成文件的 Flutter 应用。从源码结构看这种“单应用多路由”的组织方式体现在三处约定上每个场景页一个独立文件放在 dev/benchmarks/macrobenchmarks/lib/src如 simple_scroll.dart、picture_cache.dart、backdrop_filter.dart所有路由名以k*RouteName常量集中在 lib/common.dart例如kCubicBezierRouteName /cubic_bezier首页 lib/main.dart 中的HomePage用一列按钮把这些路由串起来——这些按钮既是给手动测试者的入口也是给 Flutter driver / e2e 的“可点击锚点”靠Key(routeName)定位。以下按原文档假设我们要为某个super_important_case场景编写渲染速度测试。2. 第一步在 macrobenchmarks 中添加一个测试页原文档给出的四个步骤是在 macrobenchmarks/lib/src 下创建super_important_case.dart定义SuperImportantCasePage extends StatelessWidget。如果存在一个只有单个main.dart就能复现性能问题的最小 Flutter 应用通常可以直接把那个main.dart的内容复制进来作为页面内容在 macrobenchmarks/lib/common.dart 中添加路由常量const String kSuperImportantCaseRouteName /super_important_case;打开 macrobenchmarks/lib/main.dart在MacrobenchmarksApp的routes表中注册该路由在HomePage的ListView中添加一个按钮供手动测试者和 Flutter driver 点击跳转到新页面。对照当前仓库源码第 3、4 步的真实形态值得展开。MacrobenchmarksApp在 lib/main.dart 中定义MaterialApp的routes表把每个k*RouteName映射到对应页面例如routes: String, WidgetBuilder{ /: (BuildContext context) const HomePage(), kCullOpacityRouteName: (BuildContext context) const CullOpacityPage(), kCubicBezierRouteName: (BuildContext context) const CubicBezierPage(), // ... 约 50 个场景路由 kTextShadowPerfRouteName: (BuildContext context) const TextShadowPerfPage(), },HomePagelib/main.dart的body是一个ListView(key: const Key(kScrollableName), ...)kScrollableName定义在 lib/common.dart 中值为/macrobenchmark_listview。列表里每个场景对应一个按钮当前代码中的形态是ElevatedButton( key: const Key(kCubicBezierRouteName), child: const Text(Cubic Bezier), onPressed: () { Navigator.pushNamed(context, kCubicBezierRouteName); }, ),一个重要的实操细节原文档示例中使用的是RaisedButton而当前仓库代码已全部迁移到ElevatedButtonRaisedButton已废弃。新增页面时应跟随现有代码使用ElevatedButton但“key: Key(路由名)Navigator.pushNamed”的写法保持不变——后面第 3 节会看到测试框架正是靠这个 Key 找到按钮并点击跳转的。3. 第二步编写 e2e 性能测试3.1 宿主脚本与测试模板当页面完成并手动验证后就可以编写自动化集成测试来采集性能指标。原文档要求宿主侧脚本固定使用 macrobenchmarks/test_driver/e2e_test.dart——所有 e2e 性能测试都依赖它若要修改必须先与其他 Flutter 成员讨论在 macrobenchmarks/test 目录下新增super_important_case_e2e.dart调用macroPerfTestE2E函数它会导航到目标页面并开始采集指标。原文档给出的模板其中pageDelay、duration、timeout、body、setup五个参数均为可选import package:flutter/gestures.dart; import package:flutter/widgets.dart; import package:flutter/foundation.dart; import package:flutter_test/flutter_test.dart; import package:macrobenchmarks/common.dart; import util.dart; void main() { macroPerfTestE2E( super_important_case, kSuperImportantCaseRouteName, /* optional */ pageDelay: const Duration(seconds: 1), /* optional */ duration: const Duration(seconds: 3), /* optional */ timeout: const Duration(seconds: 30), /* optional */ body: (WidgetController controller) async { ... }, /* optional */ setup: (WidgetController controller) async { ... }, ); }各参数的含义继承自原文档参数作用pageDelay页面加载等待时间默认不等待duration性能指标的采样时长timeout测试包的兜底超时原文档指向testWidgets的 timeout 概念body基准测试期间驱动页面的自定义操作如滚动列表与duration同时使用时测试执行到两者中更长者结束setup基准测试开始前的初始化操作3.2 源码佐证macroPerfTestE2E实际做了什么当前实现位于 test/util.dart。它委托给macroPerfTestMultiPageE2E核心逻辑比模板代码透露的更值得理解真实帧模式binding.framePolicy LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive;——让集成测试绑定以“基准测试用真实渲染”策略运行而不是测试常用的伪帧pump 模拟帧这才能保证指标反映真实设备上的渲染行为250 毫秒初始延迟注释写明“避免在设备高负载期开始计时否则基准测试噪声更大”对应 flutter/flutter#19434定位与跳转通过find.byKey(ValueKey(kScrollableName))找到首页ListView内的Scrollable再用scrollUntilVisibletap点击Key(routeName)对应的按钮进入目标页——这解释了第 2 节中“按钮 Key 必须等于路由名”的约定指标采集窗口真正采样的段落是await binding.watchPerformance(() async { ... })窗口内并行执行body(tester)与duration延时即以“两者取长者”作为采样区间与原文档对bodyduration语义的描述一致。顺带说明参数差异当前macroPerfTestE2E签名test/util.dart暴露pageDelay、duration默认 3 秒、body、setuptimeout由testWidgets的Timeout.none接管驱动/测试框架层面有独立超时控制原文档中的timeout:参数名可以视为该机制在文档撰写时期的表述。若你的仓库版本签名不同以本地util.dart实际签名为准。3.3 真实用例无 body 与带 body 的两种写法最简写法见 test/cubic_bezier_perf_e2e.dart——仅一行调用页面自身就是持续动画无需额外驱动void main() { macroPerfTestE2E(cubic_bezier_perf, kCubicBezierRouteName); }带body的完整示例见 test/picture_cache_perf_e2e.dart设置 1 秒pageDelay然后在body中对内部嵌套的 TabBarView 做 3 轮左右往返拖拽模拟用户滚动以触发 picture cache 的真实压力macroPerfTestE2E( picture_cache_perf, kPictureCacheRouteName, pageDelay: const Duration(seconds: 1), body: (WidgetController controller) async { final Finder nestedScroll find.byKey(const ValueKeyString(tabbar_view)); expect(nestedScroll, findsOneWidget); Futurevoid scrollOnce(double offset) async { await controller.timedDrag(nestedScroll, Offset(offset, 0.0), const Duration(milliseconds: 300)); await Futurevoid.delayed(const Duration(milliseconds: 500)); } for (var i 0; i 3; i 1) { await scrollOnce(-300.0); await scrollOnce(-300.0); await scrollOnce(300.0); await scrollOnce(300.0); } }, );3.4 本地运行与指标输出上述步骤完成后在 macrobenchmarks 目录内运行flutter drive --profile -t test/super_important_case_e2e.dart --driver test_driver/e2e_test.dart宿主脚本 test_driver/e2e_test.dart 的全部内容只有 5 行有效代码它通过integrationDriver启动应用并在responseDataCallback中把设备侧回传的performance数据以testOutputFilename: e2e_perf_summary写出——也就是说测试结束后指标会落在当前目录临时build目录下的e2e_perf_summary.json。macrobenchmarks/README.md 对 E2E 基准的运行命令给出的通用形式与之一致flutter drive --profile -t test/[test_name]_e2e.dart --driver test_driver/e2e_test.dart。该 JSON 中常用指标继承自原文档average_frame_build_time_millisaverage_frame_rasterization_time_millisworst_frame_build_time_millisworst_frame_rasterization_time_millis补充一个源码层面的背景帧指标来自引擎时间轴中 UI 线程 build/layout/paint 与光栅化线程耗时的统计build time对应 Dart 侧构建/布局/绘制帧的耗时rasterization time对应引擎光栅化线程的耗时二者共同解释一帧是否“丢帧”。4. 2a. 传统 driver 测试已废弃保留说明原文档标注该路径为deprecated若第 2 步的 e2e 方式已足够则跳过。这里完整继承其内容以备查设备侧应用固定使用 macrobenchmarks/test_driver/run_app.dart其他测试都依赖它改动前需先与 Flutter 成员讨论在 macrobenchmarks/test_driver 下添加super_important_case_perf_test.dart调用macroPerfTestimport package:flutter_driver/flutter_driver.dart; import package:macrobenchmarks/common.dart; import util.dart; void main() { macroPerfTest( super_important_case, kSuperImportantCaseRouteName, pageDelay: const Duration(seconds: 1), /* optional */ driverOps: (FlutterDriver driver) async { ... }, /* optional */ setupOps: (FlutterDriver driver) async { ... }, ); }其中driverOps提供基准测试期间驱动页面的自定义操作如滚动setupOps提供基准测试开始前的操作在 macrobenchmarks 目录内运行flutter drive -t test_driver/run_app.dart --driver test_driver/super_important_case_perf.dart指标写入临时build目录下的super_important_case_perf__timeline_summary.json对比e2e 路径写入e2e_perf_summary.json同样包含average_frame_build_time_millis、average_frame_rasterization_time_millis、worst_frame_build_time_millis、worst_frame_rasterization_time_millis等指标。driver 版的实现可对照 test_driver/util.dartmacroPerfTest通过driver.traceAction包裹采样窗口TimelineSummary.summarize后写出testName.timeline_summary.json与 e2e 版“body 与 duration 取长者”的语义相同await durationFuture与driverOps并行。当前仓库的 bin/tasks 中仍并存*_perf__timeline_summary.dart与*_perf__e2e_summary.dart两类 devicelab 任务说明旧路线在存量场景上仍在运行新场景建议直接用 e2e 路线。5. 第三步更新 README原文档要求把新测试加入 macrobenchmarks/README.md 的列表。当前 README 的结构是“Mobile benchmarksdriver 类逐一列出[test_name]与命令 E2E benchmarks列出 E2E 化测试清单与运行命令”新增 e2e 测试时应在 “E2E benchmarks” 一节补上你的test/[test_name]_e2e.dart并保证文档中的命令与第 3.4 节一致。6. 第四步接入 devicelab 实现每 commit 自动采集原文档的论证是保持 Flutter 高性能靠“偶尔本地跑一次再人工看指标”是不够的必须让 devicelab 在每个 Flutter commit 上自动运行该测试才能快速发现super_important_case的回归或提速。原文档的步骤为在 devicelab 的任务清单中注册super_important_case_perf__e2e_summary原文档写的是dev/devicelab/manifest.yaml参照其他任务设置描述、选择 agent如linux/android的 Moto G4 或mac/ios的 iPhone 6s并标记flaky: true——在观察期不阻塞构建树在 dev/devicelab/bin/tasks 添加super_important_case_perf__e2e_summary.dartimport dart:async; import package:flutter_devicelab/tasks/perf_tests.dart; import package:flutter_devicelab/framework/adb.dart; import package:flutter_devicelab/framework/framework.dart; Futurevoid main() async { deviceOperatingSystem DeviceOperatingSystem.android; // or ios await task(createSuperImportantCasePerfE2ETest()); }在 dev/devicelab/lib/tasks/perf_tests.dart 中添加任务工厂函数TaskFunction createSuperImportantCasePerfE2ETest() { return PerfTest.e2e( ${flutterDirectory.path}/dev/benchmarks/macrobenchmarks, test/super_important_case_e2e.dart, ).run; }在 dev/devicelab 目录、连接 Android 或 iOS 真机的情况下本地验证../../bin/cache/dart-sdk/bin/dart bin/run.dart -t super_important_case_perf__e2e_summary应看到成功与指标摘要输出提交包含以上全部改动的 pull request当测试稳定运行几天后移除flaky: true。原文档还建议创建一个日历提醒因为这往往需要等一段时间。与当前仓库的差异重要本仓库中dev/devicelab/manifest.yaml已不存在任务注册位于仓库根目录的 .ci.yaml。e2e 性能任务在那里以如下形态出现以 backdrop_filter 为例见 .ci.yaml- name: Linux_mokey backdrop_filter_perf__e2e_summary recipe: devicelab/devicelab_drone presubmit: false timeout: 60 properties: tags: [devicelab, android, linux, mokey] task_name: backdrop_filter_perf__e2e_summary其中task_name与bin/tasks下的文件名对应如 backdrop_filter_perf__e2e_summary.dart其内容就是设置deviceOperatingSystem后调用task(createBackdropFilterPerfE2ETest())与原文档模板一致。因此在本仓库落地时步骤 1 应改为“在.ci.yaml中按现有*_e2e_summary条目格式添加任务条目tags 标明平台与设备”其余步骤不变。PerfTest.e2e的参数佐证dev/devicelab/lib/tasks/perf_tests.dart 中PerfTest有两个构造器普通PerfTestdriver 路线需要timelineFileName、testDriver与PerfTest.e2ee2e 路线testDriver默认就是test_driver/e2e_test.dartresultFilename默认为e2e_perf_summary——正好对应第 3.4 节的输出文件名。从源码结构看benchmarkScoreKeys的默认值_kCommonScoreKeys即包含average_frame_build_time_millis、worst_frame_build_time_millis、90th/99th_percentile_frame_build_time_millis、average_frame_rasterizer_time_millis等一系列键见 perf_tests.dart 的注释这说明 devicelab 上报给看板的指标集正是原文档列举的四项“常用指标”的超集。7. 第五步设置基准线benchmark baseline原文档最后一步任务会在 devicelab 上自动运行结果展示在 Flutter 性能看板flutter-dashboard当新测试积累足够数据后在看板上设置 baseline。对于vsync_transitions_missed这类指标需把单位从默认的 ms 改为 frames帧或其他合适单位。这一步是纯看板操作仓库内无对应代码但它是链路闭环的关键没有 baselinedevicelab 采集到的每个 commit 的指标就无法判断“是回归还是正常波动”。8. 小结与仓库延伸阅读把整条链路串起来lib/src/*.dart场景页lib/common.dart路由常量lib/main.dart路由表与首页按钮→test/*_e2e.dart采样窗口与驱动操作test_driver/e2e_test.dart宿主脚本固定复用→devicelab/bin/tasks/*_e2e_summary.dartlib/tasks/perf_tests.dart的PerfTest.e2e.ci.yaml任务条目 → 性能看板上的 baseline 与回归告警。原文档在结尾对完成全部步骤的贡献者表达了祝贺——完成这条链路确实是对 Flutter 性能体系的一次实质性贡献也鼓励贡献者持续改进本文档本身。延伸阅读均为仓库内相对路径dev/benchmarks/macrobenchmarks/README.mddriver 与 E2E 基准的本地运行命令、Web 基准lib/src/web下 raw/widget/widget-build 三类 recorder写法dev/benchmarks/macrobenchmarks/lib/common.dart全部路由常量与kScrollableName的定义dev/benchmarks/macrobenchmarks/test/util.dartmacroPerfTestE2E/macroPerfTestMultiPageE2E完整实现dev/devicelab/lib/tasks/perf_tests.dartPerfTest/PerfTest.e2e全部参数与指标键说明.ci.yaml所有*_e2e_summary任务的注册格式与 agent 选择dev/contributing/testingFlutter 仓库测试相关文档索引。【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

2026/9/7 19:45:47

Claude Code不完全指南:安装配置、本地模型与省token实战

我最早接触 Claude Code 的时候,心里其实挺不以为然的——终端里跑一个 AI,能比网页对话框强到哪去?直到有一天,它自己翻出一个三个月前改过的配置文件,顺手修完 bug 还自动跑了一遍测试,我才意识到这东西根…

2026/9/7 19:40:46

MySQL事务提交失败处理实战:回滚、重试与幂等设计

在开发中遇到“MySQL事务提交失败”这类问题,几乎是每个后端工程师都绕不过去的坎。尤其是涉及订单、库存、支付这类核心链路时,一旦事务在提交阶段爆出异常,很多人第一反应就是“回滚不就完了”,但真正落地时却发现,情…

2026/9/7 20:55:56

Feed流场景下二级缓存设计:Caffeine与Redis的实战指南

1. Feed流场景下,为什么单层缓存扛不住1.1 Feed流读多写少的“骗局”:你以为的读多写少,其实是读热点写分散今天这篇是Feed流缓存设计系列的第5天,聊的是二级缓存。先说个容易被新手忽略的事实:Feed流确实是典型的读多…

2026/9/7 20:55:56

I_CreditControlArea CDS视图解析:信用控制域主数据建模与开发实践

1. 为什么信用控制域值得单独建一张 CDS 视图做 SAP 财务或主数据治理的朋友,对“信用控制域”这个词应该不陌生。它是 SAP 信用管理里的核心组织单元,决定了某个客户的信用额度在哪个范围内生效、按什么币种统计未清项、信用检查的级别和方式是什么。但…

2026/9/7 20:55:56

想做一首专属 MV?AI 生成视频画面的工程化路径

以前做一首 MV 需要拍摄团队、布景、后期,成本以万计。现在 AI 能把你的歌直接变成视频画面——歌词驱动、风格自定义。这篇讲 AI 生成 MV 的实现原理、实操路径,以及它到底能用到什么程度。一、AI MV 是怎么做出来的AI 生成 MV 大体有三条技术路线&…

2026/9/7 20:50:56

切纸机数据采集与远程监控方案:从Modbus到Web全链路实践

说实话,切纸机在车间里的地位一直挺微妙的。前面是印刷机、复合机这样的大设备,后面连着包装线,它夹在中间,看起来就是“咔嚓一刀”的动作,技术含量好像不高。可真管过车间的人都知道,这台设备一旦停下来&a…

2026/9/7 0:47:43

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/7 0:14:19

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/7 0:14:17

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/7 0:03:36

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

这次我们来看一个把目标检测算法和桌面端工具结合得很典型的项目:基于 YOLOv8 PyQt5 的麦穗稻穗检测识别系统。这个项目本身不是新概念,但它的价值在于落地形态很完整。YOLOv8 负责核心的麦穗稻穗目标检测,PyQt5 负责提供可视化的桌面交互界…

2026/9/7 0:03:36

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

简介:UL 1642是锂电池安全领域的重要规范,本中文版资源适合锂电池制造商、检测机构工程师及产品认证相关人员阅读,用于理解电池在设计与制造层面的安全要求、测试方法与合规要点。资源共1个PDF文件,压缩包大小834KB,便…

2026/9/7 0:03:36

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

简介:BS EN 13814-1:2019是英国采纳欧洲标准EN 13814-1:2019的正式版本,由BSI标准出版,重点规定游乐设施和游乐设备在设计与制造环节的安全准则,与BS EN 13814-2:2019、BS EN 13814-3:2019共同取代旧版BS EN 13814:2004。该标准面…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/6 19:33:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/6 10:19:40

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…