Skip to content

Commit 048ae84

Browse files
Add flutter frame timings to benchmark metrics (#7759)
1 parent 80f442e commit 048ae84

File tree

8 files changed

+131
-36
lines changed

8 files changed

+131
-36
lines changed

packages/web_benchmarks/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 3.1.0-wip
2+
3+
* Add `flutter_frame.total_time`, `flutter_frame.build_time`, and `flutter_frame.raster_time`
4+
metrics to benchmark results. These values are derived from the Flutter `FrameTiming` API.
5+
* Expose a new library `metrics.dart` that contains definitions for the benchmark metrics.
6+
17
## 3.0.0
28

39
* **Breaking change:** removed the `initialPage` parameter from the `serveWebBenchmark`
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
export 'src/metrics.dart';
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
/// The names for the metrics collected by the benchmark recorder.
6+
enum BenchmarkMetric {
7+
/// The name for the benchmark metric that includes frame-related computations
8+
/// prior to submitting layer and picture operations to the underlying
9+
/// renderer, such as HTML and CanvasKit.
10+
///
11+
/// During this phase we compute transforms, clips, and other information
12+
/// needed for rendering.
13+
prerollFrame('preroll_frame'),
14+
15+
/// The name for the benchmark metric that includes submitting layer and
16+
/// picture information to the renderer.
17+
applyFrame('apply_frame'),
18+
19+
/// The name for the benchmark metric that measures the time spent in
20+
/// [PlatformDispatcher]'s onDrawFrame callback.
21+
drawFrame('draw_frame'),
22+
23+
/// The name for the benchmark metric that tracks the timespan between vsync
24+
/// start and raster finish for a Flutter frame.
25+
///
26+
/// This value corresponds to [FrameTiming.totalSpan] from the Flutter Engine.
27+
flutterFrameTotalTime('flutter_frame.total_time'),
28+
29+
/// The name for the benchmark metric that tracks the duration to build the
30+
/// Flutter frame on the Dart UI thread.
31+
///
32+
/// This value corresponds to [FrameTiming.buildDuration] from the Flutter
33+
/// Engine.
34+
flutterFrameBuildTime('flutter_frame.build_time'),
35+
36+
/// The name for the benchmark metric that tracks the duration to rasterize
37+
/// the Flutter frame on the Dart raster thread.
38+
///
39+
/// This value corresponds to [FrameTiming.rasterDuration] from the Flutter
40+
/// Engine.
41+
flutterFrameRasterTime('flutter_frame.raster_time');
42+
43+
const BenchmarkMetric(this.label);
44+
45+
/// The metric name used in the recorded benchmark data.
46+
final String label;
47+
}
48+
49+
/// The name for the benchmark metric that records the 'averageTotalUIFrameTime'
50+
/// from the Blink trace summary.
51+
const String totalUiFrameAverage = 'totalUiFrame.average';
52+
53+
/// The list of expected benchmark metrics for the current compilation mode, as
54+
/// determined by the value of [useWasm].
55+
List<BenchmarkMetric> expectedBenchmarkMetrics({required bool useWasm}) {
56+
return <BenchmarkMetric>[
57+
// The skwasm renderer doesn't have preroll or apply frame steps in its
58+
// rendering.
59+
if (!useWasm) ...<BenchmarkMetric>[
60+
BenchmarkMetric.prerollFrame,
61+
BenchmarkMetric.applyFrame,
62+
],
63+
BenchmarkMetric.drawFrame,
64+
BenchmarkMetric.flutterFrameTotalTime,
65+
BenchmarkMetric.flutterFrameBuildTime,
66+
BenchmarkMetric.flutterFrameRasterTime,
67+
];
68+
}

packages/web_benchmarks/lib/src/recorder.dart

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'package:meta/meta.dart';
1818
import 'package:web/web.dart' as html;
1919

2020
import 'common.dart';
21+
import 'metrics.dart';
2122

2223
/// The number of samples from warm-up iterations.
2324
///
@@ -27,16 +28,6 @@ const int _kWarmUpSampleCount = 200;
2728
/// The total number of samples collected by a benchmark.
2829
const int kTotalSampleCount = _kWarmUpSampleCount + kMeasuredSampleCount;
2930

30-
/// A benchmark metric that includes frame-related computations prior to
31-
/// submitting layer and picture operations to the underlying renderer, such as
32-
/// HTML and CanvasKit. During this phase we compute transforms, clips, and
33-
/// other information needed for rendering.
34-
const String kProfilePrerollFrame = 'preroll_frame';
35-
36-
/// A benchmark metric that includes submitting layer and picture information
37-
/// to the renderer.
38-
const String kProfileApplyFrame = 'apply_frame';
39-
4031
/// Measures the amount of time [action] takes.
4132
Duration timeAction(VoidCallback action) {
4233
final Stopwatch stopwatch = Stopwatch()..start();
@@ -250,7 +241,7 @@ abstract class SceneBuilderRecorder extends Recorder {
250241
PlatformDispatcher.instance.onDrawFrame = () {
251242
final FlutterView? view = PlatformDispatcher.instance.implicitView;
252243
try {
253-
_profile.record('drawFrameDuration', () {
244+
_profile.record(BenchmarkMetric.drawFrame.label, () {
254245
final SceneBuilder sceneBuilder = SceneBuilder();
255246
onDrawFrame(sceneBuilder);
256247
_profile.record('sceneBuildDuration', () {
@@ -390,8 +381,11 @@ abstract class WidgetRecorder extends Recorder implements FrameRecorder {
390381
@mustCallSuper
391382
void frameDidDraw() {
392383
endMeasureFrame();
393-
profile.addDataPoint('drawFrameDuration', _drawFrameStopwatch.elapsed,
394-
reported: true);
384+
profile.addDataPoint(
385+
BenchmarkMetric.drawFrame.label,
386+
_drawFrameStopwatch.elapsed,
387+
reported: true,
388+
);
395389

396390
if (shouldContinue()) {
397391
PlatformDispatcher.instance.scheduleFrame();
@@ -417,29 +411,54 @@ abstract class WidgetRecorder extends Recorder implements FrameRecorder {
417411
_RecordingWidgetsBinding.ensureInitialized();
418412
final Widget widget = createWidget();
419413

420-
registerEngineBenchmarkValueListener(kProfilePrerollFrame, (num value) {
414+
registerEngineBenchmarkValueListener(BenchmarkMetric.prerollFrame.label,
415+
(num value) {
421416
localProfile.addDataPoint(
422-
kProfilePrerollFrame,
417+
BenchmarkMetric.prerollFrame.label,
423418
Duration(microseconds: value.toInt()),
424419
reported: false,
425420
);
426421
});
427-
registerEngineBenchmarkValueListener(kProfileApplyFrame, (num value) {
422+
registerEngineBenchmarkValueListener(BenchmarkMetric.applyFrame.label,
423+
(num value) {
428424
localProfile.addDataPoint(
429-
kProfileApplyFrame,
425+
BenchmarkMetric.applyFrame.label,
430426
Duration(microseconds: value.toInt()),
431427
reported: false,
432428
);
433429
});
434430

431+
late void Function(List<FrameTiming> frameTimings) frameTimingsCallback;
432+
binding.addTimingsCallback(
433+
frameTimingsCallback = (List<FrameTiming> frameTimings) {
434+
for (final FrameTiming frameTiming in frameTimings) {
435+
localProfile.addDataPoint(
436+
BenchmarkMetric.flutterFrameTotalTime.label,
437+
frameTiming.totalSpan,
438+
reported: false,
439+
);
440+
localProfile.addDataPoint(
441+
BenchmarkMetric.flutterFrameBuildTime.label,
442+
frameTiming.buildDuration,
443+
reported: false,
444+
);
445+
localProfile.addDataPoint(
446+
BenchmarkMetric.flutterFrameRasterTime.label,
447+
frameTiming.rasterDuration,
448+
reported: false,
449+
);
450+
}
451+
});
452+
435453
binding._beginRecording(this, widget);
436454

437455
try {
438456
await _runCompleter.future;
439457
return localProfile;
440458
} finally {
441-
stopListeningToEngineBenchmarkValues(kProfilePrerollFrame);
442-
stopListeningToEngineBenchmarkValues(kProfileApplyFrame);
459+
stopListeningToEngineBenchmarkValues(BenchmarkMetric.prerollFrame.label);
460+
stopListeningToEngineBenchmarkValues(BenchmarkMetric.applyFrame.label);
461+
binding.removeTimingsCallback(frameTimingsCallback);
443462
}
444463
}
445464
}
@@ -508,8 +527,11 @@ abstract class WidgetBuildRecorder extends Recorder implements FrameRecorder {
508527
// Only record frames that show the widget.
509528
if (showWidget) {
510529
endMeasureFrame();
511-
profile.addDataPoint('drawFrameDuration', _drawFrameStopwatch.elapsed,
512-
reported: true);
530+
profile.addDataPoint(
531+
BenchmarkMetric.drawFrame.label,
532+
_drawFrameStopwatch.elapsed,
533+
reported: true,
534+
);
513535
}
514536

515537
if (shouldContinue()) {

packages/web_benchmarks/lib/src/runner.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import 'benchmark_result.dart';
1919
import 'browser.dart';
2020
import 'common.dart';
2121
import 'compilation_options.dart';
22+
import 'metrics.dart';
2223

2324
/// The default port number used by the local benchmark server.
2425
const int defaultBenchmarkServerPort = 9999;
@@ -224,11 +225,11 @@ class BenchmarkServer {
224225
if (latestPerformanceTrace != null) {
225226
final BlinkTraceSummary? traceSummary =
226227
BlinkTraceSummary.fromJson(latestPerformanceTrace!);
227-
profile['totalUiFrame.average'] =
228+
profile[totalUiFrameAverage] =
228229
traceSummary?.averageTotalUIFrameTime.inMicroseconds;
229230
profile['scoreKeys'] ??=
230231
<dynamic>[]; // using dynamic for consistency with JSON
231-
(profile['scoreKeys'] as List<dynamic>).add('totalUiFrame.average');
232+
(profile['scoreKeys'] as List<dynamic>).add(totalUiFrameAverage);
232233
latestPerformanceTrace = null;
233234
}
234235
collectedProfiles.add(profile);

packages/web_benchmarks/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: web_benchmarks
22
description: A benchmark harness for performance-testing Flutter apps in Chrome.
33
repository: https://github.com/flutter/packages/tree/main/packages/web_benchmarks
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+web_benchmarks%22
5-
version: 3.0.0
5+
version: 3.1.0-wip
66

77
environment:
88
sdk: ^3.3.0

packages/web_benchmarks/test/src/analysis_test.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// Copyright 2013 The Flutter Authors. All rights reserved.
6-
// Use of this source code is governed by a BSD-style license that can be
7-
// found in the LICENSE file.
8-
95
import 'package:flutter_test/flutter_test.dart';
106
import 'package:web_benchmarks/analysis.dart';
117

packages/web_benchmarks/testing/test_app/benchmark/web_benchmarks_test.dart

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'dart:io';
77

88
import 'package:test/test.dart';
99

10+
import 'package:web_benchmarks/metrics.dart';
1011
import 'package:web_benchmarks/server.dart';
1112
import 'package:web_benchmarks/src/common.dart';
1213

@@ -90,14 +91,10 @@ Future<BenchmarkResults> _runBenchmarks({
9091
compilationOptions: compilationOptions,
9192
);
9293

93-
// The skwasm renderer doesn't have preroll or apply frame steps in its rendering.
94-
final List<String> expectedMetrics = compilationOptions.useWasm
95-
? <String>['drawFrameDuration']
96-
: <String>[
97-
'preroll_frame',
98-
'apply_frame',
99-
'drawFrameDuration',
100-
];
94+
final List<String> expectedMetrics =
95+
expectedBenchmarkMetrics(useWasm: compilationOptions.useWasm)
96+
.map((BenchmarkMetric metric) => metric.label)
97+
.toList();
10198

10299
for (final String benchmarkName in benchmarkNames) {
103100
for (final String metricName in expectedMetrics) {

0 commit comments

Comments
 (0)