{"id":2403,"date":"2010-03-01T23:00:00","date_gmt":"2010-03-01T23:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/visualstudio\/2010\/03\/01\/wpf-in-visual-studio-2010-part-2-performance-tuning\/"},"modified":"2022-10-14T13:17:49","modified_gmt":"2022-10-14T20:17:49","slug":"wpf-in-visual-studio-2010-part-2-performance-tuning","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/visualstudio\/wpf-in-visual-studio-2010-part-2-performance-tuning\/","title":{"rendered":"WPF in Visual Studio 2010 \u2013 Part 2 : Performance tuning"},"content":{"rendered":"<p>This post, the second in a series of articles on Visual Studio 2010\u2019s 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 <a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wpf-in-visual-studio-2010-part-1-introduction\/\">first post<\/a> in the series covered the motivation for selecting WPF and some of the new WPF 4 features of which Visual Studio takes advantage.<\/p>\n<p>&nbsp;<\/p>\n<p>Tools<\/p>\n<p>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:<\/p>\n<p>The Visual Studio Profiler. This feature is built into Visual Studio itself, so it\u2019s great that it\u2019s always handy . The profiler team has its own blog: <a title=\"http:\/\/blogs.msdn.com\/profiler\/\" href=\"https:\/\/learn.microsoft.com\/en-us\/archive\/blogs\/profiler\/\">http:\/\/blogs.msdn.com\/profiler\/<\/a><\/p>\n<p>For a deeper analysis of events and timing at the operating system level, we use xperf from the Windows Performance Analysis Tools: <a title=\"http:\/\/msdn.microsoft.com\/en-us\/performance\/cc825801.aspx\" href=\"http:\/\/msdn.microsoft.com\/en-us\/performance\/cc825801.aspx\">http:\/\/msdn.microsoft.com\/en-us\/performance\/cc825801.aspx<\/a> (Download the latest Windows SDK and find the \u201cInstall Windows Performance Toolkit\u201d 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\u2019s no hiding from this tool.<\/p>\n<p>Also included in the Windows Performance Toolkit is the WPF Performance suite: <a title=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa969767.aspx\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa969767.aspx\">http:\/\/msdn.microsoft.com\/en-us\/library\/aa969767.aspx<\/a> This utility includes a \u201cVisual Profiler\u201d giving you a detailed insight into the behavior of your WPF application. More information here: <a title=\"http:\/\/windowsclient.net\/wpf\/perf\/wpf-perf-tool.aspx\" href=\"http:\/\/windowsclient.net\/wpf\/perf\/wpf-perf-tool.aspx\">http:\/\/windowsclient.net\/wpf\/perf\/wpf-perf-tool.aspx<\/a>.<\/p>\n<h3>Tips<\/h3>\n<p>For general WPF performance, please start with these MSDN topics: <a title=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa970683.aspx\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa970683.aspx\">http:\/\/msdn.microsoft.com\/en-us\/library\/aa970683.aspx<\/a><\/p>\n<p>The following tips are based on real WPF performance problems that we encountered during Visual Studio 2010 development.<\/p>\n<h4><\/h4>\n<h4>1. Use Effects wisely<\/h4>\n<p>The Perforator tool in the WPF Performance suite contains a \u201cdirty region\u201d overlay which helped us track down a performance problem with using drop-shadows in Visual Studio.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/4\/2019\/06\/image_10.png\"><img decoding=\"async\" title=\"image\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2010\/03\/image_thumb_4-3.png\" alt=\"image\" width=\"207\" height=\"37\" border=\"0\" \/><\/a><\/p>\n<p>Adding a drop-shadow to an element causes the entire element to be \u2018dirtied\u2019 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 \u2018dirtied\u2019. 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 <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.media.effects.dropshadoweffect(VS.100).aspx\">DropShadowEffect<\/a>, should be used very sparingly and only on simple elements. Often you can create the drop shadow effect a different way \u2013 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.<\/p>\n<h4>2. Reduce IRTs<\/h4>\n<p>The Perforator tool in the WPF Performance suite also showed a spike in the \u201cHardware IRT\u201d 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 <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.media.imaging.formatconvertedbitmap(VS.100).aspx\">FormatConvertedBitmap<\/a> 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 16&#215;16 icons we use on the toolbar, thereby completely eliminating these IRTs.<\/p>\n<h4>3. Limit Visual Tree complexity<\/h4>\n<p>One area we didn\u2019t explore very thoroughly was reduction of the depth of the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms753391.aspx#two_trees\">Visual Tree<\/a>. 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 \u201cToolbarButton\u201d element, moving most of the styles and behaviors out of declarative markup (XAML) and into code. It\u2019s hard to predict exactly how much that would improve toolbar construction time and memory usage.<\/p>\n<h4>4. Optimize style templates<\/h4>\n<p>One experiment we did try, however, was to replace all the toolbar control styles with a simple colored rectangle \u2013 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\u2019s 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 \u2013 all of which was wasted work. The fix was to add a little bit of logic to our toolbar controls \u2013 moving just the Visibility property setter and its trigger into code. When the style is selected, we look ahead at the \u201cIsVisible\u201d property and, if it is false, we force the Visibility to Collapsed and set up a listener on the data model\u2019s IsVisible property. When the IsVisible property changes, we remove the listener and apply the original style.<\/p>\n<h4>5. Take care making bulk changes to Application.ResourceDictionary<\/h4>\n<p>Visual Studio sets up a <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.resourcedictionary.aspx\">ResourceDictionary<\/a> at the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.application.resources.aspx\">Application<\/a> 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 <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.systemparameters.highcontrast(VS.100).aspx\">High Contrast<\/a> 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 \u2013 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.<\/p>\n<h4>6. MeasureOverride\u2019s availableSize uses PositiveInfinity to indicate \u201cdon\u2019t care\u201d<\/h4>\n<p>If you\u2019re implementing a control with <a href=\"http:\/\/www.switchonthecode.com\/tutorials\/wpf-tutorial-creating-a-custom-panel-control\">custom layout<\/a> by overriding MeasureOverride and ArrangeOverride, be aware that the parent control may pass a value of <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.double.positiveinfinity.aspx\">Double.PositiveInfinity<\/a> for either width or height (or both) to indicate it doesn\u2019t 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\u00a0 <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.controls.textblock.aspx\">TextBlock<\/a>s 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.<\/p>\n<h4>7. Fix BindingExpression path errors<\/h4>\n<p>If, when debugging your WPF application, you see errors in the output window like:<\/p>\n<blockquote><p><span style=\"font-family: Consolas; font-size: x-small;\">System.Windows.Data Error: 40 : BindingExpression path error: &#8216;AcquireFocus&#8217; property not found on &#8216;object&#8217; &#8221;DataSource&#8217; (HashCode=61327894)&#8217;. BindingExpression:Path=AcquireFocus; DataItem=&#8217;DataSource&#8217; (HashCode=61327894); target element is &#8216;VsButton&#8217; (Name=&#8221;); target property is &#8216;AcquireFocus&#8217; (type &#8216;Boolean&#8217;)<\/span><\/p><\/blockquote>\n<p>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.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/4\/2019\/06\/image_8.png\"><img decoding=\"async\" title=\"image\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2010\/03\/image_thumb_3-3.png\" alt=\"image\" width=\"640\" height=\"372\" border=\"0\" \/><\/a><\/p>\n<h4>8. Optimize for Remote Desktop scenarios : Reduce visual complexity<\/h4>\n<p>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 \u201cover the wire\u201d. 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 <a href=\"https:\/\/learn.microsoft.com\/en-us\/archive\/blogs\/jgoldb\/optimizing-visual-studio-2010-and-wpf-applications-for-remote-desktop\">Jossef Goldberg\u2019s Blog<\/a> for some additional tips and background.<\/p>\n<h4>9. Optimize for Remote Desktop scenarios : Use scrolling hint<\/h4>\n<p>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 \u2013 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 \u201cScrollWindow\u201d 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 <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.media.visual.visualscrollableareaclip(VS.100).aspx\">VisualScrollableAreaClip<\/a>. There are some restrictions on where this can be used, so read the documentation carefully.<\/p>\n<h4>10. Optimize for virtual machines and low-end hardware<\/h4>\n<p>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 <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms742196.aspx\">rendering tier<\/a> 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 <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.media.renderoptions.processrendermode(VS.100).aspx\">RenderOptions.ProcessRenderMode<\/a> 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\u2019t automatically detect, but the user can always override these settings via a page in Tools\/Options\u2026<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/4\/2019\/06\/image_4.png\"><img decoding=\"async\" title=\"image\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2010\/03\/image_thumb_1-4.png\" alt=\"image\" width=\"465\" height=\"145\" border=\"0\" \/><\/a><\/p>\n<p>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 \u201c<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.visualstudio.shell.interop.__vsspropid4(VS.100).aspx\">VSSPROPID_VisualEffectsAllowed<\/a>\u201d or, if using WPF binding to the property \u201cEnvironmentRenderCapabilities\u201d.<\/p>\n<h3>Memory Leaks<\/h3>\n<p>It\u2019s easy to overlook memory usage while trying to optimize for elapsed time.<\/p>\n<p>For memory analysis, particularly to identify memory leaks, we start with <a href=\"http:\/\/technet.microsoft.com\/en-us\/sysinternals\/dd535533.aspx\">VMMap<\/a>.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/4\/2019\/06\/image_6.png\"><img decoding=\"async\" title=\"image\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2010\/03\/image_thumb_2-2.png\" alt=\"image\" width=\"607\" height=\"484\" border=\"0\" \/><\/a><\/p>\n<p>This identifies what \u201ckind\u201d of memory is being leaked: Images, managed heap, native heap, memory mapped files or private bytes.<\/p>\n<p>For native heap leaks we use <a href=\"http:\/\/support.microsoft.com\/default.aspx\/kb\/268343\">UMDH<\/a>. For managed leaks, we use a combination of <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms979205.aspx\">CLRProfiler<\/a> and the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/bb190764.aspx\">SOS extension<\/a> for <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/cc266321.aspx\">WinDbg<\/a>. The <a href=\"http:\/\/www.bing.com\/search?q=Using+SOS.dll+for+managed+memory+leaks\">techniques<\/a> have been covered by a number of people, including <a href=\"https:\/\/learn.microsoft.com\/en-us\/archive\/blogs\/ricom\/\">Rico Mariani<\/a>.<\/p>\n<p>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: <a href=\"https:\/\/learn.microsoft.com\/en-us\/archive\/blogs\/jgoldb\/finding-memory-leaks-in-wpf-based-applications\">https:\/\/learn.microsoft.com\/en-us\/archive\/blogs\/jgoldb\/finding-memory-leaks-in-wpf-based-applications<\/a>.<\/p>\n<h3>Conclusion<\/h3>\n<p>There\u2019s plenty of information about general performance tuning techniques on the web, but I hope this article has covered some topics you won\u2019t find elsewhere. I left out a couple of items that I thought were a little too specific to Visual Studio and wouldn\u2019t be very relevant in other applications. Besides, 10 was a nice round number for the tips. If there\u2019s enough interest from the comment stream, I may revisit performance in a later part of this WPF in Visual Studio series.<\/p>\n<p>As always, please leave your comments and questions. I\u2019ll try to answer them either directly in the comments, or in future posts.<\/p>\n<p>Next time: <a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wpf-in-visual-studio-2010-part-3-focus-and-activation\/\">Part 3. Focus and Activation.<\/a><\/p>\n<p>Previous articles in the WPF in Visual Studio 2010 series:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wpf-in-visual-studio-2010-part-1-introduction\/\">Part 1 : Introduction<\/a><\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/4\/2019\/06\/clip_image002_2.jpg\"><img decoding=\"async\" title=\"clip_image002\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2010\/03\/clip_image002_thumb-3.jpg\" alt=\"clip_image002\" width=\"101\" height=\"101\" align=\"left\" border=\"0\" \/><\/a><\/p>\n<p><strong>Paul Harrington<\/strong> \u2013 Principal Developer, Visual Studio Platform Team\n<strong>Biography: <\/strong>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\u2019s degree from the University of Cambridge, England and lives with his wife and two cats in Seattle, Washington.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post, the second in a series of articles on Visual Studio 2010\u2019s 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 [&hellip;]<\/p>\n","protected":false},"author":13,"featured_media":255385,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1196,155],"tags":[5,9,53,20,133],"class_list":["post-2403","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-desktop","category-visual-studio","tag-csharp","tag-debug","tag-performance","tag-wpf","tag-xaml"],"acf":[],"blog_post_summary":"<p>This post, the second in a series of articles on Visual Studio 2010\u2019s 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 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/2403","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/users\/13"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/comments?post=2403"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/2403\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media\/255385"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media?parent=2403"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/categories?post=2403"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/tags?post=2403"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}