question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Window closing time increases quadratically with the number of controls

See original GitHub issue

Describe the bug If a Window contains many controls, closing it can take a very long time. Specifically, the time between the Window.Closing event and the Window.Closed event increases quadratically with the number of controls in the window; for a window with 10000 controls, it takes about 30 seconds. It also appears that there are a lot of memory allocations and de-allocations that happen between the two events.

To Reproduce Create a new project with an empty window, and replace the constructor code with the following:

public MainWindow()
{
    InitializeComponent();

    StackPanel panel = new StackPanel();

    for (int i = 0; i < 10000; i++)
    {
        Button btn = new Button() { Content = "Test" };
        panel.Children.Add(btn);
    }

    this.Content = panel;

    Stopwatch sw = new Stopwatch();

    this.Opened += (s, e) =>
    {
        sw.Stop();
        Debug.WriteLine(sw.ElapsedMilliseconds);
    };

    this.Closing += (s, e) =>
    {
        sw.Restart();
    };

    this.Closed += (s, e) =>
    {
        sw.Stop();
        Debug.WriteLine(sw.ElapsedMilliseconds);
    };

    sw.Start();
}

Then change the max value in the for loop, and see how the opening time (i.e. the interval between the end of the constructor and the invocation of Window.Opened) and the closing time (i.e. the interval between Window.Closing and Window.Closed) increase.

Expected behavior Closing a window should be a relatively quick process; in any case, the time it takes should increase linearly with the number of controls in the window. Indeed, the opening time increases linearly as expected (see plots below).

Screenshots

image

Opening time (in milliseconds, Y axis) in function of the number of buttons in the window (X axis). It appears to increase linearly. Blue is a debug build with the CPU profiler activated (which is useless, most of the time is spent in “external code”); orange is a debug build without the CPU profiler; grey is a Release build run without the debugger. Dots are the measured times (median of 3 samples), the dashed line is the linear interpolation.

image

Closing time (in milliseconds, Y axis) in function of the number of buttons in the window (X axis). It appears to increase quadratically.

image

Screenshot of the diagnostic tools. Note the high number of memory allocations and garbage collections between Window.Closing and Window.Closed. The CPU plot might be slightly misleading because my machine has 36 logical processors, the amount you see here corresponds to about 100% of one processor.

Desktop (please complete the following information):

  • OS: tried only on Windows 10
  • Version: seems about the same on 0.9.12, 0.10.3 and 0.10.7

Additional context The results are similar if I manually detach the controls in the Window.Closing event by setting this.Content = null; or by doing something like while (panel.Children.Count > 0) { panel.Children.RemoveAt(0); }.

Using the DeferredRenderer or the ImmediateRenderer does not make a difference. Also, replacing the StackPanel with a Grid or a Canvas makes no difference.

Setting panel.IsVisible = false before the window is shown improves both the opening and closing time (with 10000 buttons, opening time goes down from 5.6 seconds to 160 ms and closing time goes from 28.7 seconds to 4.3 seconds).

However, if I then set panel.IsVisible = true after the window has been shown, this action blocks the UI for about the same time as the opening time.

If I set panel.IsVisible = false after the window has been shown (before closing it), this works instantly. However, closing the window still takes the “normal” amount of time (i.e. ~30 seconds).

The quadratic time might be explained if “something” happened every time a child is removed from the panel, which causes the panel to go through every other child it still has. However, I tried to override all the overridable methods and I couldn’t find anything that gets called multiple times.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
kekekekscommented, Oct 1, 2021

Observed quadratic complexity seems to be caused by WeakEventHandlerManager.Remove calling Compact which traverses the entire handler list. I guess we should only do that on actual events rather than on each Add/Remove or at least schedule compacting to be done asynchronously.

0reactions
kekekekscommented, Jan 16, 2022

Not fixed since Visual.cs is actually unchanged.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Week 10: Regression discontinuity designs
The key feature of RDD is that there is a continuous variable Xi that determines who gets treatment, denoted by Di (1 if...
Read more >
Window function
In signal processing and statistics, a window function is a mathematical function that is zero-valued outside of some chosen interval, normally symmetric ...
Read more >
Linear-Quadratic Regulator (LQR) design - MATLAB lqr
In the closed-loop step response plot, the rise time, settling time, and steady-state error meet the design goals. Input Arguments. collapse all. sys ......
Read more >
The linear quadratic model: usage, interpretation and ...
It can be seen that this leads to increases in cell survival by reducing the magnitude of the quadratic contribution to cell killing...
Read more >
Quadratic squeezing: An overview
A squeezed state, squeezed in the X sub 1 direction, has the property that delta X sub 1 is less than 1/2. A...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found