During the WinUI community call, there was a comment about improvements coming to the performance of XAML Rendering with WinUI 3.0
I thought I would start a discussion thread where people could collate performance quirks they have come up against, but for my own view, I would just like to ask if the team working on it, could write up some blog posts talking about what improvements are being made, along with the Alpha release, as well as how the process extracting the stack from the OS, enables ongoing improvements in the future.
This may be completely irrelevant, but retargeting the SDK takes forever for the XAML UI project, this may be a performance issue for VS itself though, not sure where it lies. I had my reasons for retargeting, I know it may sound really weird.
Here are my performance issues, when it comes to UWP:
Please don't use XAML ambiguously in discussions. "XAML Performance" could refer to the parsing of XAML files or it could refer to the performance of the WinUI framework. My guess is that this thread is about the latter.
On UWP performance, this is very good at the moment in release mode using .Net Native. I expect that with .Net 5 it will be similarly fast using AOT. I am concerned that in the meantime with .Net Core 3.0 it will become slow again. It would be great if UWP with .Net Core 3.0 would work with CoreRT which is very similar to .Net Native.
@charlesroddie Your comment is about the initial discussion, or my comment?
About the initial post and title, but applies to a lot of threads in this repo.
@jtorjo
Here are my performance issues, when it comes to UWP:
- uwp: compilation times are insanely slow! (pretty much, 6 times slower that WPF - i'm using target version build 1809 - build 17783)
I guess you are talking about Release build times right? The Release build is so slow because of .NET Native. It makes build times much slower, but gives you major improvements in startup times, XAML rendering (data binding) and memory usage. In Debug mode, apps should usually build pretty fast, so this does not really affect development cycles that much IMO. At least, I usually only build Release every now and then, so for me this is not a problem (I am a big fan of .NET Native performance gains).
- I know StorageFolder file enumeration API is not part of UWP, but that is insanely slow. The UWP sandbox (at least on Windows Desktop) is hurting more than helping.
I agree that StorageFolder/StorageFile API is a major painpoint for any file driven application. I don't believe that this is caused by sandboxing, it is probably more a problem of how the API is designed. Every instance contains these properties: Attributes, DateCreated, DisplayName, DisplayType. In the old FileInfo API, these would only be retrieved on-demand on first access. But in UWP all of these properties are always retrieved as soon as an instance is created, which makes the API so much slower when you enumerate large folders. It works well if you only access files every now and then (like, open one file through file dialog). But apps that work with a lot of files and folders will have major performance hits due to the bad API design.
UWP really needs a fast, light-weight file API that pretty much only wraps the file name (and maybe some file permission token). Then there should be an async method to create the "fat" StorageFile/StorageFolder instances from it, or open streams directly, on demand.
- async programming is fun, but in UWP, when an exception happens, it's somehow eaten by the UWP code, and then later break in debugger, with all the stack context lost. Sometimes, I do have the stack context inside the thrown exception itself, but not always.
I have seen things like this, but I don't think that async is really the cause of these problems. Normally, exceptions from async code are handled very well. The bigger problem might be that you usually don't see any call stack from native platform code, so if the platform calls back into user code and things fail there, you sometimes don't even know why or how things have failed, where in WPF you would always see a clear stack trace of how your code was invoked.
- Profiling UWP code is close to impossible. Event dotTrace instantly breaks when trying to do it.
Did you try with VS performance tools? They work very well for me.
@lukasf Thanks for replying!
I guess you are talking about Release build times right? The Release build is so slow because of .NET Native.
I'm talking about Debug times, without .Net Native. For me .Net Native generated a weird crash a while ago, and I haven't tested it since. My project in Debug x86 (it is slightly faster than Debug x64), usually takes 30-40 seconds to compile.
Once again, this is build 17783 - I have read on the internet that lower builds generate faster time - I think it's on your uservoice forum.
I agree that StorageFolder/StorageFile API is a major painpoint for any file driven application. ....UWP really needs a fast, light-weight file API that pretty much only wraps the file name (and maybe some file permission token).
I couldn't agree more. Or, much simpler maybe - just allow access to System.IO for all HDD ;) That would simplify things a lot.
My app displays photos/videos from user's folders, making it easy for him/her to select his favorites to edit them to create another video - in WPF, this fast simply insanely fast. In UWP, this is beyond slow.
... so if the platform calls back into user code and things fail there, you sometimes don't even know why or how things have failed, where in WPF you would always see a clear stack trace of how your code was invoked.
It is possible that this is the issue, but it does make debugging a huge pain...
Did you try with VS performance tools? They work very well for me.
I will definitely try them. I used dotTrace a low with WPF, and isolated errors insanely fast. Will definitely try the VS performance tools - just read an article about them, seems they should fit my needs.
In our case we are using UWP for a LOB app and we have found big performance issues in two common scenarios:
We need to put a lot of data in some views, so we use use datagrid inside pivot or tab controls, this generates big visual trees of more than 5000 items which are hard to reduce more. The first load of the visual tree or when you switch from one tab to another is not smooth.
Something similar happens with frame cache, we have it enabled so the state of our list views is keeped when you return from it's associated form, but if the page cached has a big visual tree the navigation when you go back is not smooth and hangs for a while. Moreover we notice a performance degradation when using the frame cache and you have navigated to a big number of views.
Things come worse in this scenarios if you don't have a powerfull machine or any other windows process is requiring CPU, most of our users are using i5 or i3 with 8Gb of RAM and the performance with this hardware is very bad, there is continuous hangs of several seconds on this environments
Here is a sample project which try to show this two issues:
App1.zip
@jtorjo
The problem is that FileInfo APIs do not work well with permissions and sandboxing. If you can just create an instance from a file path, the system cannot know if/how you got access to that file. So I think a new file API is required, but one that is optimized for performance. One that does not query lots of data, when all you need is the plain folder+file names.
If you want good file performance, there is one thing you should be aware of: Enumerating files usually involves lots of async calls. Every single "await" on the UI thread is very slow and will cost you about 10ms. This is just the "await", with no actual work being done. So for bigger file operations, it is highly recommended to not await every single async call on the UI thread. You can do that by adding ConfigureAwait(false) on every async method call, or by running the whole file IO stuff on a background thread. I use the latter, since it is easier to implement:
// read files on background thread and await result only once on UI thread
var files = await Task.Run(() => ScanFilesAsync());
// now work with files on UI thread
ShowFiles(files)
Now all the countless await calls inside ReadFilesAsync will not be awaited on the UI thread but on the background thread (which is very fast). Only the final result is awaited once on the UI thread, and then it can be used on UI tread and for data binding. Be careful to not access UI stuff in ScanFilesAsync().
The same concept is useful for other complex file or stream operations, like reading or writing stuff in a loop, where you have lots of await calls. Put that in a background task or use ConfigureAwait(false) and you will have much better performance, close to classic .NET APIs.
@lukasf Thanks for the advice. I wish that were true. That was also my idea - to just do it in another thread. But in my tests, the gain is close to 0%.
About a new file API is required: I just don't understand why MS won't simply allow System.IO over all HDD, once you've requested broadSystemAccess. It's clearly possible, since if I write a Desktop Bridge app, I can use System.IO all over the place, and it's fast as hell.
Best,
John
uwp: compilation times are insanely slow! (pretty much, 6 times slower that WPF - i'm using target version build 1809 - build 17783)
I agree. I started to reply, but decided it makes sense to take this to another discussion (specifically related to compilation performance):
I couldn't agree more. Or, much simpler maybe - just allow access to System.IO for all HDD ;) That would simplify things a lot.
@jtoro I have heard this from customers that this would be preferable (and I have to agree). I think the appropriate place to have this discussion is in the xlang project, and I opened this issue a while back for this exact issue https://github.com/microsoft/xlang/issues/484. Please go comment/upvote if you'd like to see that 馃槃
nobody talks about navigation performance of cached pages with a large visual tree? Why a cached page needs so much time to be rendered again? For us this is more important than shadows, lights, or rounded corners... @jevansaks you pointed me to start a discussion here, but I don't see any interest in improving performance, and I think there is a lot of work to be done in this area.
Just as as jtorjo mentioned, the compiling of UWP/C# projects is many times slower than WPF(even in release mode and the .NET Native checkbox is unchecked), which is very frustrating for me. Hope WinUi can change this.
@danzil @fabiant3-zz - FYI for compiler/tooling performance. @bartekk8 @Austin-Lamb - FYI for runtime performance.
Looks like there is another discussion on compiler performance here.
@asierpn If you have a specific scenario (navigation) that is not performant, can you please open an issue with a repro so it can be investigated independenty ?
We're well aware that UWP/WinUI project compile performance, including full and incremental builds, can be way better and we're taking steps to make it so. Issue #1535 that Ranjesh mentioned tracks that.
@ranjesh I've opened the issue #2032 explaining better our scenario and with a sample app.
@danzil As an FYI, the more recent of a Windows version you're targeting, the worse the compile time.
Right now, I'm targeting 1903, and just the deployment itself takes 6+ seconds! (no building at all). On 1809, deploment was less than 1 second.
Here are a few other notes:
@jtorjo thanks for adding your experience! We've got all these issues in mind as we're designing the upcoming performance improvements in XAML compilation.
@danzil Cool! Trust me, I'm more than looking forward to any improvements you guys may have. Probably on a daily basis, I'm wasting at least 45 minutest just compiling/deploying stuff.
@danzil Any ETA on this? Compile times are simply insane...
Me, and I'm sure anyone doing UWP, would love anything -- like, gradual improvements! And we'd be the first to test them. Thanks!
I totally understand it's painful and I'm sorry I can't give you better news. Right now, my understanding is that it will likely take several more months before the team could start working on an implementation for this problem. @LucasHaines may be able to help with a more precise ETA or plan.
@LucasHaines Could you please share an ETA? Thanks!
Most helpful comment
@jtorjo
I guess you are talking about Release build times right? The Release build is so slow because of .NET Native. It makes build times much slower, but gives you major improvements in startup times, XAML rendering (data binding) and memory usage. In Debug mode, apps should usually build pretty fast, so this does not really affect development cycles that much IMO. At least, I usually only build Release every now and then, so for me this is not a problem (I am a big fan of .NET Native performance gains).
I agree that StorageFolder/StorageFile API is a major painpoint for any file driven application. I don't believe that this is caused by sandboxing, it is probably more a problem of how the API is designed. Every instance contains these properties: Attributes, DateCreated, DisplayName, DisplayType. In the old FileInfo API, these would only be retrieved on-demand on first access. But in UWP all of these properties are always retrieved as soon as an instance is created, which makes the API so much slower when you enumerate large folders. It works well if you only access files every now and then (like, open one file through file dialog). But apps that work with a lot of files and folders will have major performance hits due to the bad API design.
UWP really needs a fast, light-weight file API that pretty much only wraps the file name (and maybe some file permission token). Then there should be an async method to create the "fat" StorageFile/StorageFolder instances from it, or open streams directly, on demand.
I have seen things like this, but I don't think that async is really the cause of these problems. Normally, exceptions from async code are handled very well. The bigger problem might be that you usually don't see any call stack from native platform code, so if the platform calls back into user code and things fail there, you sometimes don't even know why or how things have failed, where in WPF you would always see a clear stack trace of how your code was invoked.
Did you try with VS performance tools? They work very well for me.