49

Many types in WPF derive from Freezable. It provides immutability to mutable POCO objects and, apparently, allows for improved performance in certain situations.

Has anyone found that freezing objects within their WPF application has greatly improved performance? If so, then which items gave the biggest performance difference when being frozen?

(Note that I have posted a similar but different question too)

Community
  • 1
  • 1
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742

4 Answers4

56

You might be interested in my experiences with Freezable:

I once wrote a PDF viewer using muPdf which renders bitmaps, that I render with WPF. What helps performance greatly is that I can render the page bitmaps on a background thread, freeze them, and then pass them to the UI thread. It is nice that WPF does not copy the image to freeze it, but the ability to do all this preparation on a background thread was the key benefit for me.

From what I understand, all visuals need to be frozen so they can be safely rendered by the WPF render thread. If you render large unfrozen visuals, they will get cloned to frozen ones when WPF renders them. If you freeze your static bitmaps beforehand, WPF can just share the pointer with the render thread without cloning. Unfrozen objects may even get copied repeatedly if WPF is not aware wether the object is changed from the last time it was rendered. Frozen objects eliminate the need for all this copying.

  • Very helpful thank you. I didn't realize you could do this on a background thread, should help one of my apps considerably! – Kelly May 19 '17 at 00:00
19

These potential memory leaks could happen if you use the Image control (and not use Freeze method):

a) You use BitmapImage as the Image source and do not release the BitmapImage:

static BitmapImage bi1 = new BitmapImage(new Uri("Bitmap1.bmp",UriKind.RelativeOrAbsolute));
m_Image1 = new Image();
m_Image1.Source = bi1; 
//bi1.Freeze() 
//if you do not Freeze, your app will leak memory.
MyStackPanel.Children.Add(m_Image1);

b) You assign multiple BitmapImage as the Image source and do not release all of the BitmapImage you used (similar to (a)). This one introduced in .Net 3.5:

static BitmapImage bi1 = new BitmapImage(new Uri("Bitmap1.bmp",
UriKind.RelativeOrAbsolute));
static BitmapImage bi2 = new BitmapImage(new Uri("Bitmap2.bmp",
UriKind.RelativeOrAbsolute));
bi2.Freeze();
m_Image1 = new Image();
//bi1.Freeze() 
//even though you are really using bi2 for Image Source, 
//you also need to Freeze bi1 it to avoid leak 
m_Image1.Source = bi1;  // use un-frozen bitmap, which causes the leak
m_Image1.Source = bi2;  // use frozen bitmap
MyStackPanel.Children.Add(m_Image1);

Source: WPF Performance

M. Jahedbozorgan
  • 6,914
  • 2
  • 46
  • 51
  • 1
    I assume you are referring to the scenarios described here: https://blogs.msdn.microsoft.com/jgoldb/2008/02/04/finding-memory-leaks-in-wpf-based-applications/ (you should put the specific link in your answer). I think it is worth pointing out that freezing the bitmap in these scenarios is just a work-around for what could fundamentally be considered a bad idea in the first place, i.e. using a `static` field to store a bitmap you've then subsequently referenced in an `Image` object. – Peter Duniho Sep 26 '16 at 00:17
  • 1
    The `Image` object necessarily needs to subscribe to change events, thus the bitmap retains a strong reference, preventing GC of the `Image`, and of course of anything that subscribed to _its_ change events. This is really a broader issue people writing event-driven code need to be aware of: a `static` field can root an entire tree of objects, if those object are indirectly referenced by event fields. Freezing a bitmap is good for other reasons, but a better solution to the leak is to not keep around a bitmap object in a static field when you no longer need it (e.g. when the window is closed). – Peter Duniho Sep 26 '16 at 00:17
13

Though you have already accepted the answer, just wanted to log a different version of the answer that helped me better.

From MSDN (minor edit):

If you were to modify control holding reference to unmanaged low-level resources (eg: Brush), every modification would have to regenerated those low-level objects!

The freezable class is what gives a brush the ability to find its corresponding generated, low-level objects and to update them when it changes. When this ability is enabled, the brush is said to be "unfrozen."

A freezable's Freeze method enables you to disable this self-updating ability. You can use this method to make the brush become "frozen," or unmodifiable. Thus, improving performance.

And, the code to explain the usage:

            Button myButton = new Button();
            SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

            if (myBrush.CanFreeze)
            {
                // Makes the brush unmodifiable.
                myBrush.Freeze();
            }                
            myButton.Background = myBrush;        
            if (myBrush.IsFrozen) // Evaluates to true.
            {
                // If the brush is frozen, create a clone and modify the clone.
                SolidColorBrush myBrushClone = myBrush.Clone();
                myBrushClone.Color = Colors.Red;
                myButton.Background = myBrushClone;
            }
            else
            {
                // If the brush is not frozen, it can be modified directly.
                myBrush.Color = Colors.Red;
            }
Community
  • 1
  • 1
Manish Basantani
  • 16,931
  • 22
  • 71
  • 103
4

I developed a high-performance image viewer application. We had code on the back-end that created a new bitmap every frame and wrote that bitmap to the screen like so:

Writeablebitmap wb = new WriteableBitmap();
//  < code to set the wb pixel values here >

// Push the bitmap to the screen
image.Source = wb;

During testing, we noticed that there was a terrible flickering while going 30+ FPS with moderately-sized images (1080p). The fix? Just freeze the bitmap before setting it to the image.Source. No more product-killing performance bug. Nowadays I try to freeze everything I can.

Chromozon
  • 295
  • 2
  • 3
  • 8