27

I have a WPF application, and it's slow.

It is NOT the rendering. Firstly, the rendering is quite simple, and secondly, I looked at it with WPF Performance Toolkit - nothing.

It is NOT in my own code. Firstly, the unit tests work fast, and secondly, if I replace all DataTemplates with blank ones, everything works fast.

So far, it looks like the slow part is template instantiation. That is, when you start the application, and open some complicated screen, it takes a lot of time. And by "a lot" I mean "a lot". Sometimes can be as much as 3-5 seconds - for example, when there's a datagrid with 100 rows. But when you go to another tab, and then go back to that same screen, it opens fast (as long as its viewmodel stays put).

This is very annoying not just because it's slow, but because I can't do anything about it. If I had some control over the slowness, I could, maybe, display some "opening, please wait" message or something...

Besides, when I look at some other WPF applications (most notably, ILSpy), they seem to work reasonably fast, despite the large amounts of data. This makes me believe that I'm probably doing something wrong. But I have no idea where to start.

Any ideas? Any classic mistakes? Any tips?

Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • You're wondering and making educated guesses what the problem is. Don't wonder. Don't guess, and don't expect Performance Toolkit to tell you. [Here's how to find out.](http://stackoverflow.com/questions/375913/what-can-i-use-to-profile-c-code-in-linux/378024#378024) – Mike Dunlavey Apr 26 '11 at 13:58
  • @Mike: Yes, I use this primitive sampling technique widely, and it helps me a lot when there is general performance degradation. But in this case, I simply don't have enough time to stop the execution, since the performance degradation only lasts for, at most, 3-5 seconds. Besides, on the rare occasions that I did manage to stop it, it always stops in the guts of WPF. I do pursue that clue as well (with some help from ILSpy), but so far it looks pretty hopeless. Therefore, my question is more along the lines of what could I be doing wrong with WPF. – Fyodor Soikin Apr 26 '11 at 16:52
  • Right. It stops in the guts of WPF, but what's on the stack? Every line on the stack is responsible for that time being spent. I've had people say "Oh, every time I stop it, it's in some iterator. What good is that?" The answer is *great*, now just look up the stack and you'll see the problem. If you do have a guess what the problem is, that will tell you if you're right, and if you're wrong it will tell you what the right problem is. – Mike Dunlavey Apr 26 '11 at 20:51
  • @Mike: up the stack is WPF, all the way up until my `Main()` method. It's how WPF works: it sees that it has to display some object (usually in response to a mouse click, which activated a tab), so it goes in it's dictionaries to retrieve a template for that object's type, and then it instantiates that template. All happens without touching my own code once. All I did was populate those dictionaries with templates and create the original object that gets displayed. But that happened waaaaay before, at the very start of the program. And it was fast. – Fyodor Soikin Apr 26 '11 at 21:28
  • [Here are some suggestions.](http://www.wpftutorial.net/10PerformanceTips.html) Here is some info on an [XAML Debugger.](http://www.codeproject.com/KB/WPF/XamlVisualizer.aspx) It appears that WPF is interpreting the XAML, so what I would try to do is examine enough of the state of the WPF to see which XAML it's interpreting when it's paused. – Mike Dunlavey Apr 27 '11 at 01:50
  • @Mike: What makes you think that WPF is interpreting XAML at the moment? – Fyodor Soikin Apr 27 '11 at 04:46
  • There's no other way for it to work. It may parse the XAML into an internal structure or byte codes, but then it has to interpret it. It's just a warmed-up version of the old Windows resource definition language. If there's a way to get a view into what WPF is doing, it should be possible to see what the problem is. – Mike Dunlavey Apr 27 '11 at 11:25
  • 3
    @Mike: Is it just me, or are you *wondering and making educated guesses* all of a sudden? Maybe it wouldn't hurt to read something on how WPF works first? Here's a hint: your educated guess is not entirely correct. ;-) – Fyodor Soikin Apr 27 '11 at 12:15
  • I have to admit I read something about WPF second. Now, are you saying WPF doesn't interpret the XAML structures (after parsing them into some kind of equivalent structure). How else could it do the painting? My overall point is, things that take time expose themselves to discovery by snapshot. – Mike Dunlavey Apr 27 '11 at 17:12
  • @Mike: Yes, I got your point. Thank you. – Fyodor Soikin Apr 27 '11 at 17:17
  • I tried to mitigate the issue by putting my ContentControl switching logic into another, independent tab from which the user cannot see the content switching action (by the time the user switches to the affected tab the content should already be loaded), but this did not solve the problem for me because WPF/XAML seems to use lazy-loading (which means the newly set content of the ContentControl will start to load only after the user switches to the tab). – Emir Jan 15 '21 at 08:30

5 Answers5

6

My exerience comes from working on the WPF mind mapping application NovaMind

A couple of months ago we completely rewrote our intermediate layer to solve the performance issues we had experienced. In a nutshell, creating our user controls seemed to be way to slow. Unfortunately I couldn't find a great way to profile the performance as neither the WPF Performance Suite nor commercial applications such as ANTS Profiler give you any detailed information on this part of the WPF process. (I asked this question back then)

We resorted to manually test our application by trial and error and removed parts of our user controls to see what exactly is the culprit.

In the end we solved the performance issues by completely rewriting our controls. We also cut down on the complexity of our visual tree dramatically. Before the rewrite, one of our most used user controls, when inspected with Snoop, consisted out of 61 different things, now there are only 3. Wherever possible we only added things to the visual tree on demand. (As you know in XAML even when you set things to Collapsed, they need to be created first). Finally we were forced to write our own rich text rendering control as the built in RichtextBox is ridiculously slow and the visual tree of the RichtextBox is quite complex.

I don't know if this will apply to your situation but I would recommend that you investigate your user controls and see if they are complex. Maybe you have things that you could trim. Low hanging fruits would be parts that are only rarely visible or can be created in a lazy manner. You could create these parts from code behind when necessary rather than having them in XAML. This should help you a lot.

Otherwise virtualization is your friend, if possible. In our case we couldn't do that unfortunately.

Community
  • 1
  • 1
Patrick Klug
  • 14,056
  • 13
  • 71
  • 118
  • I just verified the visual tree size. A screen that takes about 1 second to show up contains 850 visuals total. Is this excessive in your experience? What tree size is acceptable? – Fyodor Soikin May 06 '11 at 17:30
  • As a general rule of thumb, think of every 1000 elements being created in the tree as adding 1 second to your app’s start up time. [Optimizing Your XAML App for Performance](https://blogs.windows.com/windowsdeveloper/2015/10/07/optimizing-your-xaml-app-for-performance-10-by-10/) – rschoenbach Sep 24 '19 at 15:04
2

This sounds similar to a problem i was having. I posted the fix here: WPF UI Automation issue . Just posting for the benefit of searchers, as it took ages to resolve.

Following comment on link only answer, here is the crux of that post:

I did the following:

  1. Downloaded Hotfix - - http://archive.msdn.microsoft.com/KB978520 (may not be required)
  2. Downloaded Hotfix - - http://archive.msdn.microsoft.com/KB2484841 (definitely required even if you have Windows 7 / .NET 4)
  3. Improved the code further (the validation was causing an excess of objects) - Why does WPF Style to show validation errors in ToolTip work for a TextBox but fails for a ComboBox?

It may be that only Number 3 was required, but it worked. Just posting here so people dont lose the days I lost in memory profilers etc.

JsAndDotNet
  • 16,260
  • 18
  • 100
  • 123
1

User Control in your data template, is not completely bad idea but if you crave for performance then you should consider switching to lighter control. For example, having a UserControl just hosting a TextBox is very bad idea, as UserControl is made up of ContentControl, ContentControl hosts ContentPresenter and ContentPresenter will host TextBox so if you notice your Visual Tree, it has three new layer of UI Elements. Reducing Visual Tree will certainly improve the performance.

Most likely, I would suggest creating Custom Controls that may be a completely a new control having few dependency properties that can relate to data you want to present and you can have its own custom template in generic.xaml. Second, you can just simply derive a control from existing controls and redefine its default template in generic.xaml.

This approach will certainly work better as you will be reducing your Visual Tree, thus reducing Visual State Manager's job.

Changing theme or template will be slower then changing the element that hosts content. And let the element have the default template in its own generic resource dictionary.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167
1
  • Try moving all the resources up as far as they'll go, preferably into app.xaml
  • Check if you could use StaticResource instead of dynamic ones, static ones are alot faster
  • If possible, try using depedency properties in your VMs, especially if you have alot of them at once or if they have alot of properties. That will keep wpf from having to do a bunch of reflection.
aL3891
  • 6,205
  • 3
  • 33
  • 37
  • 1) Resources are all in app.xaml already. 2) I never use DynamicResource. Only static. 3) Can't do that: firstly, dependency properties require thread affinity, and secondly, they are actually a lot slower than POCO properties. See this article for more information: http://www.markusegger.com/blog/development.aspx?messageid=3dfccfc5-1a30-4af1-a924-da22f3ff6057 – Fyodor Soikin May 05 '11 at 17:08
  • oppinions differ on #3,for data binding depedency properties are faster. http://msdn.microsoft.com/en-us/library/bb613546.aspx as for thread affinity. yes this is true, but the controls you bind to are thread affinitive anyway so you still have to deal with that.. oh well, just throwing it out there :) – aL3891 May 05 '11 at 18:46
  • another example, also see the related articles link http://blog.lexique-du-net.com/index.php?post/2010/02/24/DependencyProperties-or-INotifyPropertyChanged – aL3891 May 05 '11 at 21:55
  • 1
    Frankly, I have no idea why Mr. Antoine's tests showed reverse performance picture. My own tests were exactly consistent with Mr. Egger's. Could it be that Mr. Antoine accidentally tested his debug build, which accesses Reflection on every change notification? In any case, I did my own comparison, in my exact situation, and it was not in favor of DependencyProperties. – Fyodor Soikin May 06 '11 at 05:52
1

You mention you are using a DataGrid with, say, 100 rows. A likely culprit of your perf problems is that whatever datagrid you are using isn't doing virtualization, and so your visual tree is gigantic.

Normally, long startup time in WPF screens points to a large visual tree.

I'm not sure if you're using a datatemplate per row, or some 3rd party grid that binds columns, or what - but lets say you have 8 columns with controls. Depending on your grid/validation/etc, this could be a visual tree of 20-60 items per row. If you have a combobox, then each item in the dropdown may be created per row as well.

To fix this just takes watching the details, and taking measures as you go:

  1. Use a virtualizing control as much as possible. This means using a virtualizingstackpanel inside list controls, and making sure your 3rd party controls do as well (many stock WPF controls do now by default)
  2. Do not overuse UserControls, composite controls, etc. Adding depth adds time, and putting in extra visual tree depth in a datatemplate or other repeated area adds up fast.
  3. If all else fails, show a simple screen and add controls through code to improve perceived performance
Philip Rieck
  • 32,368
  • 11
  • 87
  • 99