Optimizing images usage in .NET – part 1

Using image is an integral part of GUI application but I have found very little resources on it's resource consumption and how I can optimize it. The goal of this post is exactly that, in part 1 I will show the performance impact and in part 2 I will propose an optimization.
I'm going to consider only the memory usage as CPU performance is generally insignificant.
 

Settings

In all the tests I'm using PNG image format as it fully supports alpha channel, the results may vary by using different formats but the basic principal remains the same.
For creating the images I have used Paint.NET as it creates images that doesn't cause late loading by .NET.
To measure the memory I'm using VMMap, great tool by Sysinternals that allows to break the process memory usage with great detail.
Finally I have used .NET 4.0 WinForms test application, thought it doesn't have any effect on the results.
 

Image memory footprint

Let's start with how much process memory loading an image consumes.
I will use two images, the first1 is quite large: 213×155, 80.7K image and the second is small 16×16, 363 bytes 'icon' image.
clip_image001.png
clip_image002.png
 
Each image is loaded 50 times into WinForms process using 'Image.FromFile' method and added to a list so the GC won't collect it:

private  readonly List<Image> _image = new List<Image>();      
private void  LoadImages()      
{      
    for (int i = 0; i < 50; i++)      
    {      
        _image.Add(Image.FromFile(@"C:Casper.png"));      
    }      
}      

 
For the first (larger) image the private bytes were raised by approximately 12MB (246K per image), that is x3 times more than the image size on disk. For the second image the private bytes were raised by approximately 1.3MB (28.28K per image), that is x42 times more than the image size on disk!
 

Dimension Size on disk Memory footprint Overhead factor
213×155 80.7K 246K x3
16×16 363 bytes 28.28K x42

Figure 1: Memory footprint of large and small image.
 
I don't want to go to deeply into this but the difference between image memory footprint and its size on disk may vary significantly depending on dimensions and complexity of the image, as a rule of thumb I would say you can except x8-x10 times overhead compared to images size on disk.
 
Note that all the raise in private bytes was in 'Heap' and 'Data' memory types while the 'Managed Heap' was not effected at all, worth mentioning as .NET developers usually restrict themselves to managed memory investigation.
 
By itself this may look futile as there is no way to lower this overhead2, but as I have explained in my previous post the most common use of images in .NET GUI applications is via resource files (controls use same resource file under-the-hood). This usage will result in creating multiple instances of the same image thus resulting in huge overhead. My solution to this issue was 'Using T4 Templates for caching image resources'.
 

Image dimensions effect

To better understand the effect of image dimensions on the memory overhead we will compare the memory overhead of loading many small images and loading a single image combined of the content of all the small images. This will nullify the effect of the image content so only the dimensions are effecting the memory overhead.
 
I will use a collection of 23 images, 16×16 with total size of 13.9K and a single image that is combined of all the 23 images resulting in 48×128, 8.94K image. Note that just combining the 32 images into a single image results in 35% reduction in image size on disk.
clip_image003.png
 
Similar to previous test the collection of 23 images will be loaded 50 times into WinForms process by 'Image.FromFile' method and put it in the list so the GC won't collect them, and the same for the single combined image:

var images =  new List<Image>(50*23);      
for (int i =  0; i < 50; i++)      
{      
    foreach (var file in files)      
        images.Add(Image.FromFile(file));      
}      

 
For the 23 images collection the private bytes were raised by approximately 52MB (1069K per iteration), that is x76 times more than the images size on the disk. For the single combined image the private bytes were raised by approximately 3.3MB (67.7K per image), that is only x7.5 times more than the image size on the disk.
Loading the combined image consumed 1MB less memory in the process, that’s x15 times gain!
 
Just to be sure the results are persistent I ran another test with actual images used in one of my projects: 66 images 24×24, 63.2K , and the combined single image is 528×72, 45.4K . Running the same test resulted in 3,265K memory for the 66 images and 226K for the combined image, 3MB less memory (x14.4 times gain).
 

Count Dimension Size on disk Memory footprint Overhead factor Optimization
23 16×16 13.9K 1069K x76
1 48×128 8.94K 63.2K x7.5 1MB (x15)
66 24×24 63.2K 3,265K x51
1 528×72 45.4K 226K x5 3MB (x14.4)

Figure 2: Loading images of different size benchmark.
 
I have also measured loading times using Stopwatch that showed 9.5 msec average time for loading 23 separate 16×16 images and 1 msec for loading the combined 48×128 image, x10 times faster.
 

Image empty spaces effect

The technique of creating a single image is well known in web development, but very often you will see web sites use huge images that contain images of different size thus requiring empty space padding. Lets see if this method is acceptable in .NET.
I have taken the combined image used previously for 16×16 images and enlarged it by 50% as if to support 24×24 images resulting in 72×198, 9.5K image (only 6% increase in size on disk).
Running the same test shows the larger image took 96.1K memory, 50% increase that directly correlate with the increase in image dimensions (I know the image area is actually x2 times larger but it's not the point).
So this method is very wasteful for .NET, combined images should have as little extra space as possible.
 

Dimension Size on disk Memory footprint
48×128 8.94K 63.2K
72×198 9.5K 96.1K

Figure 3: Image with larger empty space image comparison.
 

Conclusion

  1. Loading images in .NET application is much more expensive than expected with order of magnitude overhead.
  2. Small images has significantly larger overhead.
  3. Combining small images into a single large image can reduce memory consumption by an order of magnitude.
  4. Having empty space padding in the combined image is very wasteful.

In part 2 I will propose an optimization.
 


  1. A clip of my beloved cat that goes by the name of Casper. 
  2. Not that I know of, I really would like to be corrected. 
Advertisement

2 comments on “Optimizing images usage in .NET – part 1

  1. […] my previous post, where I investigated the performance impact of loading images in .NET application, I will conclude […]

  2. […] my previous post, where I investigated the performance impact of loading images in .NET application, I will conclude […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s