Profiling WPF Applications in Visual Studio 2015 with the WPF Timeline Tool

In VS2013 Microsoft introduced the Performance and Diagnostics Hub. (Check my blog post here: http://www.12qw.ch/2015/03/performance-profiling-of-net-applications-in-visual-studio-20132015/) In visual Studio 2015, the XAML UI Responsiveness Tool has been renamed to Timeline and now fully supports all XAML based applications and therefore WPF!

You can read all the details in this post from the WPF team:

http://blogs.msdn.com/b/wpf/archive/2015/01/16/new-ui-performance-analysis-tool-for-wpf-applications.aspx

The tool is very easy to use. Just go to ANALYZE –> START DIAGNOSTICS WITHOUT DEBUGGING or hit Alt-F2.

Lets see how it works using a sample application:

image

The application is very simple. It consists of two listboxes that load 5000 images each. The only difference is that in the listbox on the left side, UI Virtualization is disabled, while in the listbox on the right side, it is enabled.

As you might know, enabling and disabling UI virtualization in WPF is a one-liner:

<ListBox  VirtualizingStackPanel.IsVirtualizing="False"
        Grid.Row="1" Grid.Column="0" Name="lb" Background="LemonChiffon" >
</ListBox>

You can already see that the number in the screenshot states that there is a tremendous performance difference once virtualization is enabled. Instead of 7.7 seconds, it took only 0.7 seconds, which is an improvement by  a factor of 10!

We are going to use the Timeline tool to profile the application and take a look under the hood to find out where the differences are. We start the profiling using the Timeline tool. Once the application is running, we load the images on the left, then on the right side and close the window.

image

The report shows us two graphs. The first one is the UI thread utilization and the second one is the frames per second on the composition and the UI thread. We can clearly see the two red sections that correspond to the loading of the images into the listboxes.

We notice that for the non-virtualized listbox, the UI thread was completely busy for a period of about 8 seconds. The thread was exclusively running layout calculations. There is a perfect correlation between the UI thread going up to 100% and the FPS dropping to 0. This is exactly what a user would experience looking at the UI. It comes to a full stop.

When we look at the virtualized listbox we can see that we have about half the CPU used for layout and the rest for rendering. The duration of the loading process can be read at about 0.8 seconds. Furthermore, we notice that the FPS did not drop to absolute zero.

Lets take a look at the details. We are going to zoom in into the 2 interesting sections and compare them side by side.

image

image image

We can confirm our conclusion from the overview that the performance bottleneck in the non-virtualized listbox comes from too long layout operations. Let’s dig deeper:

In the timeline details of the non-virtualized listbox we find one layout event that calculated roughly 20’000 visuals and lasted for a total time of 8.1 seconds. Further below we notice a lot of garbage collection activity.

image

By expanding the layout event, we can look at the layout time required for each element in the visual tree:

image

Let’s drill down to the ItemsPresenter that contains our 20K visuals. We can see that each and every visual could be rendered in a relatively short time (about 60ms). Obviously, it was the single layout operation that exhausted the UI thread because it had to calculate 20’000 child visuals and caused a lot of GC activity.

Lets look at the virtualized listbox details:

image

The details show that the layout process in the virtualized listbox took only 385ms and had to process about 80 visuals. Additionally, we can see a GC call that took 259ms and was executed during the layout calculations. If we select the garbage collection event, the timeline tool even gives us an explanation about the nature of the GC action:

image

In conclusion, it is fair to say that the Timeline Tool is incredibly easy to use and gives us very deep insight into the inner workings of our application. This could save you a lot of time when looking for performance problems.