microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.36k stars 678 forks source link

Question: Why is WinUI so slow? #4597

Closed Noemata closed 1 year ago

Noemata commented 3 years ago

Based on the tests done here: https://github.com/Noemata/XamlBenchmark

WinUI performance is extremely slow? Why?

huoyaoyuan commented 3 years ago

It should be because drawing in C# is doing interop every time. It is slower than pure managed implementation. Honestly, this isn't the designed usage. I didn't do a serious benchmark, but when resizing a window with complex xaml, WinUI is fast.

Noemata commented 3 years ago

@huoyaoyuan , I completely disagree. The performance issues have nothing to do with interop. If WPF outperforms WinUI by a significant margin, there is a big problem given WPF has a very similar interop model.

It's reasonable to expect WinUI to be slower at present because the UWP compositing layer hasn't yet been hoisted out of the OS. It's also likely there are several other optimizations missing at this stage. Overall, progress with WinUI has been OK. I'm more concerned with Microsoft's posture which has consistently challenged the assertions of outsiders, performance being just one example.

In the last two community calls, Microsoft staffers questioned the prospect of performance issues in WinUI. This is either a problematic lack of knowledge, or a serious level of disingenuousness. Neither is acceptable given Microsoft is completely in the drivers seat until the project goes Open Source.

I seriously doubt Microsoft is unaware of the performance issues with WinUI. They have some of the best profiling tech in the industry. So what's the real deal?

UWP can't become another Silverlight fiasco. Such a result would be too costly for all of us, Microsoft included.

maxkatz6 commented 3 years ago

given WPF has a very similar interop model

WPF works in managed world with rendering-layer interop (DirectX). There also might be some optimizations to minimize calls to the interop code. WinUI is completely unmanaged, and you need to use interop for anything. CsWinRT can optimize it as much as possible, but it unlikely be as fast as managed only or unmanaged only code.

It's reasonable to expect WinUI to be slower at present because the UWP compositing layer hasn't yet been hoisted out of the OS.

It's reasonable to expect WinUI to be slower because of missing AOT compilation, that was the biggest reason of good enough performance in UWP. .NET Native is discontinued and alternative in .NET world wasn't released yet (NativeAOT probably will be one). .NET compilation side is not controlled by WinUI team.

I wonder if you actually can try to use WinUI 3 with UWP and .NET Native enabled. It might show interesting results, probably even comparable with UWP results.

Noemata commented 3 years ago

I've added a results section to the repo: https://github.com/Noemata/XamlBenchmark

It really doesn't matter how anyone tries to spin these results. It won't fly for WinUI in its current "Preview" state.

@maxkatz6 , I respectfully disagree. Especially the "completely unmanaged" part of your statement???

Any decent rendering system does batching and pruning. Even a purely managed graphics layer can be made to render complex graphics with great speed. Just look at the pre IL2CPP versions of Unity for what's possible. The DirectX rendering layer is asynchronous to the C# side, so you can pull all kinds of batching tricks there.

You can even monitor the display list independent of the app code and tune for specific types of changes during the render pass.

StephenLPeters commented 3 years ago

@bartekk8 and @Austin-Lamb FYI

Noemata commented 3 years ago

@StephenLPeters , @bartekk8 , @Austin-Lamb , any word on this? Performance hasn't changed with the GA release of WinUI. There is at least a 3x performance gap between WPF and UWP at present. Would be nice to get some sort of statement since this question has been asked in the community calls over the last couple months without any sort of reply.

Is the final release of WinUI expected to be roughly on par with UWP? Or will the final version of WinUI be 2x or more slower? Regardless of other capability/compatibility considerations, the memory and performance footprint will ultimately be major adoption considerations.

asierpn commented 3 years ago

We are also very concerned about performance issues of UWP, we are waiting for a long time the final realease of WinUI 3 hoping that these issues will be solved, but reading this it is not clear that it will happen. We have important performance issues in our LOB UWP app and migrating the app to WPF is a huge work that we can not assume.

There is a lot of feedback about these issues reported, here are a few samples: https://github.com/microsoft/microsoft-ui-xaml/issues/1517 https://github.com/microsoft/microsoft-ui-xaml/issues/1633 https://github.com/microsoft/microsoft-ui-xaml/issues/2028 https://github.com/microsoft/microsoft-ui-xaml/issues/2032 https://github.com/microsoft/microsoft-ui-xaml/issues/2707 https://stackoverflow.com/questions/49831807/do-all-uwp-apps-leak-memory-when-navigating-pages

Noemata commented 3 years ago

@asierpn , it's looking like CsWinRT may be the achilles heel of WinUI. It seemed like a really bad idea from the get go. Hopefully, UWP won't get crippled because of the rejigging being done to accommodate WinUI at the OS level. WinUI is eating up a lot of resources that could have significantly moved UWP forward and it looks like hard design choices will soon need to be faced by Microsoft regarding WinUI.

It would have been far far easier to open up the Win32 API to UWP than the course Microsoft is on with WinUI. My two cents worth.

asierpn commented 3 years ago

@Noemata I am totally agree with you, UWP has more than 5 years at its shoulders and we have not seen any performance improvement in the last years, our last hope was WinUI 3, but at this moment we still don't see any improvement with the new library. There is a lot of good Microsoft employees working on this project now and they should be concerned about these issues and address them quickly, and if the solution to the performance issues requires to change completely the core of the UWP technology they should do it.

We need a way to migrate our current UWP apps to a performant framework without rewritting completly our apps.

Noemata commented 3 years ago

@asierpn , UWP is very performant. It's hard to beat, in fact. I looked at your issues. They all have nice resolutions under UWP. Unfortunately, there is a black art around some of this. I looked at your sample app. You can fix the issues there with this.NavigationCacheMode = NavigationCacheMode.Enabled;

The docs are confusing around this. I don't understand why the Required flag doesn't work. One further consideration. Debug and Release builds behave differently. Performance and memory use is very good for a UWP Release build.

asierpn commented 3 years ago

@Noemata We use this.NavigationCacheMode = NavigationCacheMode.Enabled but it doesn't help, try this repro project App1.zip, If you click the button in the right upper corner quickly and repeatedly you will freeze the app in a few seconds.

The same issue happens in Debug and Release (.NET Native).

I have reproduced the issue with the official Windows Community Toolkit Sample App navigating repeatedly to the Controls > DataGrid page, the memory used by the app grows and on my computer when it reaches 300 Mb it freezes.

Same happens with the XAML Controls Gallery app, I've opened every page several times, from start to the end, and the collection sample pages several times each one, after some time the UI starts freezing, very noticeable with the NavigationView, TabView or WebView sample pages, finally the app crashed.

Noemata commented 3 years ago

@asierpn , I'll have to apologize, there are a few issues here. There is indeed a memory expansion problem happening to navigation. I was looking at the wrong process when I was looking at memory use. I had a few too many things on the go at the time I was looking at this.

That said, I did not have the problem you are seeing, but I was being well behaved. To clarify, I did let your data pages complete loading. I assumed your async loader method was written as is for expediency, and not for correctness. If you let your data pages completely load before navigating away, you will discover that you can navigate thousands of times before running into a memory stress situation.

That said, there is indeed a leak. I've revised your sample a bit further to make the navigation part correct in terms of how it is consuming memory.

App1.zip

If you run this version as is, provided you let your data pages fully load, you will not experience a hang. However, if you rapidly start navigation around without letting your data pages fully load, the app hangs. The code for managing data loading is not correct. void async methods lead to all sorts of grief.

So there are two concerns with your sample. One surfaces a legitimate memory growth issue. The other is due to a coding flaw.

I didn't correct the latter in the sample I just provided. If you block navigation during the data load cycle, your core problem will go away. That's just one way to fix that issue. Microsoft needs to have a look at the other leaking memory problem.

Noemata commented 3 years ago

@asierpn , here's a more correct version of your sample:

App1.zip

I didn't bother with minimizing code duplication. The essential bit is the awaitable call during initialization.

bartekk8 commented 3 years ago

@Noemata How are you executing your benchmark? I tried running it (full-screen, without making any changes) with Reunion 0.5, release / x64 and results are WPF = 20.0s and WinUI = 29.1s. This is slower, but not to the degree you observed and documented in your project's description.

Noemata commented 3 years ago

@bartekk8 , are you sure you were running the WinUI project. From the sounds of it, you were comparing against the UWP project. Make sure you've selected the correct startup project. The UWP run cycle is about what you are getting. WinUI is way slower.

image

Noemata commented 3 years ago

@bartekk8 , please show the following screenshots:

WPF

wpf

UWP

UWP

WinUI

WinUI

asierpn commented 3 years ago

@Noemata Thanks for the updated sample, you are correct that the original sample was using the UI thread to load data and this is not optimal. Your workarround of disabling navigation during the page data loading can reduce the issue, and I will try it in our production app, but it worses the usability forcing user to wait if he wants to navigate to another page.

Unfortunately navigation issues is only a part of the problem, we also have issues staying on the same page and loading/unloading some complex XAML controls to keep a low number of items in our visual tree.

Noemata commented 3 years ago

@asierpn , you did uncover a bug. I used your sample to file the issue here: https://github.com/microsoft/microsoft-ui-xaml/issues/4713

Honestly, I thought this had been fixed.

As for navigation slow down, other than the initial hit on load, I think performance is snappy. I don't know what your application looks like, you can have a look here: https://github.com/Noemata/FakePOS

For ideas on how to minimize performance issues by leveraging the ShellView / ShellViewModel.

I stuff anything I want to be performant in my ShellViewModel, that way it's always there. You can also "fool" the user by using lazy load strategies giving the user only what they need to see.

Microsoft's LOB sample was a very good start. The changes made in FakePOS take some requisite design changes across the finish line. There's still room for improvement. I hacked through this somewhat quickly.

Noemata commented 3 years ago

@asierpn , fortunately I was in the mood for a good April's fools joke today.

Here it is twice over, only it's not funny in your case: https://github.com/microsoft/microsoft-ui-xaml/issues/4718 https://github.com/microsoft/microsoft-ui-xaml/issues/4713

bartekk8 commented 3 years ago

@Noemata on one machine, the results are as I mentioned before:

gh-wpf gh-winui

On my laptop, WinUI is worse, but still not as bad as your results:

gh-wpf2

gh-winui2

Let me dig into it to see why there's such a difference.

Noemata commented 3 years ago

@bartekk8 , interesting. I can't explain the difference. I'm surprised your WPF times are slower than what I'm getting (desktop), yet WinUI is much faster on your setup. Initially I was worried that my quick tweaks to the project might be the issue, but since you're running with my project files, that can't be the case. Your setup is somehow faster by double for WinUI, and about 10% slower for WPF.

Did I grab the Nuget packages too early perhaps, and there was a behind the scenes tweak?

Noemata commented 3 years ago

@bartekk8 , tried three different systems. Some with NVidia GPUs some with Intel. Very similar results to what I posted. The only difference, on my fastest system, UWP produced the fastest time compared to all others. I guess we need more people to chime in with their results to get a better picture.

For Microsoft staffers that may be looking, the posted results are from a Surface Book 2. So it's Microsoft all the way ...

Noemata commented 3 years ago

Mystery solved ... you are right @bartekk8 , I was running the tests from Visual Studio. Looks like being attached to VS is bad for WinUI.

To remove the GPU side of things, here are the results running from a 4K RDP session on a very fast CPU and network.

UWP 18737 WPF 24933 WinUI 33290

What can I say, I made a bonehead move. Helps to have the input of others. Thank you. Will revise my posted results.

Results running against actual hardware have been updated: https://github.com/Noemata/XamlBenchmark

Fortunately, UWP and WinUI had a proportionally similar performance dip when attached to VS, so WinUI is still 3 times slower ... compared to UWP.

bartekk8 commented 3 years ago

@Noemata I'm glad we figured it out. I started looking at why there is such a difference between UWP and WinUI and so far I opened this. Thanks for your help finding these scenarios!

Noemata commented 3 years ago

This issue has gotten a little overloaded, but that turned out to be a good thing. Because of @bartekk8's input, I noticed that Visual Studio 2019 contributes negatively to both performance and memory handling. It seems to contribute its own memory leak in certain scenarios.

For anyone arriving here, the TabView leak mentioned above does not exist when NavigationCacheMode="Enabled" provided you are running outside of VS2019, though it appears to be present when the flag is set to "Disabled". Would be helpful if others could have a look at the code below in case I got something wrong.

VS2019 also slows down navigation dramatically when running under debug. That's to be expected given how it wires into the app. Does anyone know how to disable this effect?

Here's a revised version of the TabView test code (run it outside of VS):

TabView1.zip

bartekk8 commented 3 years ago

@Noemata Regarding the VS issue, I think the reason you were seeing worse performance when debugger is attached to WinUI app is because of this code in NativeDrawLine method:

            try
            {
                element.StrokeDashArray = CurrentState.XamlDashArray;
            }
            catch (Exception exc)
            {
                Logger.Debug(exc);
            }

Throwing exception will cause a message to be written to Debug Output, which is very slow and will slow down execution of your benchmark (try commenting this code out). I also noticed a small improvement in execution time when I run it commented out in Release mode (exceptions are not free...).

Noemata commented 3 years ago

@bartekk8 , that is indeed the case. However, I did revise my benchmarks after running the series without VS in the mix. WinUI was still significantly slower than UWP. I was generous in stating it was 3 times slower. I excluded any benchmarks that were obviously affected by memory leaks. I also excluded benchmarks that highlighted compute related tasks, since UWP has a clear advantage there.

For a taste of what a real world experience might look like for a user, run some of these as a release build without VS in the mix: https://github.com/Noemata/RosettaNavigation

Because of memory leaks, these apps progressively slow down, first becoming unusable and eventually crashing. I realize I am throttling the apps, but that's the point. Under load, presently, WinUI breaks. Given it's a v 0.5 designation release, I would be Ok with such issues except that there has been posturing on the part of key Microsoft staffers like Kevin Gallo that the current release may be used in production. Perhaps if you have a 128 GB system with multiple CPUs and GPUs, like he has for development, you can run WinUI all day without crashing. In the real world, WinUI turns out to be far more fragile at present.

Obviously, a lot of these issues will be sorted out. I worry that Microsoft is being way too premature with some of its messaging. It's perfectly acceptable to have "prerelease" problems. If it's dubbed as a "production grade" release, expectations are legitimately very high. Microsoft Betas used to be much better than this. Some old school thinking needs to be brought back.

KenionX commented 3 years ago

Ony my machine I reach 8000 ms ~ on WPF and UWP WinUi takes 46000ms~

JamesLear92 commented 2 years ago

When not debugging, I'm getting ~11250ms for WPF, and ~18500ms for SKD 1.0 Preview 3. That's pretty sad to see considering that WPF was already so much slower than Winforms.

I also notice a very worrying quirk of WinUI. You can see the CPU on the UI thread is very high when moving your mouse quickly over the UI.

lukeblevins commented 2 years ago

When running this rather synthetic benchmark again, but updated to the Windows App SDK v0.8.6, the measured performance gap from UWP improves by ~20%. Keep it coming!

FYI @StephenLPeters

Mohsens22 commented 2 years ago

https://github.com/mohsens22/XamlBenchmark/

I have updated the projects to the latest .NET and WinUI version (also updating MAUI bits) and the performance is promising!

( UWP .NET Native) Elapsed: 10004 ms, Passes: 1200 ( WPF .NET 6 Ready to Run ) Elapsed: 10164 ms, Passes: 1200 ( WinUI 1.0 .NET 6 ) Elapsed: 13530 ms, Passes: 1200