Skip to content

Conversation

@yjbanov
Copy link
Contributor

@yjbanov yjbanov commented Jun 7, 2023

FlutterTimeline

Add a new class FlutterTimeline that's a drop-in replacement for Timeline from dart:developer. In addition to forwarding invocations of startSync, finishSync, timeSync, and instantSync to dart:developer, provides the following extra methods that make is easy to collect timings for code blocks on a frame-by-frame basis:

  • debugCollect() - aggregates timings since the last reset, or since the app launched.
  • debugReset() - forgets all data collected since the previous reset, or since the app launched. This allows clearing data from previous frames so timings can be attributed to the current frame.
  • now - this was enhanced so that it works on the web by calling window.performance.now (in Timeline this is a noop in Dart web compilers).
  • collectionEnabled - a field that controls whether FlutterTimeline stores timings in memory. By default this is disabled to avoid unexpected overhead (although the class is designed for minimal and predictable overhead). Specific benchmarks can enable collection to report to Skia Perf.

Semantics benchmarks

Add BenchMaterial3Semantics that benchmarks the cost of semantics when constructing a screen full of Material 3 widgets from nothing. It is expected that semantics will have non-trivial cost in this case, but we should strive to keep it much lower than the rendering cost. This is the case already. This benchmark shows that the cost of semantics is <10%.

Add BenchMaterial3ScrollSemantics that benchmarks the cost of scrolling a previously constructed screen full of Material 3 widgets. The expectation should be that semantics will have trivial cost, since we're just shifting some widgets around. As of today, the numbers are not great, with semantics taking >50% of frame time, which is what prompted this PR in the first place. As we optimize this, we want to see this number improve.

@flutter-dashboard flutter-dashboard bot added a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. c: contributor-productivity Team-specific productivity, code health, technical debt. labels Jun 7, 2023
@github-actions github-actions bot added the a: text input Entering text in a text field or keyboard related problems label Jun 7, 2023
@yjbanov yjbanov changed the title [web] add FlutterTimeline and semantics benchmarks that use it [framework,web] add FlutterTimeline and semantics benchmarks that use it Jun 7, 2023
@yjbanov yjbanov requested review from goderbauer and mdebbar June 7, 2023 21:04
Copy link
Contributor

@mdebbar mdebbar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with some nitpicks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have the potential of turning semantics on for other benchmarks?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because we reload the page between the benchmarks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The expect output numbers of this benchmarks should be very small as
/// The expected output numbers of this benchmarks should be very small as

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the 50% ratio is more important than the absolute numbers. Should we report the ratio instead? i.e. run the same widget with semantics and without semantics, and report the ratio between the two.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually attempted it just to rediscover that our benchmark system is designed to only accept timeline data in microseconds. Maybe we should generalize it to accept other units.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"extent"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check for mounted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already mounted by the time initState is called.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean inside Timer.run(() { ...here... });

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I don't think we need it. mounted being false inside the timer implies the benchmark ended before pumping any interesting frames we care to measure, which can't happen because WidgetRecorder waits for a minimum number of frames before stopping the benchmark.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 277 to 281
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the order of elements matter? If yes, then _chain.forEach(result.addAll) should be moved above the for loop. If no, then let's mention that in the dartdoc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does! Good catch. Fixed, and added a test for it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason performanceTimestamp is a double instead of 1000 * _performance.now().toInt()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried, but since there's no int64 or Int64List on the web, I have to store these as doubles anyway. Otherwise, I'd have to store values in List<int> (which on the web is an array of Numbers, i.e. double) and lose some performance predictability. This code relies on the fact that I can allocate small fixed-sized Float64List instances for a constant cost.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I was confused by this line:

static int get now => impl.performanceTimestamp.toInt();

But I see that it's being retained as a double elsewhere.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part doesn't seem to be true? It should be, though!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! I removed the assertion to debug something, and forgot to put it back.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call this debugCollectionEnabled to make clear that this is non-release only functionlaity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call this debugCollect and document that it can only be called in non-release mode.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering if collect should just always imply reset? Is there reason to collect, wait a little and then collect again later where you want the previously collected data to be included?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should also be called debutReset.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all these should probably also be guarded by !kReleaseMode to ensure that they are compiled out in release mode to lower the overhead even further.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these maybe only be computed if someone accesses aggregatedBlocks and/or getAggregated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@yjbanov
Copy link
Contributor Author

yjbanov commented Jun 9, 2023

@goderbauer I started adding the debug prefix everywhere but then I thought maybe we should encourage people to use this in release mode, if it's cheap enough. I collected these numbers:

  • Pixel 6 Dart AOT: ~0.4 microseconds/sample
  • M1 macOS Dart AOT: ~0.15 microseconds/sample
  • Pixel 6 dart2js: ~2 microseconds/sample
  • M1 macOS dart2js: ~0.4 microseconds/sample

So a few 10s of invocations won't cut into the frame budget, but may provide much better understanding of an app's real performance. Most of the cost comes from dart:developer. So I don't call it in release mode.

LMK which way you prefer to go. I'm open to either.

@goderbauer
Copy link
Member

I thought maybe we should encourage people to use this in release mode

I think that's what profile mode is for. It's release mode + hocks to measure performance. We should make this available in profile mode for sure. But I don't see the appeal for release mode.

@yjbanov
Copy link
Contributor Author

yjbanov commented Jun 9, 2023

I think that's what profile mode is for.

Oh, what meant was that apps could collect performance analytics from installed apps in production. But if we're not interested in supporting that, it's fine. That's out of scope for this PR anyway. Just a drive-by thought. The only issue is that the debug prefix will stick forever if we change our minds later.

@yjbanov yjbanov force-pushed the bench-semantics branch from c37af75 to 1bd4ba8 Compare June 9, 2023 18:53
@yjbanov yjbanov requested a review from goderbauer June 9, 2023 19:12
Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uber-nit:

Suggested change
/// useful in the profile mode where application performance is representative
/// useful in profile mode where application performance is representative

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add the doc line about this being a drop-in replacement for [Timeline.instantSync]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add: ... or the previous debugCollect ... ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update this since debugCollect automatically does a debugReset?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@yjbanov yjbanov added the autosubmit Merge PR when tree becomes green via auto submit App label Jun 21, 2023
@auto-submit auto-submit bot merged commit 07772a3 into flutter:master Jun 21, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jun 22, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jun 22, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jun 22, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jun 22, 2023
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Jun 22, 2023
flutter/flutter@c40baf4...042c036

2023-06-22 [email protected] Remove unnecessary variable `_hasPrimaryFocus` (flutter/flutter#129066)
2023-06-22 [email protected] Fix Material 3 Scrollable `TabBar` (flutter/flutter#125974)
2023-06-22 [email protected] Fix: Closing bottom sheet and removing FAB cause assertion failure (flutter/flutter#128566)
2023-06-22 [email protected] Add `InputDecorationTheme.merge` (flutter/flutter#129011)
2023-06-22 [email protected] Roll Packages from 9af50d4 to 95bc1c6 (6 revisions) (flutter/flutter#129351)
2023-06-22 [email protected] Prevent crashes on range errors when selecting device (flutter/flutter#129290)
2023-06-22 [email protected] Revert "Roll Flutter Engine from 703c9a14ac7f to 8cc6d6d5efdb (1 revision)" (flutter/flutter#129353)
2023-06-22 [email protected] Roll Flutter Engine from 703c9a14ac7f to 8cc6d6d5efdb (1 revision) (flutter/flutter#129339)
2023-06-22 [email protected] Roll Flutter Engine from d9530e2b87de to 703c9a14ac7f (1 revision) (flutter/flutter#129337)
2023-06-22 [email protected] Roll Flutter Engine from c6251a69a09a to d9530e2b87de (1 revision) (flutter/flutter#129334)
2023-06-22 [email protected] Roll Flutter Engine from 08aaa88bf67f to c6251a69a09a (10 revisions) (flutter/flutter#129331)
2023-06-22 [email protected] Manual roll of packages to 9af50d4 (flutter/flutter#129328)
2023-06-21 [email protected] Roll Flutter Engine from 090fae83548a to 08aaa88bf67f (3 revisions) (flutter/flutter#129306)
2023-06-21 [email protected] [framework,web] add FlutterTimeline and semantics benchmarks that use it (flutter/flutter#128366)
2023-06-21 [email protected] Roll pub packages (flutter/flutter#128966)
2023-06-21 [email protected] Remove incorrect non-nullable assumption from `ShapeDecoration.lerp` (flutter/flutter#129298)
2023-06-21 [email protected] Gracefully handle negative position in getWordAtOffset (flutter/flutter#128464)
2023-06-21 [email protected] [flutter_tools] add a gradle error handler for could not open cache directory (flutter/flutter#129222)
2023-06-21 [email protected] Roll Flutter Engine from f973fb4636d3 to 090fae83548a (5 revisions) (flutter/flutter#129293)
2023-06-21 [email protected] Selection area right click behavior should match native (flutter/flutter#128224)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 16, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) a: text input Entering text in a text field or keyboard related problems autosubmit Merge PR when tree becomes green via auto submit App c: contributor-productivity Team-specific productivity, code health, technical debt. f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants