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

[WinUI] Creating a complex grid is very slow #21787

Open
MartyIX opened this issue Apr 11, 2024 · 6 comments
Open

[WinUI] Creating a complex grid is very slow #21787

MartyIX opened this issue Apr 11, 2024 · 6 comments
Labels
area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter layout-grid platform/windows 🪟 t/bug Something isn't working t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.)
Milestone

Comments

@MartyIX
Copy link
Contributor

MartyIX commented Apr 11, 2024

Description

I have a non-trivial grid which I create by hand (not in XAML but by code) and it can easily take hundreds of milliseconds to show such a grid.

Steps to Reproduce

  1. Fetch https://github.com/MartyIX/maui/tree/feature/2024-04-11-grid-perf (MartyIX@dbb147d)
  2. cd src/Controls/samples/Controls.Sample.Sandbox
  3. dotnet publish -f net8.0-windows10.0.19041.0 -c Release -p:PublishReadyToRun=false -p:WindowsPackageType=None
  4. dotnet trace collect --format speedscope -- .\bin\Release\net8.0-windows10.0.19041.0\win10-x64\publish\Maui.Controls.Sample.Sandbox.exe
  5. Click "Generate Grid" to generate a 30x30 grid and see how long it took.

It can take 900 ms, and then it speeds up after a several clicks to about 600 ms but it does not drop under that bound.

image

Speedscope

Maui.Controls.Sample.Sandbox.exe_20240411_223826.speedscope.json

image

From the data, it's clear that

public static void AddWithSpan(this Grid grid, IView view, int row = 0, int column = 0, int rowSpan = 1, int columnSpan = 1)
takes a lot of time but it's very much unclear to me how to speed it up because it seems there is no batching support in MAUI right now.

Link to public reproduction project repository

https://github.com/MartyIX/maui/tree/feature/2024-04-11-grid-perf

Version with bug

8.0.20 SR4

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

Windows, I was not able test on other platforms

Affected platform versions

Windows 10, Windows 11

Did you find any workaround?

No.

Relevant log output

No response

@MartyIX MartyIX added t/bug Something isn't working platform/windows 🪟 layout-grid legacy-area-perf Startup / Runtime performance labels Apr 11, 2024
@daltzctr
Copy link
Contributor

daltzctr commented Apr 11, 2024

I reproduced this in release mode with the following code. The root page is just a ContentPage in a blank MAUI project.

private void Button_Clicked(System.Object sender, System.EventArgs e)
{
    List<Label> labels = new List<Label>();

    const int numRows = 30;
    const int numCols = 30;

    Stopwatch watcher = new Stopwatch();

    RowDefinitionCollection rowDefinitions = new RowDefinitionCollection();
    ColumnDefinitionCollection columnDefinitions = new ColumnDefinitionCollection();

    /* Configure sizing */
    for (int i = 0; i < numCols; i++)
    {
        rowDefinitions.Add(new RowDefinition(50));
        columnDefinitions.Add(new ColumnDefinition(50));
    }

    /* Add labels */
    for (int i = 0; i < numCols; i++)
    {
        for (int x = 0; x < numRows; x++)
        {
            labels.Add(new Label()
            {
                Text = $"Label {i}:{x}"
            });
        }
    }
    
    content.RowDefinitions = rowDefinitions;
    content.ColumnDefinitions = columnDefinitions;

    int z = 0;
    watcher.Start();
    /* Add content */
    for (int i = 0; i < numCols; i++)
    {
        for (int x = 0; x< numRows; x++)
        {
            content.Add(labels[z], i, x);

            z++;
        }
    }
    watcher.Stop();

    debugTxt.Text = $"Time Taken: {watcher.Elapsed.TotalMilliseconds}ms";
}

@MartyIX
Copy link
Contributor Author

MartyIX commented Apr 12, 2024

Master

Avg time to create a grid was: 920 ms

image

Branch with WindowsBatchPropertyMapper

Avg time to create a grid was: 694 ms (25% improvement)

https://github.com/MartyIX/maui/tree/feature/2024-04-12-grid-perf-full containing two commits

image

@jsuarezruiz jsuarezruiz added this to the Backlog milestone Apr 15, 2024
jonathanpeppers pushed a commit that referenced this issue Apr 19, 2024
The PR trivially attempts to decrease the number of interops, using a local variable.

Contributes to #21787
rmarinho added a commit that referenced this issue May 6, 2024
* [iOS] Fix crash closing Popup with WebView (#21718)

* Added repro sample

* Fix the issue

* Added UI Test

* Updated csproj

* More changes

* Removed sample and test

* More changes

* Removed unnecesary changes

* Update dependencies from https://github.com/dotnet/xharness build 20240408.1 (#21839)

Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit
 From Version 9.0.0-prerelease.24203.1 -> To Version 9.0.0-prerelease.24208.1

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>

* [Android] Fix crash navigating back (#20420)

* Fix the issue

* Added device test

* [XC] Fix x:DataType resolution for BindingContext (#21454)

* Add test

* Add special case for resolving x:DataType for the  binding context property

* [X] Fix TargetType simplification bug (#21764)

* Add unit test

* Simplify TargetType=x:Type before setting resources

* fix: Use AppContext.BaseDirectory instead of Environment.CurrentDirectory (#21797)

#21750
---------
Co-authored-by: Eilon Lipton <[email protected]>
Co-authored-by: Jonathan Peppers <[email protected]>

* Remove 2nd webview2 used to add `base` tag to passed html

* Revert script to OG version

* [ios/catalyst] fix memory leak with CollectionView (#21850)

Fixes: #20710
Context: https://github.com/marco-skizza/MauiCollectionView

Testing the sample above, you can see a memory leak when setting up a
`CollectionView`'s `DataTemplate` in XAML, but it appears to work just
fine with C#.

Using `dotnet-gcdump` with a `Release` build of the app, I see:

![image](https://github.com/dotnet/maui/assets/840039/6b4b8682-2989-4108-8726-daf46da146e4)

You can cause the C# version to leak, if you make the lambda an instance
method:

* jonathanpeppers/iOSMauiCollectionView@3e5fb02

XAML just *happens* to use an instance method, no matter if XamlC is on/off.

I was able to reproduce this in `CollectionViewTests.iOS.cs` by making the
`CollectionView` look like a "user control" and create an instance method.

There is a "cycle" that causes the problem here:

* `Microsoft.Maui.Controls.Handlers.Items.VerticalCell` (note this is a `UICollectionViewCell`) ->
* `DataTemplate` ->
* `Func<object>` ->
* `PageXamlLeak` (or `PageCsOk` w/ my change) ->
* `CollectionView` ->
* `Microsoft.Maui.Controls.Handlers.Items.VerticalCell`

The solution being to make `TemplatedCell` (which is a subclass of `VerticalCell`)
hold only a `WeakReference` to the `DataTemplate`.

With this change in place, the test passes.

~~ Notes about Catalyst ~~

1. The new test passes on Catalyst (with the `#if` removed), if you run it by itself
2. If you run *all* the tests, it seems like there is a `Window` -> `Page` -> `CollectionView` that makes the test fail.
3. This seems like it's all related to the test setup, and not a bug.

It seems like what is here might be OK for now?

If Catalyst leaks, it would probably leak on iOS as well and the test passes there.

* [tests] add `SourceGen.UnitTests` to CI (#21889)

Context: #21725

In #21725 we noticed the source generator unit tests (for `*.xaml.g.cs`) don't run on CI.

Whoops!

* [C] Propagate resources when reparenting (#21879)

* [C] Propagate resources when reparenting

- fixes a bug when the theme is changed while the control/page isn't
  parented. Should fix @LeDahu22 reported issue of #21744

* move logic to resourcesextensions

* [iOS] Fix crash closing Popup with WebView  (#21923)

* [iOS] Fix crash closing Popup with WebView (#21718)

* Added repro sample

* Fix the issue

* Added UI Test

* Updated csproj

* More changes

* Removed sample and test

* More changes

* Removed unnecesary changes

* Added UITest

* Update Issue21846.xaml.cs

* Update Issue21846Modal.xaml.cs

* Update Issue21846.cs

---------

Co-authored-by: Javier Suárez <[email protected]>

* Update locker.yml (#21894)

* Bump the Locker action version
Refer to microsoft/vscode-github-triage-actions#210

* Restrict the Locker action to dotnet org

* do not scroll if the view is in the navbar (#21806)

* [windows] less interops in calling `get_Children` (#21792)

The PR trivially attempts to decrease the number of interops, using a local variable.

Contributes to #21787

* [controls] fix leak in ImageSource, caused by Task that never completes (#21928)

Context: https://github.com/AdamEssenmacher/MemoryToolkit.Maui/tree/main/samples

Testing the above sample, I saw some odd results in a `.gcdump`
related to `UriImageSource` inside a `CollectionView`:

    AsyncTaskMethodBuilder+AsyncStateMachineBox<VoidTaskResult, Microsoft.Maui.Controls.ImageElement+<CancelOldValue>d__10>, Count: 182
        Dictionary+Entry<Int32, Task>[], Count: 182
            Dictionary<Int32, Task> [Static variable Task.s_currentActiveTasks], Count: 1

It appears that we `await` a `Task` that never completes, which is:

https://github.com/dotnet/maui/blob/8e4450cbc14932a6c74aeb8b7bfee9c94eca18b0/src/Controls/src/Core/ImageElement.cs#L129

The `Task` also holds onto the `ImageSource` and whatever memory it
happened to use. That would be ok if the `Task` completed.

I could reproduce the problem in a test:

    [Fact]
    public async Task CancelCompletes()
    {
        var imageSource = new StreamImageSource
        {
            Stream = _ => Task.FromResult<Stream>(new MemoryStream())
        };
        await ((IStreamImageSource)imageSource).GetStreamAsync();
        await imageSource.Cancel(); // This should complete!
    }

This non-completing `Task` can occur when changing `Image.Source`,
which would certainly be bad while scrolling a `CollectionView`!

To fix, I refactored the code slightly and had the problematic case
return:

    return Task.FromResult(false);

* More IndexOf() optimization (#20083)

* Use better IndexOf where possible

* apply similar performance improvement to the predicate-based IndexOf()

* use built-in IndexOfChild on Android

* add braces as per new editorconfig rule

* use TryGetValue to avoid double lookup

---------

Co-authored-by: Edward Miller <[email protected]>

* Go back to ignoring ExpectingPageNotToBreak (#21940)

* [ios/catalyst] fix memory leak in gestures (#21887)

* [ios/catalyst] fix memory leak in gestures

Related: #21089
Context: jonathanpeppers/iOSMauiCollectionView@547b7fa

Doing something like this and running on iOS or Catalyst:

    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Label Text="{Binding Name}">
                <Label.GestureRecognizers>
                    <TapGestureRecognizer Command="{Binding BindingContext.Command}" />
                </Label.GestureRecognizers>
            </Label>
        </DataTemplate>
    </CollectionView.ItemTemplate>

Causes a memory leak.

I could reproduce this in a test that is a bit more elaborate than #21089,
showing issues for different types of gestures:

* Usage of `ShouldReceiveTouch` creates a cycle:

  * `GesturePlatformManager` -> `UIGestureRecognizer.ShouldReceiveTouch` -> `GesturePlatformManager`

  * We can move `ShouldReceiveTouch` to a "proxy" class, and avoid the cycle.

* `PanGestureRecognizer` has closures that cause the same cycle:

  * `this.PlatformView` -> `eventTracker?.PlatformView`

  * `panRecognizer` -> `((PanGestureRecognizer)panGestureRecognizer)`

  * These already had a `WeakReference` to use instead, but we could just make use of them.

There *may* be a general problem with `CollectionView` in how it doesn't
tear down `GesturePlatformManager` in the same way for children. But
in either case, the problems above are cycles that should be broken.

* Fix test on Windows

* Setup scaffolding for legacy test runner (#21423)

* Just added a couple of tests

* Focus on Android and iOS (for now)

* Added legacy tests to the pipeline

* More changes

* More changes

* Fixed build

* Created ui-tests-legacy-steps.yml

* More changes

* More changes

* Updated Appium to RC7

* Update ui-tests.yml

* Fixed test on Android

* Fixed test

* More changes

* More changes

* Updated snapshot

* More fixes

* Clean code

* Run test only on iOS

* Added pending constant

* Fix build error

* More changes

* Update ControlGallery.iOS.Appium.UITests.csproj

* Update ControlGallery.Shared.Appium.UITests.csproj

* Trying to run only on iOS

* This is the fix, I feel it

* Fix merge error

---------

Co-authored-by: Gerald Versluis <[email protected]>

* [WinUI] `GesturePlatformManager.Windows.cs` - unused variable (#21953)

* Code style

* Remove unused line

* IndexOf() stackOverflow fix (#20083) (#21965)

* Update EnumerableExtensions.cs

* Update EnumerableExtensions.cs

* Add visual test for webview2 on windows

* add EnumerableTests (#21978)

Co-authored-by: Edward Miller <[email protected]>

* Re-generate template localization (#21988)

* Add s/triaged label for issues opened by (core) team (#22006)

* Add s/triaged label for issues opened by (core) team

Alternate approach to #21775 as that clearly didn't work. Hoping this _will_ work!

From the original PR:

> Adds a rule to the bot that adds the s/triaged label to an issue which is opened by the core team (someone with write permissions on the repo). A lot of times these issues are tasks and/or very cryptic to the triage team. This way, the triage team will skip these issues and we assume the issues are valid ones and/or not issues that need triage.

* Update resourceManagement.yml

* Add latest .NET9 and .NET8 (#22034)

* [iOS] Add UITest for #21806  (#21951)

* Add UITest and use IsDescendant of ContainverVC

* Add button to focus entry in the navbar

* UITests working separately

* Use the modal stack

* [ios/catalyst] fix memory leaks in ListView (#22007)

* [ios/catalyst] fix memory leaks in ListView

Fixes: #20025

I could reproduce the leak here, pretty easily with changes to `MemoryTests`.

`ListView` has several cycles that prevent garbage collection from
happening:

* `FormsUITableViewController` has `ListView _list`
    * `FormsUITableViewController` -> `ListView` -> `ListViewRenderer` -> `FormsUITableViewController`

* `ListViewDataSource` has `UITableView _uiTableView`
    * `ListViewDataSource` -> `UITableView` -> `ListViewDataSource`

* `ListViewDataSource` has `FormsUITableViewController _uiTableViewController`
    * `ListViewDataSource` -> `FormsUITableViewController` -> `UITableView` -> `ListViewDataSource`

* `ListViewDataSource` has `protected ListView List`
    * `ListViewDataSource` -> `ListView` -> `ListViewRenderer` -> `FormsUITableViewController` -> `UITableView` -> `ListViewDataSource`

I changed the above fields to all be `WeakReference<T>` and the leaks
went away.

* Tentative fix for Android

Context: #18757

* Revert "Tentative fix for Android"

This reverts commit b294779.

* Ignore ListView test on Android for now

* [UI Testing] Split mouse and touch Appium actions and added pending ones (TouchAndHold etc) (#21305)

* Split mouse and touch Appium actions

* More changes

* Fixes

* Fix build errors

* More changes

* Fix build errors

* More updates

* [Windows] Use correct build version check (#22013)

* [Windows] Use correct build version check

Windows 11 version isn't actually 11.*, it's 10.0.21996

* Update Connectivity.uwp.cs

* Navigate Directly to Test (#22019)

* Navigate Directly To Test

* - add additional wait for element

* Fix main (#22065)

Remove duplicate Tap method

* [iOS] Implemented PrefersHomeIndicatorAutoHidden, PrefersStatusBarHidden and PreferredStatusBarUpdateAnimation Platform Specifics (#20069)

* iOS page specifics

* Added a sample (#19970)

* Code refactor

* Replaced public with internal

---------

Co-authored-by: dustin-wojciechowski <[email protected]>

* Reenabled ReturnsNonEmptyNativeBoundingBox tests on Windows (#20238)

* [iOS] Allow compat iOS buttons to resize image and to respect spacing and padding (#21759)

* Fix the iOS button to resize the image, respect padding, and respect spacing

* allow UITest - waiting for CI results

* Allow the image to actually take up the whole space and fix issue with buttons without images or titles resizing

* Add UITest screenshot

* remove changes to sandbox

* Added manual testing sample

* More changes in the sample

* Do not hide the text if it doesn't fit and resize if no title

* UITests cannot share the same name

---------

Co-authored-by: Javier Suárez <[email protected]>

* Fix null reference exception in KeyboardAutoManagerScroll when UIWindow is null (#21753)

* Fix null reference when window is null

When using certain controls ie bottom sheets and search bars there can be a crash when the keyboard displays. Check for null & return early if it is.

* set flag to false if returning

* remove change on line 1

* Add IsDescendant of ContainerVC

* remove the extra IsKeyboardAutoScrollHandling flag

* remove extra style changes

* Add UITest

* Use UIView AccessibilityIdentifier on UITest

* Add ignore in platform and remove catch in favor of rebasing main

---------

Co-authored-by: tj-devel709 <[email protected]>

* Fix screenshot for 18242 for new test behavior (#22081)

* [android] use Java primitive `boolean` for `UriImageSource` (#22040)

Context: https://cs.android.com/android/platform/superproject/main/+/main:libcore/ojluni/annotations/hiddenapi/java/lang/Boolean.java;l=109?q=boolean.java

Some internal APIs in MAUI were using `java.lang.Boolean` when they
could just use `boolean` instead.

Java `boolean` is a primitive type, while `java.lang.Boolean` is an
object (that can be null). This is similar to `bool` vs `bool?` in C#,
except Java does not have `struct`.

This avoids:

* JNI field lookup of `Boolean.TRUE` and `Boolean.FALSE` on startup

* JNI field reads of `Boolean.TRUE` and `Boolean.FALSE` on every
  `UriImageSource` creation

* Creating a C# instance of `Java.Lang.Boolean`, and the bookkeeping
  for reusing C# instances

I was also going to change `ImageLoaderCallback.onComplete`, but it is
listed as a public API.

This is a small improvement, but should help all `UriImageSource` on
Android.

* Fix (#22033)

* fix CA1864 (#22092)

Co-authored-by: Edward Miller <[email protected]>

* Improve error logging for failed resizetizering (#22064)

* Improve error logging for failed resizetizering

* Fix tests

* [UI Testing] Implement PressEnter method (#22112)

* Implement PressEnter method on appium

* IsKeyboardShown not implemented on Windows

* Updated test

* Fix TabbedPage title displaying incorrectly (#17039)

* Fix TabbedPage title displaying incorrectly

Presently, when a navigation page is created and a tabbed page is added to it with children, the navigation page uses the tabbed page's selected child title as its title. This behavior is unexpected when using standard tabs (tabs on the top) in a navigation page on Android.

Without a custom tabbed page, this behavior makes sense, especially for bottom tabs; however, when a custom tabbed page is defined, it seems that the title should not be overridden.

Fixes #8577

* Add device tests

* Add unit tests

* Updated test

* More tests

* Fixed test not being able to fail

* Changed GetTitle to work with more than just TabbedPages

* Removed tests that deal with Child page titles replacing the Toolbar title

---------

Co-authored-by: Javier Suárez <[email protected]>
Co-authored-by: Dustin Wojciechowski <[email protected]>
Co-authored-by: Dustin Wojciechowski <[email protected]>

* Update dependencies from https://github.com/dotnet/xharness build 20240424.1 (#22119)

Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit
 From Version 9.0.0-prerelease.24208.1 -> To Version 9.0.0-prerelease.24224.1

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>

* [Android] Crash removing item from CarouselView - fix (#22107)

* Fix #19786

* Added a Ui test (#19786)

* Update MauiCarouselRecyclerView.cs

* [iOS] Shell page title fix (#20575)

* [iOS] Shell page title fix (#20199)

* Added a UiTest (#20199)

* Added pending snapshot

* Shell page title fix

---------

Co-authored-by: Javier Suárez <[email protected]>

* Add VS Code Extension to move-to-vs-feedback label (#22152)

* Android mipmap/appicon failing with "APT2260" (#21838)

Context https://developercommunity.visualstudio.com/t/Android-mipmapappicon-failing-with-APT/10622494

Users are reporting this error quite allot. During
a build it fails with the following error

```
 APT2260 resource mipmap/appicon (aka xxx:mipmap/appicon) not found.
```

Further investigation this only appears to happen during a full build
of all the platforms. Specifying `-f net8.0-android` on the build
this does not seem to happen at all.

The problem is the build gets into a weird situation where the `mauiimage.stamp` file
is present but none of the generated images are. Considering that the
stamp file gets generated AFTER the images its kinda difficult to see
how this even occurs.

The work around for this is to write the list of generated image files to
`mauiimage.outputs`. We can then read this list back on subsequent builds
and use that list for the `Outputs` of the `ResizetizeImages` target.
This means the target will run if either the stamp file or any of the
expected images are not present.

* Create similarIssues.yml (#22170)

* Support manual triggering of similar issues (#22176)

---------

Co-authored-by: Javier Suárez <[email protected]>
Co-authored-by: dotnet-maestro[bot] <42748379+dotnet-maestro[bot]@users.noreply.github.com>
Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
Co-authored-by: Šimon Rozsíval <[email protected]>
Co-authored-by: Precious Nyasulu <[email protected]>
Co-authored-by: Eilon Lipton <[email protected]>
Co-authored-by: Jonathan Peppers <[email protected]>
Co-authored-by: Mike Corsaro <[email protected]>
Co-authored-by: Jonathan Peppers <[email protected]>
Co-authored-by: Stephane Delcroix <[email protected]>
Co-authored-by: Shane Neuville <[email protected]>
Co-authored-by: Igor Velikorossov <[email protected]>
Co-authored-by: TJ Lambert <[email protected]>
Co-authored-by: MartyIX <[email protected]>
Co-authored-by: Edward Miller <[email protected]>
Co-authored-by: Edward Miller <[email protected]>
Co-authored-by: Gerald Versluis <[email protected]>
Co-authored-by: Jakub Florkowski <[email protected]>
Co-authored-by: Matthew Leibowitz <[email protected]>
Co-authored-by: Mike Corsaro <[email protected]>
Co-authored-by: dustin-wojciechowski <[email protected]>
Co-authored-by: Axemasta <[email protected]>
Co-authored-by: tj-devel709 <[email protected]>
Co-authored-by: Adam Anderson <[email protected]>
Co-authored-by: Dustin Wojciechowski <[email protected]>
Co-authored-by: Dean Ellis <[email protected]>
Co-authored-by: Craig Loewen <[email protected]>
@Eilon Eilon added t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.) area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter and removed legacy-area-perf Startup / Runtime performance labels May 10, 2024
@MartyIX
Copy link
Contributor Author

MartyIX commented May 28, 2024

With

merged, it appears that about 300 - 400 ms out of original 900 ms were shared off based on speedscope in #22650 (comment). One should take these numbers with a grain of salt though. However, for 7 weeks of medium effort, I guess it's not bad.

Anyway, I would consider 100 ms to be more or less OK but 500 ms is still way too much. Not sure if we can get there. Let's hope so. Ideas are welcome.

Edit: my approach for the time being is to do only local optimizations.

Foda added a commit that referenced this issue Jun 5, 2024
…s` (#22781)

### Description of Change

The idea of the PR is simple. There two sets of pointer event
subscriptions:


https://github.com/dotnet/maui/blob/fcabff1fe3e551e22558cba74dd3a86e00323b69/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs#L792-L796

and


https://github.com/dotnet/maui/blob/fcabff1fe3e551e22558cba74dd3a86e00323b69/src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Windows.cs#L824-L827

Consequently, `_container.PointerPressed`, `_container.PointerExited`,
and `_container.PointerReleased` are subscribed twice. And this PR
removes these subscriptions.

 

### Performance impact

* MAIN:
[Maui.Controls.Sample.Sandbox.exe_20240601_222830.speedscope.MAIN.json](https://github.com/user-attachments/files/15523262/Maui.Controls.Sample.Sandbox.exe_20240601_222830.speedscope.MAIN.json)
* PR:
[Maui.Controls.Sample.Sandbox.exe_20240601_223130.speedscope.PR.json](https://github.com/user-attachments/files/15523263/Maui.Controls.Sample.Sandbox.exe_20240601_223130.speedscope.PR.json)


![image](https://github.com/dotnet/maui/assets/203266/fd77fd77-9c62-414f-b7a6-d9ceec6b3621)

-> ~25% improvement

### Issues Fixed

Contributes to #21787
@MartyIX
Copy link
Contributor Author

MartyIX commented Jul 10, 2024

I ported my grid demo from MAUI to a WinUI app (https://github.com/MartyIX/WinUiDemos/tree/perf/2024-07-10-Grid). The app just adds 30x30 (=900) TextBlocks to a StackLayout (so no grid structure) and it takes about ~100 ms to complete that operation.

It's not a grid as in this issue. However, it just shows a lower bound which should be more or less a target.

Right now, my MAUI use case with grid takes about 350 ms. We are very likely 3-4 times slower than what WinUI can offer right now (even though MAUI does more work because it creates a grid and the WinUI app just vertical layout of labels). So the comparison is not fair but still the target should be about 100 ms IMO. That would be perfectly fine performance.

With #23515, I can sometimes observe 250 ms to display a single grid and each grid appears to require 50 ms less time to display than with the MAUI main branch. So that PR should offer big time savings for complex apps (hundreds of milliseconds).

@baaaaif
Copy link

baaaaif commented Aug 28, 2024

@MartyIX
I just want to say THANK YOU!
Your contributions have made our app super fast.
Actually, Microsoft should set up a MAUI bonus program and pay it out directly to you. You would have more than earned it @davidortinau.

I'd love to see your improvements for iOS and Android too

@MartyIX
Copy link
Contributor Author

MartyIX commented Aug 28, 2024

I appreciate the kind words.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter layout-grid platform/windows 🪟 t/bug Something isn't working t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.)
Projects
None yet
Development

No branches or pull requests

5 participants