System.Drawing.Image Performance

As I mentioned earlier, I wrote an application called JPEG Hammer for manipulating and viewing EXIF data in digital photos. As a .NET Application the single biggest performance bottleneck has been:

 Image photo = Image.FromFile(fileName, true);

For a 6.1 megapixel (3008 x 2000) 1.47 MB JPEG it took on the order of 631 milliseconds to load. Multiply this by 50 or so photos and you are talking about 32 seconds to fully load these pictures. Then add the time necessary to draw the UI for the app, update thumbnails etc. On a 1.8 ghz Pentium 4 I'd expect more... and lets just say it's no where near as fast as the XP Shell (or other non .NET phot apps), so what gives?

Well, after much Googling (with no success) I tried https://support.microsoft.com/ and I came across this gem (Microsoft KB 831419):

FIX: Slow performance when you call System.Drawing.Image.FromStream to load a bitmap image

So, curious as I am, I get my hands on this hotfix which updates:

  • System.Windows.Forms.dll
  • System.Design.dll
  • System.Drawing.dll

Specifically, this update adds a new method to System.Drawing.Imaging:

 System.Drawing.Image.FromStream(Stream stream, bool useICM, bool validateImageData)

This is essentially a new signature for an existing method:

 System.Drawing.Image.FromStream(Stream stream, bool useICM)

As you can see, validateImageData is a new parameter. Setting it to true is the default behavior that we have today (essentially the same as calling FromStream(Stream stream, bool useICM)).

So I made a change to my application. Before my code looked like this:

 using (Image photo = Image.FromFile(this.fileInfo.FullName, true))
{
    //do stuff
}

So I changed it to:

 using (FileStream fs = new FileStream(this.fileInfo.FullName, FileMode.Open, FileAccess.ReadWrite))
{
    using (Image photo = Image.FromStream(fs, true, false))
    {
        // do stuff
    }
}

And here are the results. The average time it took to process 37 JPEGs taken from the same camera and roughly the same size went from 631ms to 6.76ms. So, this new method is 93x faster. Holy Cow!!! I thought that maybe something was wrong, so I re-ran the tests many times, and always got the same result. Simply mind boggling. This probably brings the .NET implementation to the same speed as writing native C++ code against GDI+.

The good news is that the article mentions that this fix will get rolled into the next Service Pack. However, you'll need to modify your code as I did to take advantage of this.

Update: you can download this fix in SP1 of the .NET Framework 1.1:

https://www.microsoft.com/downloads/details.aspx?FamilyID=a8f5654f-088e-40b2-bbdb-a83353618b38&DisplayLang=en