AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.76k stars 2.23k forks source link

Set Child Performance Issues #7366

Open RationalFragile opened 2 years ago

RationalFragile commented 2 years ago

Describe the bug Having 200 buttons in an empty Window detached then attached to a parent Border takes 100-200ms on the parent.Child=child.

To Reproduce Steps to reproduce the behavior: Put 200 buttons in a <Border Name="Parent"> and have something like this:

private Border? border = null;
private IControl? swap = null;

public void Swap() {
    if(border == null){
        border = this.FindControl<Border>("Parent");
    }
    if(swap == null) {
        swap = border.Child;
        border.Child = null;
        System.Console.WriteLine("removed");
    } else {
        border.Child = swap; // takes more than 100ms
        swap = null;
        System.Console.WriteLine("restored");
    }
}

Expected behavior Take less than 10ms.

It's completely unreasonable for it to take more than 100ms. On my app, as soon as I have 3 cards with 10 buttons (with one icons Path inside each), it starts to take more than 100ms to switch "tabs"... Plus, if it was instantiating the Control, then maybe that would be okay, but simply keeping the Control in memory and reattaching it later shouldn't take as much as instantiating it (which already takes too much).

Desktop (please complete the following information):

Additional context I tried debugging this issue using Avalonia's source code. I installed all .net versions used and followed the build documentation, but after finally making it build and run, the Visual Studio profiler simply stops as soon as it starts if I select any project other than "Sandbox" (which I'm using for the test). The profiler simply stops immediately without any errors as if Avalonia is exiting as soon as it detects instrumentation. Otherwise, without the source code, it only shows Avalonia.Controls.Decorator.Set_Child as the call that takes more than 100ms. And after spending more than 20 hours on this, I give up trying to fix it myself 😢

kekekeks commented 2 years ago

https://github.com/AvaloniaUI/Avalonia/issues/6660

Should be somewhat fixed by https://github.com/AvaloniaUI/Avalonia/pull/7301

RationalFragile commented 2 years ago

Thank you very much! I tested with 0.10.999-cibuild0017725-beta and it is a huge improvement; it's down to 70ms average exclusive time for the Set_Child. But I think it's still a serious issue.

For context, removing and reattaching 200 buttons in Chrome, (each button with text, a gradient background and a boxshadow) takes less than 1ms! 1 percentile max is like 2ms... I went as far as destroying all the buttons and recreating them and then reattaching them, and measured it a thousand time in a row, max per call is usually less than 8ms! average is less than 1ms!

So imho, 200 buttons should never cause a frame drop, let alone 4 (70ms). But again, thank you very much for the quick reply 😄

RationalFragile commented 2 years ago

If anyone is facing the same issue, you can try Control.IsVisible instead of removing and reattaching the control. This will work in cases like if you have a "history" control, you can make it a Grid and set IsVisibile on children that you wanna keep alive for fast switching without any skipped frames. But, you still can't pool controls (both because of the performance hit discussed here in this bug report, and because of #7381 ).