WPF in Visual Studio 2010 – Part 2 : Performance tuning

WPF in Visual Studio 2010 – Part 2 : Performance tuning

Rate This
  • Comments 12

This post, the second in a series of articles on Visual Studio 2010’s use of WPF, covers tips and techniques for optimizing performance of WPF applications, and also several areas where we needed to tune Visual Studio 2010 in order to squeeze the best out of WPF. The first post in the series covered the motivation for selecting WPF and some of the new WPF 4 features of which Visual Studio takes advantage.

Tools

Performance optimization begins with measurement and tuning of WPF in Visual Studio is no exception. To measure performance in Visual Studio, we used the following tools:

The Visual Studio Profiler. This feature is built into Visual Studio itself, so it’s great that it’s always handy . The profiler team has its own blog: http://blogs.msdn.com/profiler/ 

For a deeper analysis of events and timing at the operating system level, we use xperf from the Windows Performance Analysis Tools: http://msdn.microsoft.com/en-us/performance/cc825801.aspx (Download the latest Windows SDK and find the “Install Windows Performance Toolkit” link). Actually, if I had to choose one tool to do performance analysis, this would be it. Whether your performance problem is CPU, disk, memory or network (or a combination), there’s no hiding from this tool.

Also included in the Windows Performance Toolkit is the WPF Performance suite: http://msdn.microsoft.com/en-us/library/aa969767.aspx This utility includes a “Visual Profiler” giving you a detailed insight into the behavior of your WPF application. More information here: http://windowsclient.net/wpf/perf/wpf-perf-tool.aspx

Tips

For general WPF performance, please start with these MSDN topics: http://msdn.microsoft.com/en-us/library/aa970683.aspx

The following tips are based on real WPF performance problems that we encountered during Visual Studio 2010 development.

1. Use Effects wisely

The Perforator tool in the WPF Performance suite contains a “dirty region” overlay which helped us track down a performance problem with using drop-shadows in Visual Studio.

image

Adding a drop-shadow to an element causes the entire element to be ‘dirtied’ even when only a small part of the element is updated. For example, adding a drop-shadow to a toolbar causes the entire toolbar to be re-rendered whenever one of its buttons changes. At one point during development, we tried placing a drop-shadow on the document well container in Visual Studio without realizing the performance implications. From then on, any time you typed in the text editor, or moved the cursor around, the entire document well would be redrawn. Even blinking the caret caused the entire region to be ‘dirtied’. Of course, once we discovered this, we removed the drop-shadow effect and performance was back to normal. The conclusion is that bitmap effects such as DropShadowEffect, should be used very sparingly and only on simple elements. Often you can create the drop shadow effect a different way – for example, by surrounding the perimeter of the element with shapes filled with gradient brushes, varying the brush opacity to create a soft shadow effect.

2. Reduce IRTs

The Perforator tool in the WPF Performance suite also showed a spike in the “Hardware IRT” graph every time the toolbars were refreshed. IRTs (Intermediate Render Targets) are used during composition of the final image in WPF. While not particularly expensive in themselves, each one is extra memory allocated on the GPU and contributes to the overall rendering time. After a little experimentation, it turned out that our algorithm for creating grayed out icons for disabled toolbar buttons was the culprit. We were using a FormatConvertedBitmap to convert to grayscale. We replaced that algorithm with an equivalent hand-written routine for converting color images to grayscale that was optimized for the small 16x16 icons we use on the toolbar, thereby completely eliminating these IRTs.

3. Limit Visual Tree complexity

One area we didn’t explore very thoroughly was reduction of the depth of the Visual Tree. Our style templates, particularly for toolbar controls are quite complex. The visualization of a control depends on properties in the data model such as visible/invisible, enabled/disabled, iconic/textual and these are all bound to behaviors (property setters) in the templates. Even a simple-looking button with an icon on it contains all the necessary borders, grids and child elements to support the drawing of toolbar controls in the most generic way possible. It makes for a very flexible system, but results in an overly complex Visual Tree. We could replace this complexity with a single, custom “ToolbarButton” element, moving most of the styles and behaviors out of declarative markup (XAML) and into code. It’s hard to predict exactly how much that would improve toolbar construction time and memory usage.

4. Optimize style templates

One experiment we did try, however, was to replace all the toolbar control styles with a simple colored rectangle – the idea being to see if we could get a best case measurement for replacing declarative XAML styles with imperative C# code. This experiment alone revealed something interesting about style application in WPF. The full style template is parsed and evaluated even if the element is invisible. In our style templates, visibility is determined via a property setter triggered by the IsVisible property in the control’s data model. Seems straightforward enough but, as far as WPF is concerned, the Visibility property is no different from any other property. By analyzing xperf traces of toolbar initialization, we discovered that applying these style templates to controls which were ultimately invisible was costing us a significant penalty – all of which was wasted work. The fix was to add a little bit of logic to our toolbar controls – moving just the Visibility property setter and its trigger into code. When the style is selected, we look ahead at the “IsVisible” property and, if it is false, we force the Visibility to Collapsed and set up a listener on the data model’s IsVisible property. When the IsVisible property changes, we remove the listener and apply the original style.

5. Take care making bulk changes to Application.ResourceDictionary

Visual Studio sets up a ResourceDictionary at the Application level for use by any component in the application. The resources contain colors, brushes and styles for the color theme used throughout the UI. When the OS theme changes, for example when entering or leaving High Contrast mode, these resources are updated with new colors. Our first approach was to replace the resources one by one, but this was incredibly slow because each change caused property change notifications to ripple throughout the entire visual tree – nearly every visual was listening to resource changes and reapplying its style. So, the solution was to construct a brand new, standalone ResourceDictionary with the new colors and, when complete, switch out the existing resources for this new set in one operation.

6. MeasureOverride’s availableSize uses PositiveInfinity to indicate “don’t care”

If you’re implementing a control with custom layout by overriding MeasureOverride and ArrangeOverride, be aware that the parent control may pass a value of Double.PositiveInfinity for either width or height (or both) to indicate it doesn’t care. This may be your cue to use an optimized code path in your measurement logic. If your implementation needs to call Measure on its children, then continue the favor by passing PositiveInfinity down to the children too. Bonus tip: WPF  TextBlocks are optimized so that if you call Measure more than once with the same size, then it will return the same size without doing complicated measurement.

7. Fix BindingExpression path errors

If, when debugging your WPF application, you see errors in the output window like:

System.Windows.Data Error: 40 : BindingExpression path error: 'AcquireFocus' property not found on 'object' ''DataSource' (HashCode=61327894)'. BindingExpression:Path=AcquireFocus; DataItem='DataSource' (HashCode=61327894); target element is 'VsButton' (Name=''); target property is 'AcquireFocus' (type 'Boolean')

then, as well as a broken data-binding, you may have a performance problem. WPF tries several different ways to resolve path errors, including searching for attached properties and this is quite expensive. Eliminate all such warnings, and you should be good. Visual Studio 2010 has new options for debugging WPF databindings.

image

8. Optimize for Remote Desktop scenarios : Reduce visual complexity

Over a remote desktop connection, all WPF content is rendered as a bitmap. This is in contrast to GDI rendering, where primitives such as rectangles and text are sent over the wire for reconstruction on the client. The way to improve remote desktop performance is to minimize the number of bytes sent “over the wire”. The remote desktop protocol has some compression so, this means optimizing the WPF scene for compression. A simple scene compresses far better than a complex one. For example, solid colors compress better than gradients or textured brushes. In remote desktop scenarios, Visual Studio 2010 automatically reduces visual complexity by turning off animations and shadows, and by using solid color backgrounds. Also check out Jossef Goldberg’s Blog for some additional tips and background.

9. Optimize for Remote Desktop scenarios : Use scrolling hint

Also on the topic of Remote Desktop, one area where we needed additional support from WPF was for scrolling in the text editor. As I mentioned above, when in a remote session, all WPF content is transmitted as a bitmap. When the text editor scrolls by a line, that means that the entire contents of the editor region needs to be retransmitted as a bitmap. This, of course can be expensive – the larger the area of the text view, the larger the bitmap and the slower it will be. Fortunately, WPF 4.0 now knows how to issue a “ScrollWindow” command to the remote desktop session which drastically reduces the amount of information transferred across the wire. Only the scroll operation itself (very short) and the newly-exposed line need to be transmitted. To take advantage of this new operation, you need to use the property VisualScrollableAreaClip. There are some restrictions on where this can be used, so read the documentation carefully.

10. Optimize for virtual machines and low-end hardware

As well as remote desktop, Visual Studio also reduces visual complexity when it detects a virtual machine or a low-end graphics device. WPF handles the rendering tier detection and we augment it with some additional heuristics to detect virtual environments. If necessary, we automatically reduce visual complexity just as if we were in a remote session. We can also force software rendering for the entire process with the RenderOptions.ProcessRenderMode property which is new in WPF 4.0. In testing, we found some netbooks which, even though they reported Tier 2 capable GPUs, were slower in hardware rendering than in software. For those situations, we don’t automatically detect, but the user can always override these settings via a page in Tools/Options…

image

Other parts of Visual Studio, including extensions can also react to this setting by either checking the Visual Effects setting directly via the new shell property “VSSPROPID_VisualEffectsAllowed” or, if using WPF binding to the property “EnvironmentRenderCapabilities”.

Memory Leaks

It’s easy to overlook memory usage while trying to optimize for elapsed time.

For memory analysis, particularly to identify memory leaks, we start with VMMap.

image

This identifies what “kind” of memory is being leaked: Images, managed heap, native heap, memory mapped files or private bytes.

For native heap leaks we use UMDH. For managed leaks, we use a combination of CLRProfiler and the SOS extension for WinDbg. The techniques have been covered by a number of people, including Rico Mariani.

Jossef Goldberg on the WPF team has this excellent write-up on tracking down memory leaks in WPF which is just as relevant today as it was two years ago: http://blogs.msdn.com/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx

Conclusion

There’s plenty of information about general performance tuning techniques on the web, but I hope this article has covered some topics you won’t find elsewhere. I left out a couple of items that I thought were a little too specific to Visual Studio and wouldn’t be very relevant in other applications. Besides, 10 was a nice round number for the tips. If there’s enough interest from the comment stream, I may revisit performance in a later part of this WPF in Visual Studio series.

As always, please leave your comments and questions. I’ll try to answer them either directly in the comments, or in future posts.

Next time: Part 3. Focus and Activation.

Previous articles in the WPF in Visual Studio 2010 series:

Part 1 : Introduction

clip_image002

Paul Harrington – Principal Developer, Visual Studio Platform Team
Biography: Paul has worked on every version of Visual Studio .Net to date. Prior to joining the Visual Studio team in 2000, Paul spent six years working on mapping and trip planning software for what is today known as Bing Maps. For Visual Studio 2010, Paul designed and helped write the code that enabled the Visual Studio Shell team to move from a native, Windows 32-based implementation to a modern, fully managed presentation layer based on the Windows Presentation Foundation (WPF). Paul holds a master’s degree from the University of Cambridge, England and lives with his wife and two cats in Seattle, Washington.

Leave a Comment
  • Please add 2 and 5 and type the answer here:
  • Post
  • That is interesting, however I see there several things that should be optimized in the WPF itself. For example, what is the point of having a "drop shadow" effect if the recommended performance workaround is to draw it manually using gradients?

    Also, it is nice that you used a custom algorithm for graying out toolbar buttons, but it is the most common functionality ever. Should all WPF customers rewrite this algorithm or maybe it should be built in? I like this post on similar matter: http://apocryph.org/2007/08/26/what_hell_wrong_wpf/ --  "Using WPF I can easily make a button which contains a spinning 3D cube playing videos on each face that turns green when the mouse goes over it, all without any C# code. Unfortunately, no one would ever want to do that, while it’s easy to imagine a situation wherein you’d want toolbar icons to gray themselves (like, I dunno, every Windows application ever made).".

    "The full style template is parsed and evaluated even if the element is invisible." is another thing that should be done by WPF, not its users. Browsers already do some optimizations on CSS-hidden elements (not loading images, for one), I don't see why WPF can't.

  • Good article. Useful info.

    "The Visual Studio Profiler. This feature is built into Visual Studio itself, so it’s great that it’s always handy"

    Is the profiler part of the standard SKU or only in the really expensive Team System SKU?

    I do have to echo Andrey's criticism of WPF. It seems like every new UI framework from MS solves the problems that nobody really cares about while ignoring the glaring every-day issues. Eventually support/development is dropped for the UI framework and another one comes along promising to be "the one," rinse and repeat.

    Knowing I'd still end up writing loads of boilerplate code myself, with performance issues on top, I can't find any enthusiasm to switch to WPF, even though I hate the UI framework (or lack of) I'm using right now.

  • This is great!

    This is what I was expecting from Microsoft when you started using WPF for large appliations like VS10: to polish WPF into bein suitable for large applications and a nice set of rules and lessons that will help us building such applications.

    Buiding WPF applications is rather hard (harder than using something else), but with the added benefit of extreme flexibility. WPF is a very complex framework, with complex rules, and the indept knowledge is barely beginning to show up, so I thank you for all the insights you can provide, they will be put to good use.

  • I must add that I do use WPF for all new projects, and I think it is a great framework (except for styling system that hates reuse).

    I do not think building WPF apps is hard (after writing WinForms/OnPaint when I needed a custom-colored border).  But I do think that rules such as these should not be read in a blog, they should be built into framework so it tries to push developer into "pit of success" by default.

  • Thanks for this very interesting article! More WPF performance tuning tips and tricks are highly appreciated :)

  • @Andrey

    To be fair to WPF with their overall content model there is really no way they could (within reason) 'automatically gray out' a button image.  The root problem, as I see it, lies in the fact that they don't require/constrain buttons to have images.  There is no Icon or Image property on a button.  There is only a Content property, which is of type object and is inherited from ContentControl.  What exactly makes up this Content?  Well it may be as simple as an image, or it may be a StackPanel with a TextBlock and an Image, or it may be a spinning 3D cube playing videos on each face that turns green when the mouse goes over it :)  The framework doesn't know, or care.  One could argue this is a poor decision in the face of the fact that 99% of buttons have images (at least ones on toolbars), but that would mean WPF should specialized buttons on toolbars in a way that is completely different than buttons in other locations.  As I rule I tend to abhor special casing such as this, but reasonable people may disagree :)

     Not imposing these requirements on controls gives a lot of flexibility to the end user to create a 'button like' control with an arbitrary visual tree.  Unfortunately it also means that what could be considered a common case (displaying an image and having that image be grayscaled when the button is disabled) is not supported 'out of the box'.  While I am sure there are ways that could be conceived of for WPF to support arbitrary content for buttons and still discover and change visuals when a button was disabled I don't know if I would want the framework trying to be that clever.  What if my buttons contents consist of vector graphics?  Should WPF grayscale them as well?  What if it is a custom image format that WPF doesn't know anything about...how would it retrieve the raw pixels in order to do the conversion?  Should I have to implement a specific interface in order to put my neato custom image format into a button?  I do agree it would be nice if there was an out of the box converter that would take in some smallish number of known image object types and do grayscale conversion in an efficient manner (i.e. with the IRT penalty we were seeing with FormattedBitmap), but even then the user would be required to do SOME work (i.e. associating the converter).

    Ryan

  • Great article, Paul!

    @Ryan -

    I think you hinted at the right solution when you mentioned interfaces.  IMO, the entire BCL does a poor job of using interfaces to layer & compose functionality.  If I want to make a collection that supports Union() like HashSet<T> but is based on another type of backing store, I should be able to implement a [hypothetical] ISet<T> rather than falling back to ICollection<T> and hoping my consumers are smart enough.  If I want to make a WPF control that does its own custom layout, there should be an IPanel interface for me to declare that I've overridden ArrangeOverride() et al.  Changing my abstract base class from UIElement to FrameworkElement or Panel doesn't express my intent nearly as well, and comes with many unwanted side effects.

    So it is with greyscaling.  Why not make an IGreyable interface?  Suppose that custom controls/panels/etc that declare support for that interface must implement a specially optimized GreyoutOverride() method.  Controls that don't would fall back to the expensive-but-easily-generalized method currently provided by WPF.

    Naturally, if the BCL team had enough time to implement the most common converters for us, that would be icing on the cake.  But until then, giving us a solid Presentation Foundation for high performance graphics with low maintenance costs must be priority #1.

  • Just to add that yes indeed, the WPF team will be looking to address as many of these gotchas in future versions of WPF. Not everything can be made automatically fast but we know we can do much better than we are doing today.

    In the meantime, do check out the links Paul mentions as well as http://blogs.msdn.com/jgoldb/default.aspx for more hints\tips about performance as well examples of some previous issues we've addressed.

    Thanks,

    Ian Ellison-Taylor

  • A quick note on the profiler question asked by Leo Davidson: The profiler is included in the Premium and Ultimate editions. It is not in the Professional or Express editions.

    Here is a comparison matrix:

    http://www.microsoft.com/visualstudio/en-us/products/2010/default.mspx#compare

  • Thanks for the comments so far, everyone. I knew when I began putting together this series that there was the risk it would appear like a catalog of problems with WPF and that would leave you with the impression that WPF is hard to use or not ready for "prime time". From the comments, a few people are reaching that opinion, so let me cautiously try to address that.

    This is not a "marketing" blog. Far from hiding the imperfections of software engineering, I want to bring them to the foreground. I believe many of you appreciate that candor and transparency. The purpose of this particular series is to educate readers who are planning their own projects and may be considering using WPF. I want to give them an opportunity to evaluate WPF fairly, even if they ultimately choose an alternative presentation framework.

    I don’t believe there is such thing as the perfect presentation framework, and WPF doesn’t pretend to be it. On the other hand, it’s easy to point out flaws, while completely overlooking all the pieces that ‘just work’. That’s the “squeaky wheel” syndrome. What this series and, in particular this article on performance, does not cover are all the changes in WPF 4 that everyone gets “for free out of the box”. That gives me an idea for an extra part in the series, but I’m sure it’s better to close with that article than to lead with it.

  • @Paul, I see your point about grayscaling, however two things would be enough for majority of cases: effective greyscaling filter/converter, good solution would be would be to have it as attached property on Image, and, in addition to that, default support for Button+Image case within toolbar.

    WPF already has targeted styles for buttons within toolbars (only on the top level), so I do not see it as an issue, especially since it would be located in Style as presentation-related stuff should be.

    Of course building your own complex custom format-based solution would require custom greyscaler, but this would not be a question for a lot of people. It is the same things as the built-in controls -- most people do not expect to rewrite combobox, however custom control for image album cover is something that they have to write themselves.

    Btw, if WPF has style selectors system comparable to what CSS has, supporting arbitrary levels of inclusion could be as easy as ToolBar Button[IsEnabled=false] Image { filter: GreyScale }. And switching it off could be as easy.

  • With respect to "Leaks caused by use of Event Handlers", was this potential problem addressed in VS2010 by (1) using weak references or (2) any event hook / unhook design pattern used systematically throughout the development process or (3) a mix of both depending on some criteria like the type of control concerned.

    Thanks.

Page 1 of 1 (12 items)