- Notifications
You must be signed in to change notification settings - Fork 29.5k
[framework] dont allocate forgotten children set in profile/release mode #94911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| Also fyi @srawlins , the lint this is hitting seems a bit bogus. I wrote:
And I can satisfy the lint by switching it to
Which seems pointless (and worse IMO). |
| // [update] to remove the global key reservations of forgotten children. | ||
| final Set<Element> _debugForgottenChildrenWithGlobalKey = HashSet<Element>(); | ||
| // Initialze to null in profile/release mode to avoid allocating unused sets | ||
| final Set<Element>? _debugForgottenChildrenWithGlobalKey = kDebugMode ? HashSet<Element>() : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't this tree shaken away as a write only field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤷 is it write only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - it's assigned on creation and then never used again outside of asserts. @alexmarkov do you know why this would still show up in profile builds using --aot --tfa?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do those flags apply in profile mode? (I think so)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure it's this one here and not the one in _updateInheritence?
Looking at a release snapshot this is definitely shaken away. It looks like it is in the profile one as well if I'm reading the tooling output correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#94955 seems to substantially improve build times on Android Go. Running more benchmarks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like a good deal!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Digging a little more on this, late final does not end up letting the field get tree shaken, but your version here does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I think we'd probably want to eliminate both the field and initializer. WDUT about reopening this (and applying it to all the complex fields ?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sgtm
| I'll close this in favor of Dan's change, thanks all! |
| lockState(() { | ||
| _inactiveElements._unmountAll(); // this unregisters the GlobalKeys | ||
| }); | ||
| lockState(_inactiveElements._unmountAll); // this unregisters the GlobalKeys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated, can't help myself
| Open to ideas for testing, some thoughts: I could create a private test/release mode test combo that allowed me to assert these fields are null. but, the issue we don't actually care if these fields are null - just that they are eliminated along with their initializers in profile/release mode (and I don't particularly care how that happens). A more appropriate test might be to analyze a compiled application, but I don't think we have any tools to do that in the SDK currently? |
| Maybe a test that tracks the size of the |
| benchmarks are good but ideally it would just be a pass/fail signal |
| Yeah, the only thing I can think of is measuring the size of the |
| I could assert that the size was smaller than it was before this change. that would allow a bit of wiggle room though it would require some explanation in the test as to why it might have failed and how to debug. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a cool find!
| | ||
| // In Profile/Release mode this field is initialized to `null`. The Dart compiler can | ||
| // eliminate unused fields, but not their initializers. | ||
| final Set<Element>? _debugIllFatedElements = kDebugMode ? HashSet<Element>() : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Can we come up with some kind of a check to avoid re-introducing debug only fields that are not initialized to null in release mode? A lint is probably a little to specific?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to maintain it, I could write a regex test... :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a regex test is probably the best we can do right now. We should just add comments explaining what it does and why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started writing a regex for this last week and thoguht we'd probably have to do some kind of post filtering on it to avoid it flagging things that are trivial to initialize like bools or ints.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe analyzer package is a better way to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, ideally we would apply this to all fields with debug in the name. The analysis should be able to detect that something is initialized to a const value and not complain? (At least for the likely simple cases).
At some point we could/should extent the analysis to ensure that debug only fields are only used from asserts and other debugOnly methods to ensure they will actually be shaken out.
Having this opt-in for now is fine, too, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think super ideally the compiler would just eliminate both the field and the initializer and we could delete the test :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol, yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler would have to understand the difference between side effects you want and side effects you don't. But maybe this could be special cased.. It's just a weird perf cliff :\
You could imagine a type that actually does work in its initializer though, and users depend (perhaps unintentionally) on that work getting done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bugs @mraleph has filed related to this would be helpful
| analysis test added, PTAL |
| final List<File> files = await _gitFiles(workingDirectory); | ||
| for (final File file in files) { | ||
| if (path.basename(file.path).endsWith('_test.dart')) | ||
| if (path.basename(file.path).endsWith('_test.dart') || path.basename(file.path) == 'analyze.dart') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without this the check trips on analyze.dart itself if you've changed it
| } | ||
| | ||
| const String _kDebugOnlyAnnotation = '@_debugOnly'; | ||
| final RegExp _nullInitializedField = RegExp(r'kDebugMode \? [\w\<\> ,{}()]+ : null;'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wasn't too bad using a Regex
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
| | ||
| // In Profile/Release mode this field is initialized to `null`. The Dart compiler can | ||
| // eliminate unused fields, but not their initializers. | ||
| final Set<Element>? _debugIllFatedElements = kDebugMode ? HashSet<Element>() : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, ideally we would apply this to all fields with debug in the name. The analysis should be able to detect that something is initialized to a const value and not complain? (At least for the likely simple cases).
At some point we could/should extent the analysis to ensure that debug only fields are only used from asserts and other debugOnly methods to ensure they will actually be shaken out.
Having this opt-in for now is fine, too, though.
| Due to an infrastructure issue, we're missing transition_perf benchmark data for the range where this commit landed. Also in the same range, the device that the benchmark was run on was changed. To make a long story short, I'm interested in the impact if any that this change had on performance of our benchmarks. Does anyone have any numbers on that handy? |
| could always revert and reland 🤣 I think @dnfield is the one with the wonky android device that goes really slow and shows the exaggerated impact |
Running #94585, HashSet constructor shows up in the top 10 or so hot functions. Since this is unused in profile/release mode and private - just set to null.
@dnfield