Today a friend and I were reflecting through System.Math (courtesy of IronPython) and we noticed the BigMul method:
Why have a method just for multiplication? It seems to be a trivial reason to add a method to the .NET framework. After all, multiplication with casting does the same thing:
Being optimistic, I suggested that perhaps Microsoft’s BigMul is implementing a faster and more efficient multiplication algorithm. Maybe there is a clever way to multiply two 32 bit numbers without explicit casting to 64 bit. Naturally, I wrote a simple speed test.
The results were not encouraging.
Then I remembered to do a Release build and run without debugging. :-D The real results:
After running it several times, I saw that they had basically the same performance. This begs the question: why did Microsoft even include BigMul? I cracked open Reflector and checked out the code myself. What I found surprised me.
That looks like an unsafe operation to me! Disturbed, I wrote and tested my own static method using the exact same code (named BigMul2). Sure enough, my copy-cat code overflowed. Puzzled, I looked at the IL code for both methods:
Do you see the difference? Math.BigMul pushes a on the stack, casts a to long, pushes b on the stack, casts b to long, multiples them together and returns the result. This is a correct algorithm for multiplying two ints (but not what Reflector initially indicated). My BigMul2 method pushes a on the stack, then pushes b, multiples, then casts to long and returns the result. Apparently Reflector didn’t properly translated the code into C#. The actual C# code used by BigMul is exactly what you would expect it to be, there is nothing special going on here.
What does this all mean?
I learned a few things from this exercise.
- Microsoft felt the need to include the trivial BigMul method although it does nothing special. It probably exists as a Best Practice device to ensure that integer multiplication is cast correctly.
- Reflector is not reliable. It apparently drops cast operation under certain conditions. It doesn’t express the cast in C#, VB, Delphi, MC++, or Chrome – only IL. This problem is reproducible, the explicit casts in my speed-test program are also missing when viewed in Reflector.
- I was reminded that debugging incurs a performance hit. What is especially noteworthy is the tremendous slowdown that comes from the debugger as it follows the execution stack into another method (in another assembly).