I decided to benchmark reading an image in C#, and in C++, to decide which language to use in a project i’m thinking about making for myself.
I expected the benchmarks to be extremely close with C++ maybe pushing ahead slightly.
The C# code takes about 300ms each run (I ran each test 100 times), where the C++ code takes about 1.5ms.
So is my C# code wrong? Am I benchmarking it badly? Or is it really just this much slower?
Here’s the c# code I used:
Stopwatch watch = new Stopwatch();
watch.Start();
Image image = Image.FromFile(imagePath);
watch.Stop();
Console.WriteLine("DEBUG: {0}", watch.ElapsedMilliseconds);
And the C++ code pretty much boiled down to this:
QueryPerformanceCounter(&start);
Image * img = Image::FromFile(imagePath);
QueryPerformanceCounter(&stop);
delete img;
return (stop.QuadPart - start.QuadPart) * 1000.0 / freq.QuadPart;
Regardless of which language, they need to end up in an Image object, as it provides the functionality i’m going to need.
=======================================================================
As xanatos pointed out in the comments, the Image.FromFile does do checking.
More specifically, this:
num = SafeNativeMethods.Gdip.GdipImageForceValidation(new HandleRef(null, zero));
if (num != 0)
{
SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(null, zero));
throw SafeNativeMethods.Gdip.StatusException(num);
}
Using Image.FromStream() instead, you can avoid this.
What i’m wondering is, if you do avoid this and try to load an invalid image file it throws an OutOfMemory exception.
And in C++, you don’t do checking like this. So how important is this checking? Can anyone give me a situation where it would be bad to avoid this?
Yes, your benchmark is flawed. The problem is that you forgot to actually do something with the bitmap. Like paint it.
GDI+ heavily optimizes the loading of an image. Very similar to the way .NET optimizes loading an assembly. It does the bare things necessary, it reads the header of the file to retrieve essential properties. Format, Width, Height, Dpi. Then it creates a memory-mapped file to create a mapping to the pixel data in the file. But doesn’t actually read the pixel data.
Now the difference comes into play. System.Drawing.Image next actually reads the pixel data. That causes page faults, the operating system now reads the file and copies the pixel data into RAM. Highly desirable, if there’s anything wrong with the file then you’ll get an exception at the FromFile() call instead of some time later, typically when your program draws the image and is buried in framework code you didn’t write. Your bench mark for the C# code times the creation of the mmf plus the reading of the pixel data.
The C++ program is always going to have to pay for reading the pixel data too. But you didn’t measure that, you only measured the cost of creating the MMF.