views:

103

answers:

3

I am currently investigating a performance issue in an application and have highlighted the following;

I have a class -

public static class CommonIcons
{
    ...
    public static readonly System.Windows.Media.ImageSource Attributes = typeof(CommonIcons).Assembly.GetImageFromResourcePath("Resources/attributes.png");
    ...
}

As a test harness I then have the following code using this class to show the issue -

for (int loop = 0; loop < 20000; loop++)
{
    // store time before call

    System.Windows.Controls.Image image = new System.Windows.Controls.Image
                    {
                        Source = CommonIcons.Attributes,
                        Width = 16,
                        Height = 16,
                        VerticalAlignment = VerticalAlignment.Center,
                        SnapsToDevicePixels = true
                    };

    // store time after call
    // log time between before and after

}

At the start of the loop the time difference is less than 0.001 seconds, but after 20000 goes this has increased to 0.015 seconds.

If I don't use the static member and directly refer to my icon, then I do not have the performance hit, i.e.

for (int loop = 0; loop < 20000; loop++)
{
    // store time before call

    System.Windows.Controls.Image image = new System.Windows.Controls.Image
                    {
                        Source = typeof(CommonIcons).Assembly.GetImageFromResourcePath("Resources/attributes.png"),
                        Width = 16,
                        Height = 16,
                        VerticalAlignment = VerticalAlignment.Center,
                        SnapsToDevicePixels = true
                    };

    // store time after call
    // log time between before and after

}

But in my real world program I don't want to be creating the imagesource on every call (increased memory until a garbage collection), hence why a static member is used. However I also cannot live with the performance hit.

Can someone explain why the original code is creating this performance hit? And also a better solution for what I am trying to do?

Thanks

+1  A: 

It smells like something to do with garbage collection. I wonder whether there's some kind of coupling between the ImageSource and the Image which is causing problems in your first case. Have you looked to see what the memory usage of your test harness looks like in each case?

Out of interest, what happens if you set the Source to null at the end of each iteration? I know this is a bit silly, but then that's a natural corollary of it being a test harness :) It might be a further indication that it's a link between the source and the image...

Jon Skeet
For the first case, memory usage increases. Forcing a garbage collection at the end does not free the memory. For the second case, memory usage increases much more, but forcing a garbage collection at the end does free memory back up. I have took your advice with the test harness and set the Source to null at the end of each iteration and this works there is no longer a performance hit. Why is this? Thanks :)
TheBlackKnight
@TheBlackKnight: Well, presumably the `ImageSource` *somehow* has a list of `Images` created from it. That's preventing the images from being garbage collected - so you've got constant GC pressure in the first case. The solution: don't do that :) Clear the image source when you're done with the image.
Jon Skeet
A: 

Can you store only constant strings like "Resources/attributes.png" in your CommonIcons class ?

raf
A: 

The difference is not between static member or not, but it is that in the first version you create 20000 images all having the same imagesource. I don't know exactly what is going on, but under the hood there may be delegates created automatically which handle communication between imagesource and image and everytime if an event in the imagesource occurs, 20000 clients needs to be notified, so this is a large performance hit.

In the second version, each of the 20000 created images have their own imagesource so you don't experience this overhead.

Note that you should always dispose graphical objects like Images with their Dispose()-method after you are done with them, this will speed up your application a bit and lowers your general memory usage.

codymanix
Thanks for the answer. System.Windows.Controls.Image does not have a dispose method (System.Drawing.Image does).
TheBlackKnight