Skip to content
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

[net7.0] [controls] fix performance issue in {AppThemeBinding} #15384

Merged
merged 2 commits into from
Jun 1, 2023

Commits on Jun 1, 2023

  1. [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.
    jonathanpeppers authored and github-actions committed Jun 1, 2023
    Configuration menu
    Copy the full SHA
    1bed004 View commit details
    Browse the repository at this point in the history
  2. PR feedback

    jonathanpeppers authored and github-actions committed Jun 1, 2023
    Configuration menu
    Copy the full SHA
    44000a8 View commit details
    Browse the repository at this point in the history