Wednesday, May 12, 2010

MergedDictionaries performance problems in WPF

I don’t normally like to blatantly plagiarise other people’s comments, but this seems to be a little know bug that sounds like it should be shared.

A colleague of mine emailed our internal tech list the following email

I strongly urge everyone working with WPF to use this or at least benchmark it in your own applications if you use ResourceDictionaries.MergedDictionaries. I consider this to be a huge problem in WPF. I’m not sure if it exists in Silverlight, but I would assume it does.

I was just debugging a very long render delay in some WPF code and I came across this little tidbit:

http://www.wpftutorial.net/MergedDictionaryPerformance.html

The quote of interest is: “Each time a control references a ResourceDictionary XAML creates a new instance of it. So if you have a custom control library with 30 controls in it and each control references a common dictionary you create 30 identical resource dictionaries!”

Normally that isn’t a huge problem, but when you consider the way that I personally (and have suggested to others) that they organize their resources in Prism projects it gets to be a **serious** problem. For example, let’s say we have this project structure:

/MyProject.Resources
       /Resources
                -Buttons.xaml
                -DataGrid.xaml
                -Global.xaml
                -Brushes.xaml
                -WindowChrome.xaml
                -Icons.xaml
 
/MyProject.Module1
      /Resources
                -Module1Resources.xaml  (References all Dictionaries in /MyProject.Resources/Resources/*)
      /Views
                -View1.xaml
                -View2.xaml
      
/MyProject.Module2
      /Resources
                -Module2Resources.xaml   (References all Dictionaries in /MyProject.Resources/Resources/*)
      /Views
                -View1.xaml
                -View2.xaml
      
/MyProject.Shell
      /Resources
                -ShellResources.xaml   
      /Views
                -MainShell.xaml

If in your views you reference the module-level ResourceDictionary (which helps for maintainability and modularity) then every time you create an instance of View1.xaml for example, you would have to parse all the ResourceDictionaries in /MyProject.Resources/Resources/* every time. This isn’t really a memory concern but it is a huge performance concern. There can potentially be thousands of lines of XAML code to parse and the time really does add up.

I recently switched all of the MergedDictionary references:

<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source=”/SomeDictionary.xaml/>
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

To use the attached SharedResourceDictionary which shadows the Source property and keeps a global cache of all ResourceDictionaries parsed:

<ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
        <SharedResourceDictionary Source=”/SomeDictionary.xaml/>
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

And I saw a performance increase of almost two orders of magnitude … From almost 6000ms to 200ms. I’ve attached this code; I used the basic sample implementation in the link above so this is considered public information for client purposes.

Cheers,

Charlie

Thanks to Charlie Robbins (Lab49) for expanding on Christian’s blog post and for letting me re-print your email.

5 comments:

sandip patel said...

Thanks for FYI Lee.

jehof said...

I´m currently trying to use the SharedResourceDictionary in my Silverlight 4 Application.

But if i change the Element to i get erros in Visual Studio 2010 (COMExceptions)

Do you changed the ResourceDictionary-Element in all MergedDictionaries-Elements or only your UserControl.Resources

jehof said...

Currently i´m trying to use the SharedResourceDictionary in my Silverlight 4 application, but i´m not getting it to work.

I have the same project structure as you, but when i try to change the ResourceDictionary-Element to SharedResourceDictionary-Element i´m getting COMErrors in Visual Studio 2010.

Do you changed the Element in all MergedDictionaries-Elements or only in your UserControls?

In which Project/Assembly do you pack SharedResourceDictionary?

Lee Campbell said...

@jehof; as I said in the post this was plagiarised from a collegue. I have yet to personally try this on Silverlight 4. I will attempt to do my own analysis on this but I can not promise anything soon.
At a guess, you would need to make sure that you either
1. ensure that the namespace (ie 'WPFTutorial.Utils') in the XmlnsDefinition attribute you set is not just cut and paste from the example on wpftutorial.net, but actually relates to the namespace you have put the SharedResource class
2. or you use an appropriate namespace prefix in your xaml to correctly identitfy the SharedResource class.

HTH

ME. said...

Has anyone gotten this to work in Silverlight 4? I am also getting the COM exceptions.. after getting an InvalidOperationException: Element is already the child of another element.

Any help is appreciated.

Thanks,
Mary Ellen