Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[controls] fix performance issue in {AppThemeBinding} (#14625)
* [controls] fix performance issue in {AppThemeBinding} Context: #12130 Context: https://github.com/angelru/CvSlowJittering Profiling a customer sample app, I noticed a lot of time spent in `{AppThemeBinding}` and `WeakEventManager` while scrolling: 2.08s (17%) microsoft.maui.controls!Microsoft.Maui.Controls.AppThemeBinding.Apply(object,Microsoft.Maui.Controls.BindableObject,Micr... 2.05s (16%) microsoft.maui.controls!Microsoft.Maui.Controls.AppThemeBinding.AttachEvents() 2.04s (16%) microsoft.maui!Microsoft.Maui.WeakEventManager.RemoveEventHandler(System.EventHandler`1<TEventArgs_REF>,string) 16% is a *lot* to notice while scrolling. Sometimes I've made improvements where I only shaved off 3% of the total time. What is going on here is: * Default `maui` template has lots of `{AppThemeBinding}` in the default `Styles.xaml`. This supports Light vs Dark theming. * `{AppThemeBinding}` subscribes to `Application.RequestedThemeChanged` * Making every MAUI view subscribe to this event -- potentially multiple times. * Subscribers are a `Dictionary<string, List<Subscriber>>`, where there is a dictionary lookup followed by a O(N) search for unsubscribe operations. I spent a little time investigating if we can make a faster `WeakEventManager`, in general: dotnet/runtime#61517 I did not immediately see a way to make "weak events" fast, but I did see a way to make this scenario fast. Before: * For any `{AppThemeBinding}`, it calls both: * `RequestedThemeChanged -= OnRequestedThemeChanged` O(N) time * `RequestedThemeChanged += OnRequestedThemeChanged` constant time * Where the `-=` is notably slower, due to possibly 100s of subscribers. After: * Create an `_attached` boolean, so we know know the "state" if it is attached or not. * New bindings only call `+=`, where `-=` will now only be called by `{AppThemeBinding}` in *rare* cases. * Most .NET MAUI apps do not "unapply" bindings, but `-=` would only be used in that case. After this change, the following method disappeared from `dotnet-trace` output completely: 2.05s (16%) microsoft.maui.controls!Microsoft.Maui.Controls.AppThemeBinding.AttachEvents() Meaning that `AppThemeBinding.AttachEvents()` is now so fast that 0% (basically no time) is spent inside this method. I also could notice a difference in general startup time of the sample app. An average of 10 runs on a Pixel 5: Before: Average(ms): 967.7 Std Err(ms): 4.62132737064436 Std Dev(ms): 14.6139203045133 After: Average(ms): 958.9 Std Err(ms): 3.22645316098034 Std Dev(ms): 10.2029407525478 So I could notice a ~10ms improvement to startup in this app, and scrolling seemed a bit better as well. Note that I don't think this completely solves #12130, as things still seem sluggish to me when scrolling. But it is a reasonable improvement to start with that benefits all .NET MAUI apps on all platforms. * PR feedback
- Loading branch information