[WIP][Perf] Reduce BindableObject allocations (event args caching, tree propagation, lazy init) - #34132
Closed
simonrozsival wants to merge 10 commits into
Closed
[WIP][Perf] Reduce BindableObject allocations (event args caching, tree propagation, lazy init)#34132simonrozsival wants to merge 10 commits into
simonrozsival wants to merge 10 commits into
Conversation
Introduce EventTrigger.Create<T>() factory methods that use static lambdas instead of reflection-based event subscription. The XAML SourceGen now emits calls to these factory methods for AOT compatibility. Architecture: - Single EventTrigger class with internal strategy pattern - ReflectionStrategy for backward compat (annotated with RUC/RDC) - StaticStrategy<T> for AOT-safe event subscription - No new public types - strategies are private nested classes Backward compatibility: - Existing new EventTrigger() constructor still works - EventTrigger.Event property is still set by SourceGen - All existing XAML works unchanged Fixes #33591
The SourceGen was generating both 'new EventTrigger()' and 'EventTrigger.Create<T>()' for the same EventTrigger, resulting in dead code. This change defers EventTrigger creation from CreateValuesVisitor to SetPropertiesVisitor when the Event property is present, allowing the factory method to be used directly. Changes: - EventTriggerValueProvider now marks EventTrigger nodes for deferred creation instead of skipping them entirely - CreateValuesVisitor registers variable names for deferred EventTriggers without emitting creation code - SetEventTriggerEvent emits the full variable declaration with the factory call - Added DeferredEventTriggers set to SourceGenContext to track nodes that need deferred creation - Updated Maui33591SourceGenTests snapshot to verify clean output
Move EventTrigger declaration to CreateValuesVisitor to fix variable ordering. EventTrigger children (TriggerActions) need the variable declared before they're processed in SetPropertiesVisitor (which uses bottom-up order). Key changes: - CreateValuesVisitor: Emit EventTrigger.Create<T>() call directly by walking up the XAML tree to find the target type - EventTriggerValueProvider: Simplified to just return existing variable name - EventTrigger.Create: Added eventName parameter for cleaner one-line generation - Updated snapshot test to match new output
Consolidate EventTrigger-specific code in one place: - Add EmitDeclaration static method with all generation logic - Add FindTargetType helper method - CreateValuesVisitor now just calls EmitDeclaration
- CreateValuesVisitor registers the variable name - GenerateCreateInstanceCall generates the Create<T>() or new EventTrigger() call - Cleaner separation of concerns
CreateValuesVisitor: writer.Write($"var {varName} = ");
EventTriggerValueProvider: writer.WriteLine("EventTrigger.Create<...>(...);")
Fixes #34093 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes #34129 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…bleProperty Fixes #34092 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes #34131 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Fixes #34092
Fixes #34093
Fixes #34129
Fixes #34131
Description
Four targeted allocation reductions in the BindableObject property system — all on hot paths that fire during page load, BindingContext propagation, and layout.
1. Cache PropertyChangedEventArgs / PropertyChangingEventArgs (#34092)
Every
SetValuecall allocated newPropertyChangedEventArgsandPropertyChangingEventArgs. SinceBindableProperty.PropertyNameis immutable, these are now cached on theBindablePropertyinstance via lazy??=fields.Files:
BindableProperty.cs,BindableObject.cs2. Reuse ElementEventArgs in tree propagation (#34093)
OnDescendantAdded/OnDescendantRemovedallocated a newElementEventArgsat every tree level during parent propagation. Now creates the args once and passes the same instance up the tree.File:
Element.cs3. Reduce inherited BindingContext allocations (#34129)
Two fixes:
WeakReference:SetInheritedBindingContextnow updates_inheritedContext.Targetinstead of allocating a newWeakReferenceper descendant..ToArray()inApplyBindings: Replaced the snapshot array allocation with direct dictionary iteration.File:
BindableObject.cs4. Lazy-initialize
_triggerSpecificitydictionary (#34131)The
Dictionary<TriggerBase, SetterSpecificity>was eagerly allocated on everyBindableObject, but <5% of objects use triggers. Now lazy-initialized on first use.Files:
BindableObject.cs,TriggerBase.csChanges
BindableObject.csBindableProperty.csElement.csTriggerBase.cs