Optimizing a WPF Screen – The Problem
The new version of my company’s flagship application was in trouble – the project floundering and was in danger of being cancelled. It seems that the primary screen of the application was ponderously slow – slow to come up, and vaguely clunky to use – and was eating up an excessive amount of memory. I was asked to crack it open and see if there was anything to be done.
A single run of the application demonstrated that there were definitely things amiss. It took almost 2 full seconds to load and render the screen, and memory consumption would jump by at least 50mb. While this might make sense if the screen were fetching and displaying an enormous amount of data, this wasn’t the case – the screen only displayed about 60 fields worth of data. Furthermore, if the screen were repeatedly opened and closed, memory consumption would continue to rise – easily growing to several hundred megabytes.
I knew, in broad strokes, how the screen worked. A call was made to a WCF Service which retrieved the data from the DB, formatted it in a way that was more easily accessible by the client layer, and returned an XML document. The screen would then render this data.
I had been involved in the data layer of the application, and was intimately familiar with that part of the process. I was pretty confident that the fetching and generation of the data wasn’t the problem, but I wanted to rule that out first. A simple test application demonstrated that the data was returned from the WCF call in under 300ms. That left the UI layer, so I turned my attention there.
The application’s UI is written using WPF and makes extensive use of DataTemplates to render the UI directly off of the data-model, which is held in a single XmlDataProvider. There is also a automation engine which enables customer configured validation logic – for instance, if field 1 has the value X, flag fields 2 and 3 as “required” and hide field 4.
I captured the XML from the WCF call and built a sandbox application that contained only the primary screen of the application. With that, I captured some baseline metrics:
| Load the screen | 1390ms |
| Set a value in the XML which triggers automation | 650ms |
| Set a value in the XML which doesn’t trigger automation | 30ms |
| Memory consumption (via GC.GetTotalMemory(true)) | 82mb |
None of that was good news. Even the 30ms time was alarming. I realize that 30ms isn’t very much, but that was being incurred with every keystroke via the UI, and that was on my hefty development box. Just simply typing in a field would put things under load.
So, I had determined quantitatively that there was an actual problem, and that we were not just battling user-perception. And, I had some baseline numbers to use to verify if a change had a positive or negative effect.
Next time, I’ll drill into the actual XAML and see what there was to be found there.