web_benchmarks testing 目录运行指南:Flutter Web 基准测试的客户端-服务器端到端实践
【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages
本文以web_benchmarks包内置的 testing 目录运行说明 为主线,完整梳理"如何在本仓库中运行 Flutter Web 基准测试",并深入到 browser.dart、runner.dart、client.dart 等源码,还原从 Chrome 定位、应用构建、性能追踪到指标收率的完整链路。读完本文,你既能照搬命令跑通 testing 目录下的全部基准测试,也能理解 web_benchmarks 的客户端-服务器协作模型与指标计算原理,为在自己的 Flutter Web 应用中编写与接入基准测试打下基础。
一、testing 目录与 web_benchmarks 框架概览
web_benchmarks是 Flutter 团队维护的 Flutter Web 基准测试工具包,当前仅支持在 Chrome 中运行基准(详见 包级 README)。仓库中的 testing 目录 是一个完整的基准测试示例工程:
- test_app:一个可运行的 Flutter Web 示例应用(含
lib/main.dart、lib/home_page.dart、lib/about_page.dart),其benchmark子目录下存放基准测试用例; - web_benchmarks_test.dart:核心测试文件,驱动真实 Chrome 运行滚动、翻页、点击等基准;
- 其余为测试基础设施(
benchmark/test_infra/)。
testing 目录下的基准采用客户端-服务器(client-server)模型,这一点与integration_test包的思路类似:一端是在浏览器内与被测应用一起运行的客户端代码,另一端是负责构建产物、托管静态资源、并通过 Chrome DevTools 协议提取性能追踪数据的服务器(详见 包级 README 及 server.dart 的serveWebBenchmark文档注释)。后续章节会先给出官方推荐的运行步骤,再逐层剖析其内部机制。
二、运行前提:让测试能找到 Chrome
testing 目录的测试需要真实的 Chrome 浏览器,测试框架通过Chrome.launch启动它(browser.dart)。因此第一步是"安装一个测试可以找到的 Chrome"。
从源码看,Chrome 可执行文件的定位逻辑位于_findSystemChromeExecutable(browser.dart),其查找顺序如下:
| 平台 | 查找方式 |
|---|---|
| 任意平台 | 优先读取环境变量CHROME_EXECUTABLE(适用于 CI 等 Chrome 位于非标准路径的环境) |
| Linux | 执行which google-chrome,找不到则抛出Failed to locate system Chrome installation. |
| macOS | 固定路径/Applications/Google Chrome.app/Contents/MacOS/Google Chrome |
| Windows | 依次探测LOCALAPPDATA、PROGRAMFILES、PROGRAMFILES(X86)下的Google\Chrome\Application\chrome.exe |
此外还有两个环境变量会影响启动行为(browser.dart):
CHROME_NO_SANDBOX=true:追加--no-sandbox启动参数;- 在 Linux 无显示器环境(如 CI)下配合
headless: true使用,此时还会追加--use-gl=angle、--use-angle=swiftshader、--enable-unsafe-swiftshader、--disable-gpu-sandbox等软件渲染参数。
在本地开发机上,通常只需安装好系统 Chrome 即可满足查找条件。
三、三步运行 testing 目录测试(官方步骤)
在满足 Chrome 前提后,按官方 testing README 依次执行以下命令。
第 1 步:拉取 test_app 的依赖
flutter pub get --directory testing/test_app该命令在testing/test_app目录(pubspec.yaml)下执行flutter pub get,拉取示例应用与基准测试用例所需的依赖。
第 2 步:拉取 web_benchmarks 包的依赖
在 web_benchmarks 包根目录 下执行:
flutter pub get第 3 步:运行测试
flutter test testing官方文档给出的参考输出如下(节选):
00:03 +0: Can run a web benchmark Launching Chrome. Launching Google Chrome 98.0.4758.102 Waiting for the benchmark to report benchmark profile. [CHROME]: [0215/133233.327761:ERROR:socket_posix.cc(150)] bind() failed: Address already in use (98) [CHROME]: [CHROME]: DevTools listening on ws://[::1]:10000/devtools/browser/4ef82be6-9b68-4fd3-ab90-cd603d25ceb1 Connecting to DevTools: ws://localhost:10000/devtools/page/21E7271507E9BC796B957E075515520F Connected to Chrome tab: (http://localhost:9999/index.html) Launching benchmark "scroll" Extracted 299 measured frames. Skipped 1 non-measured frames. Launching benchmark "page" [APP] Testing round 0... [APP] Testing round 1... ... [APP] Testing round 9... Extracted 490 measured frames. Skipped 0 non-measured frames. Launching benchmark "tap" [APP] Testing round 0... ... Received profile data 00:26 +1: All tests passed!对这段输出做几点解读,帮助判断运行是否正常:
bind() failed: Address already in use (98)是开发机端口占用告警,属于测试过程中的常见噪音,不代表测试失败——框架随后仍成功连接到了 DevTools(ws://localhost:10000);- 默认端口约定:基准服务器端口
9999、Chrome DevTools 调试端口10000,均定义于 runner.dart; Extracted N measured frames.与Skipped N non-measured frames.由BlinkTraceSummary.fromJson打印(browser.dart),表示从 Chrome 性能追踪中成功解析出的有效帧数;[APP] Testing round 0...系列输出经/print-to-console端点由浏览器端转发打印(runner.dart)。
注意:官方文档还提示,若上述步骤失效,可参考 CI 中web_benchmarks_test步骤当前的实际做法同步更新本文档(原始说明链接指向仓库根目录的.cirrus.yml,本仓库快照中未包含该文件,实际排错可结合 tool/run_tests.dart 的流程核对)。
四、测试内部到底发生了什么:构建、启动、追踪与收率
flutter test testing只是入口,真正的基准驱动逻辑在 web_benchmarks_test.dart 中,它通过serveWebBenchmark(server.dart)编排整个流程。该文件共注册了三个测试用例:
- Can run a web benchmark:依次运行
appNavigate(翻页)、appScroll(滚动)、appTap(点击)三个基准,入口为benchmark/test_infra/client/app_client.dart; - Can run a web benchmark with an alternate benchmarkPath:使用
benchmark/test_infra/client/simple_benchmark_path_client.dart,并传入自定义benchmarkPath,校验初始加载与重载时的expectedUrl指标; - Can run a web benchmark with wasm:使用
CompilationOptions.wasm()以 WebAssembly + skwasm 方式编译运行,校验isWasm指标。
每个用例都会对全部基准名 × 全部指标 × 全部统计计算组合做存在性断言,并验证totalUiFrame.average指标存在(web_benchmarks_test.dart)。
BenchmarkServer.run()(runner.dart)的完整流程为:
- 校验 flutter 可执行:通过
ProcessManager.canRun('flutter')检查,若不在 PATH 中直接抛异常; - 构建 Web 应用:执行
flutter build web --profile -t <entryPoint>,并附带--dart-define=FLUTTER_WEB_ENABLE_PROFILING=true;treeShakeIcons=false时追加--no-tree-shake-icons;CompilationOptions.wasm()时追加--wasm --no-strip-wasm(对应 dart2wasm + skwasm,而默认CompilationOptions.js()对应 dart2js + CanvasKit,见 server.dart 文档说明); - 托管静态资源:用
shelf_static提供build/web目录,并对 HTML 响应附加Cross-Origin-Opener-Policy: same-origin与Cross-Origin-Embedder-Policy: require-corp头,使页面处于 crossOriginIsolated 状态——skwasm 渲染器依赖 SharedArrayBuffer,且该状态能获得更高精度的计时器(runner.dart); - 启动 Chrome:以 headless 模式 +
--remote-debugging-port=10000启动,窗口默认 1024×1024(ChromeOptions默认值,见 browser.dart),随后通过 WebKit Inspection Protocol 连接 DevTools 并找到打开http://localhost:9999/index.html的标签页; - 按序运行基准:服务器通过
/next-benchmark逐个下发基准名,每个基准运行前调用/start-performance-tracing(Tracing.start,类别为blink,blink.user_timing,见 browser.dart),运行结束后/stop-performance-tracing取回追踪数据,并汇总为BenchmarkResults返回。
值得注意的是,头部ERROR:bind()告警并不会中断流程:Chrome 的 stderr 中DevTools listening ...这一行才是连接成功的信号,框架会阻塞等待该行出现(browser.dart)。
五、客户端-服务器协作模型与手动回退
测试期间,浏览器内运行的是 client.dart 提供的runBenchmarks客户端。它以LocalBenchmarkServerClient与服务器通过 REST 接口协作,核心端点如下(实现见 client.dart 与 runner.dart):
| 端点 | 方向 | 作用 |
|---|---|---|
POST /next-benchmark | 客户端 → 服务器 | 上报基准名列表,领取下一个要运行的基准名 |
POST /start-performance-tracing?label=... | 客户端 → 服务器 | 请求服务器通过 DevTools 协议开启 chrome://tracing |
POST /stop-performance-tracing | 客户端 → 服务器 | 停止追踪并取回原始 trace 数据 |
POST /profile-data | 客户端 → 服务器 | 上报某个基准的 Profile 数据 |
POST /on-error | 客户端 → 服务器 | 上报浏览器端异常,服务器会中止整个任务 |
POST /print-to-console | 客户端 → 服务器 | 把浏览器端日志转发到服务器控制台 |
客户端在浏览器中的print与未捕获异常都会经 zone 规范转发给服务器(client.dart),这就是测试输出中[APP] Testing round 0...的来源。
该模型还内置了手动回退(manual fallback)机制:当服务器不可用(例如用flutter run直接调试而非走测试框架)时,/next-benchmark返回 404,客户端据此进入手动模式——页面上会渲染一个可点击的基准名列表,运行结果直接可视化打印在网页上(TimeseriesVisualization会用 canvas 绘制时序柱状图,见 client.dart)。这使得基准既可以自动化跑,也方便人工调试。
六、收集哪些指标:帧耗时与统计计算
基准数据最终以BenchmarkScore(metric, value)形式呈现,指标体系定义于 metrics.dart:
- Flutter 帧级指标(
BenchmarkMetric枚举):preroll_frame(提交图层/图片前的预处理,含变换与裁剪计算)、apply_frame(向渲染器提交图层与图片信息)、draw_frame(PlatformDispatcher.onDrawFrame回调耗时)、flutter_frame.total_time、flutter_frame.build_time、flutter_frame.raster_time(分别对应 Flutter EngineFrameTiming的totalSpan、buildDuration、rasterDuration); - UI 线程整体指标:
totalUiFrame.average,来自 Blink trace 中WebViewImpl::beginFrame(脚本执行)与updateAllLifecyclePhases(样式、布局、绘制、合成)两类事件的平均耗时之和(browser.dart); - 每个指标的统计计算(
BenchmarkMetricComputation.values,见 metrics.dart):average、outlierAverage、outlierRatio、noise以及p50、p90、p95三个百分位。
统计采样数以kMeasuredSampleCount = 100为上限(common.dart),测试运行时打印的Extracted N measured frames.即来自该解析过程;若一个可测帧都解析不出来,会抛出 "No measured frames found in benchmark tracing data." 并提示可能原因(帧泵不够或 Chrome 追踪格式变更),同时把原始 trace 写入./chrome-trace.json便于排查(browser.dart)。
七、结果分析:计算增量与平均值
跑完基准得到 JSON 结果后,web_benchmarks还提供离线分析能力(见 包级 README 与 analysis.dart):
computeDelta(baseline, test):计算两份基准结果的差值,用于对比基线;computeAverage(results...):计算多份结果的均值,用于多轮合并。
仓库 example/analyze_example.dart 给出了完整用法:用BenchmarkResults.parse读取 JSON 文件后调用上述函数,再把结果toJson()输出。该文件同样被包级 README 以<?code-excerpt>方式引用,是编写分析脚本的直接模板。
八、自动化:tool/run_tests.dart
仓库还提供了可脚本化的测试入口 tool/run_tests.dart,其执行逻辑与"三步法"完全对应:
- 在
testing/test_app下执行flutter pub get(flutter pub get --directory的等价实现); - 在包根目录执行
flutter test testing。
此外该脚本目前只在 Linux 主机上实际运行(其他平台直接跳过),并预留了dart run tool/run_tests.dart的调用方式说明,适合接入自定义 CI 任务。需要说明的是,官方测试还包含包内 test/ 目录 下的单元测试(如browser_test.dart、computations_test.dart等,另有 more_tests_in_testing_dir_test.dart 将 testing 目录纳入测试范围),flutter test testing主要针对端到端基准用例。
九、常见问题速查
Failed to locate system Chrome installation.:Linux 下which google-chrome找不到 Chrome,请安装 Chrome 或设置CHROME_EXECUTABLE环境变量指向可执行文件;flutter executable is not runnable:flutter不在 PATH 中,请修正环境;Address already in use (98):多为开发机端口占用,属噪音;若 9999/10000 端口确实被占用导致连接失败,可通过serveWebBenchmark的benchmarkServerPort与chromeDebugPort参数调整;No measured frames found in benchmark tracing data.:按错误提示检查基准是否正常泵帧,或确认近期 Chrome 版本是否变更了追踪数据格式,必要时核对 browser.dart 中BlinkTraceEvent的解析逻辑(调试时原始 trace 会保存在./chrome-trace.json)。
至此,从环境准备、三步运行、内部流程到结果分析,web_benchmarks testing 目录的完整运行链路已全部打通;后续如需编写自己的 Web 基准,可参照 web_benchmarks_test.dart 与 client.dart 的runBenchmarks接口进行扩展。
【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考