dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
21.83k stars 1.67k forks source link

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

Open MartyIX opened 2 months ago

MartyIX commented 2 months ago

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 (https://github.com/MartyIX/maui/commit/dbb147d1fd07520553b459e37404bdcf58be3194)
  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 https://github.com/dotnet/maui/blob/0a562dcd1de72db1d52426fdd89c29e02fb00f1b/src/Controls/src/Core/Layout/GridExtensions.cs#L60 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

daltzctr commented 2 months ago

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 commented 2 months ago

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

MartyIX commented 1 month ago

With

merged, it appears that about 300 - 400 ms out of original 900 ms were shared off based on speedscope in https://github.com/dotnet/maui/pull/22650#issue-2317270042. 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.