-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Startup cost of XamlServices parsing is 27% slower on .NETCore than on .NET Framework #94
Comments
Thanks @dotMorten. This is likely to be expected, as Core is not currently ngened. We'll address before Core3 ships. |
Baiting @benaadams @AndreyAkinshin if they have interest to look for low-hanging fruit here 😉 |
Loading the assemblies' custom attributes takes a lot of time. The more assemblies that are loaded, the slower it will take. If you create a simple hello world app that tests this, .NET Framework loads 5 assemblies but .NET Core 3.0 preview loads 23 assemblies. There's a EDIT: If a |
I love seeing this kind of issue. Great to see the focus on fundamentals. |
Re GetCustomAttributes, it was improved by @NickCraver in dotnet/coreclr#20779 but that change would already be in preview 1. |
@danmosemsft It's also 24 loaded assemblies (.NET Core 3.0 latest daily build) vs 5 loaded assemblies (.NET Framework 4.7.2). If you patch System.Xaml to only check the first 5 returned assemblies (.NET Framework only loads 5 assemblies), then .NET Core 3.0 code executes faster. So it seems the 5x number of assemblies is causing the slowdown. It has to execute more code. Original .NET Core 3.0 daily build:
|
Finally had a chance to look into this. Really great to see folks engaged here. I was able to reproduce the difference and captured traces of 10K iterations of the parse operation to compare. As was mentioned above, the biggest issue that I see is significantly higher cost in and caused The biggest CPU usage differences are:
Breaking down the allocations that contribute to the additional allocation helper CPU (as well as memory cost), these are the top 3 allocators:
The first step here should be for this to be investigated from the reflection angle. @joshfree, can you get eyes on this from a reflection expert? I can share traces offline and help ramp someone up on the data. |
Having taken some steps here already, it definitely seems like we can eliminate If you're curious, this is where they're generated for a great many attribute code paths. |
This is probably because .NET Core loads 24 assemblies but .NET Framework only loads 5 assemblies. The code gets custom attributes of all loaded assemblies. |
@0xd4d, you're right - the iteration over all assemblies happens here:
As @NickCraver points out, there is possibly some optimization that can happen in the reflection path, but it is also true that there may need to be some work done in Xaml parsing to either limit the number of assemblies that get processed, or cache the results. Without a clear understanding of the WPF technology, it's not clear to me what should be done here. @fabiant3, are you the right person to look at this? |
cc @steveharter for Reflection |
The main cost for Parsing (64%) happens under |
Would it be valid to make those Sounds like the context is quite flexible on usage:
Though I don't know if lookup should be unique in some way (e.g. different Xaml files having explicitly different contexts) |
There are multiple things we can yet do in reflection and I'd like to get to them, but I second @benaadams's approach here on an actual solution that would make the differences really desired here. The problem with reflection performance optimizations is they hit a hard wall with contracts: they must return a new attribute set on each call - we cannot return the same instances (this would break all sorts of existing code). The only way we can cache instances (after deciding it's valid to do so) is well above the layer those allocations happen at, e.g. in a lookup dictionary such as those in play here. |
I just wanted to add here: in doing some benchmark down we discovered that while my optimizations went into 3.0 Preview 1, some steps backwards also landed in there. Details are in https://github.com/dotnet/coreclr/issues/21456 and a PR to fix that regression is in dotnet/coreclr#21462. That should help out here, but only marginally compared to not actually calling this path more than once as proposed above. |
@steveharter can you assist in the investigation from the coreclr/Reflection side of the house? |
Sure I'll take a look asap at the reflection side |
If thats the chosen path the cache might need weak references (making it even more expensive) to not block unloadable assemblies "forever", otherwise it would be a memory leak with apps having reloadable plugin architectures or are emitting generated code in collectible assemblies at runtime. We are doing the latter on desktop, I haven't used AssemblyLoadContext in Core yet so I don't know entirely the implications, but its something to keep in mind. |
Changed the title to reflect the real issue. |
executing HTTP requests in WPF command is much slower on .NET Core idk what causes this but it works fine in .NET Framework. (I'm using refit btw) not sure if it's WPF problem or .NET Core problem or refit problem... https://github.com/dotnet/corefx/issues/23401 EDIT: SOLVED. See this comment https://github.com/dotnet/corefx/issues/23401#issuecomment-561671803 |
For reference: @bugproof comment is unrelated to this issue or to WPF. (.NET Core implements HTTPRequest differently from .NET Framework.) |
Just FYI it is issues like this that have caused us to stop our port to netcore. I had hoped performance would be better. I hope these issues continue to see activity and improvement. |
@rolandh - We determined that this issue only affects apps that load lots of small XAML snippets using XamlServices.Parse. Is that your case? (We believe it's not all that common, but we don't have any hard evidence to either support or refute that belief.) Please continue to open issues, or upvote existing issues, about specific perf concerns. Comments about "issues like this" are not really actionable, as no one knows what you're referring to specifically. |
I haven't experienced any issues as we haven't ported to net core yet. I was just saying that seeing multiple issues like this has meant that we won't be considering porting ove from net 4.7 in the short term. I've seen quite a few benchmarks showing slow downs, I haven't seen any showing performance increases in WPF yet, when I do I'll look at porting. I thought that might be good information for MS on how to prioritise issues and development. I know its not an actionable comment for you, but it might be for someone higher up who wants to see a higher percentage off people port their applications. |
|
Pretty much every major library's first iteration to netcore has had slowdowns. Sometimes dramatic, eg 10x slower with entity frameworks 2.1 release. I see almost nothing showing performance benefits, only contrived small applications seem to benefit. Everything that is a large in production library seems to go backwards and introduce bugs. There is no incentive for us to upgrade after seeing issues like this repeatedly occur after porting to netcore. Here is a list of relevant cases I've seen when researching whether netcore is stable enough to upgrade yet. https://supportcenter.devexpress.com/ticket/details/T824984/wpf-core-3-0-performance This was nopcommerce after upgrading to netcore EF Core large slow downs vs the older entity framework |
Just my 2 cents: I also ported a large WPF+EntityFramework app to core 3.0 and found that load times increased from <4 seconds for .Net Framework to 5+ seconds with .Net Core 3.0. This is with creating native images for both in release and debug builds. Runtime performance seemed largely unchanged so I abandoned this porting effort and will try this again with .Net 5 when it is released. There are numerous reports of slow-downs like in the post above and also this one: https://github.com/dotnet/wpf/issues/94 My impression is that some work is required to address all these already reported WPF + .Net Core performance issues in order to make it worthwhile to perform this migration for more complex applications with lots of assemblies and XAML resources. Cheers |
@ajcvickers re EF feedback above. |
@laggage your .NET 48 is running as 32-bit and .NET Core is running as 64-bit Does adding |
@benaadams I tried your suggestion, but still, core wpf just cost more memory than net4.x wpf; |
@SamBent This issue was milestoned to core 5.0 but I don't see any updates even though 5.0 is being released. Is there reason to believe these performance regressions were fixed and the people on this forum should evaluate this again in their applications. |
The original issue - startup cost of XamlServices.Parse - is not yet addressed in 5.0. It only comes up if the app explicitly asks for it, so it's not a factor in load times of most apps. A number of other issues have been mentioned here, most of them outside WPF's scope. You can follow the links to check on the status with the appropriate owners. |
@SamBent In my case the problems may be DataTemplate instantiation via XamlReader.Parse which I assume internally calls XamlServices.Parse. Will this issue be assigned to a new milestone or will WPF apps which make using of XAML parsing see permanent load time degradation if ported to .Net Core? |
This was milestoned to 7.0 but the startup time of WPF apps running .net7.0 are still very slow compared to .net 4.8 |
Pls is there any updates on this from .net 8 |
This is a very old thread but can someone give any benchmarks for net 8 vs net 4.7/4.8 ? |
The issue is still present. We recently migrated our net48 product to .net8. We have automated benchmarking after each build. We can clearly see how almost everything at startup is performing better except the xaml parsing. This negates all performance gains of the other parts of the code where thing are performing better. We end up with around 10% slower startup times, because wpf performance issues. Everyone is very disappointed because .net 8 promised performance but I hasn't been delivered in wpf apps. |
Thank you for saving us an immense amount of effort for little gain. |
I ran the benchmarks in the original post locally but I now compare .Net Framework 4.8 vs .Net 8.0 and here are my results:
It looks like .Net 8.0 is faster than .Net Framework 4.8 for this specific benchmark on my machine. If others encounter any performance regression from .Net Framework, it would be great if they could share them and ideally include a repro. |
Problem description:
I expected/hoped that with the XmlReader hopefully being
Span<T>
based. the XAML parser would be faster in .NET Core than the .NET Framework equivalent. However that's not what I'm seeing with this benchmark:Here are the results:
The text was updated successfully, but these errors were encountered: