Skip to content

Made Directionality forego dependency tracking for better performance.#102336

Merged
fluttergithubbot merged 6 commits into
flutter:masterfrom
gaaclarke:directionality-faster
Apr 25, 2022
Merged

Made Directionality forego dependency tracking for better performance.#102336
fluttergithubbot merged 6 commits into
flutter:masterfrom
gaaclarke:directionality-faster

Conversation

@gaaclarke

@gaaclarke gaaclarke commented Apr 21, 2022

Copy link
Copy Markdown
Member

Customer money is spending 20ms of startup time doing bookkeeping for Directionality This gets rid of that bookkeeping which makes updates faster, but Directionality should be changed infrequently. On some platforms change in text direction actually forces reloading the process anyways (iOS).

Local testing of this change to the increase stock_build_iteration 2.5% (21ms).

I tried to make this solution more general but there was apprehension to change the public API and it seemed like other Widgets didn't have the same draw as Directionality.

Examples of widgets using Directionality.of:

  • Semantics.getTextDirection
  • createLocalImageConfiguration
  • Transform.updateRenderObject
  • FittedBox.createRenderObject

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@flutter-dashboard

Copy link
Copy Markdown

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat (don't just cc him here, he won't see it! He's on Discord!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@flutter-dashboard flutter-dashboard Bot added the framework flutter/packages/flutter repository. See also f: labels. label Apr 21, 2022
Comment thread packages/flutter/lib/src/widgets/basic.dart Outdated
@gaaclarke
gaaclarke requested a review from jonahwilliams April 21, 2022 21:34
@gaaclarke

Copy link
Copy Markdown
Member Author

The widget test is legitimately broken. I'm going to take this PR out of review until I fixed it.

@gaaclarke
gaaclarke removed the request for review from jonahwilliams April 22, 2022 18:00
@gaaclarke
gaaclarke marked this pull request as draft April 22, 2022 18:00
@gaaclarke
gaaclarke force-pushed the directionality-faster branch 2 times, most recently from a0b5c0c to 7fb7ce9 Compare April 22, 2022 21:38
@gaaclarke
gaaclarke marked this pull request as ready for review April 22, 2022 22:14
@gaaclarke
gaaclarke requested a review from jonahwilliams April 22, 2022 22:14
@gaaclarke

Copy link
Copy Markdown
Member Author

Okay, I fixed the tests. I previously wasn't recursing into the children elements and I had to make updates get filtered based on the bookkeeping we are keeping lower in the tree to match existing behavior. I had to make one tiny addition to the Element api.

@jonahwilliams

Copy link
Copy Markdown
Contributor

Some questions I have about this approach - how well does this scale in the odd case where users override directionality? What is the cost of updating the widget tree if the directionality does change?

Finally, how does the performance improvement scale to less constrained devices? I'm assuming the number comes from a wembly?

@goderbauer

goderbauer commented Apr 25, 2022

Copy link
Copy Markdown
Member

Local testing of this change to the increase stock_build_iteration 2.5% (21ms).

What does this mean? Did it make the benchmark worse?

What is the expected performance benefit of this change?

Comment on lines 92 to 94

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could just be an assert?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I thought about it but I don't know what our policy is there. You probably know better. We can't guarantee that the assert would always get hit in debug build and not release build. If Directionality ends up being the only user of it it might not be a big deal. I was just erring on the side of caution.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would just go with the assert here for now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(oops, fixed it, good now)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: a future reader will probably appreciate a little more documentation on how this is different from the regular InheritedElement.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 1713 to 1720

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This override seems unnecessary?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I did this in order to convert a @protected method to a public one. I don't know if there is an easier way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh, right. Makes sense then :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For my own understanding: Is the optimization here that we omit the bookkeeping that originally happened in this method? (Adding the dependent to a map?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yep, that's correct. You can see that in the profile I linked in the internal bug.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe leave that as a comment here for future readers, who want to understand why we did this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done

@gaaclarke

Copy link
Copy Markdown
Member Author

Local testing of this change to the increase stock_build_iteration 2.5% (21ms).

What does this mean? Did it make the benchmark worse?

Nope, an increase in performance, a decrease in time. I wouldn't recommend a change that doesn't help haha.

What is the expected performance benefit of this change?

In the stock_build_iteration test, run locally on my iPhone SE, the build times have improved 2.5% (21ms). I think that benchmark is pretty representative of many of our users UIs. I have profiles of the startup time of customer: money on low end Android devices which show it taking up 20ms of startup time in this bookkeeping as well. Details can be found on b/229707977.

@goderbauer

Copy link
Copy Markdown
Member

I have profiles of the startup time of customer: money on low end Android devices which show it taking up 20ms of startup time in this bookkeeping as well. Details can be found on b/229707977.

Does this change translate to a 20ms faster startup time for customer:money then?

@jonahwilliams

Copy link
Copy Markdown
Contributor

Ahh Iphone SE, not bad

@jonahwilliams

Copy link
Copy Markdown
Contributor

Another follow up question: can we identify other inherited widgets that frequently don't change?

@gaaclarke

gaaclarke commented Apr 25, 2022

Copy link
Copy Markdown
Member Author

Some questions I have about this approach - how well does this scale in the odd case where users override directionality? What is the cost of updating the widget tree if the directionality does change?

We are switching our algorithm from:

  • insertion: average O(1) to O(1), worst case O(n) to O(1) (edit: well even better than O(1) since nothing is happening, is O(0) a thing? haha)
  • lookup: average O(1) to O(n), worst case O(n) to O(n)

Insertions are happening when we are building the widget tree, lookups happen when the widget decides its data has changed and needs to notify its dependencies.

So, the performance will be somewhere between where it was before the change and a hot reload. It won't be as costly as a hot reload because we are still filtering updates to widgets that actually depend on the widget. I think in practice Directionality is so ubiquitous that the delta won't be that much different.

Finally, how does the performance improvement scale to less constrained devices? I'm assuming the number comes from a wembly?

My local testing was done on a modern iPhone and the supporting evidence for the change was the profiles of low end android devices. I suspect this to be a win across the board.

@gaaclarke

Copy link
Copy Markdown
Member Author

Another follow up question: can we identify other inherited widgets that frequently don't change?

I think there is an opportunity to. The other widgets I identified as candidates are [MediaQuery] and [Theme]. I know less about those and how much they change. Also, making them UbiquitiousInheritedWidgets would mean making the code visible across multiple files and we had some reservations on making this a public API so I avoided it since 20ms was a big enough win.

@gaaclarke

Copy link
Copy Markdown
Member Author

I have profiles of the startup time of customer: money on low end Android devices which show it taking up 20ms of startup time in this bookkeeping as well. Details can be found on b/229707977.

Does this change translate to a 20ms faster startup time for customer:money then?

I didn't run the test for customer:money since it is a bit involved. I felt I had enough evidence in hand to know it would be beneficial and I'd get the exact results for them as part of their regular testing.

@jonahwilliams

Copy link
Copy Markdown
Contributor

MediaQuery can change a lot when things like the keyboard opens or screen resizes, I wouldn't risk it

@jonahwilliams jonahwilliams left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approach LGTM, leave it up to @goderbauer to review the details.

@goderbauer goderbauer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall this LGTM.

Please leave a little more breadcrumbs in the docs so a future reader has an easier time understanding what the optimization is and why we did it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe leave that as a comment here for future readers, who want to understand why we did this.

Comment on lines 92 to 94

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would just go with the assert here for now.

Comment on lines 1699 to 1701

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: nit formatting here should be:

Suggested change
final _TestInheritedElement ancestor = _TestInheritedElement(
const Directionality(
textDirection: TextDirection.ltr, child: Placeholder()));
final _TestInheritedElement ancestor = _TestInheritedElement(
const Directionality(
textDirection: TextDirection.ltr,
child: Placeholder(),
),
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This and the next nit are still unaddressed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: same

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done

@gaaclarke
gaaclarke requested a review from goderbauer April 25, 2022 18:59

@goderbauer goderbauer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM with two remaining nits

Comment on lines 1699 to 1701

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This and the next nit are still unaddressed.

engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 27, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 28, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/plugins that referenced this pull request Apr 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants