Math.BigMul Exposed

| | Comments (0)

Today a friend and I were reflecting through System.Math (courtesy of IronPython) and we noticed the BigMul method:

Math.BigMul(Int32, Int32) : Int64

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:

(long)a * (long)b

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.

static void Main(string[] args)
{
    int a = 40993;
    int b = 69872;
    long c = 0;
 
    DateTime start;
    TimeSpan length;
 
    Console.WriteLine("Inline multiplication");
    start = DateTime.Now;
    for (int i = 0; i < 1000000000; i++)
        c = (long)a * (long)b;
    length = DateTime.Now - start;
    Console.WriteLine(c);
    Console.WriteLine(length.ToString());
    Console.WriteLine();
 
    Console.WriteLine("Math.BigMul");
    start = DateTime.Now;
    for (int i = 0; i < 1000000000; i++)
        c = Math.BigMul(a, b);
    length = DateTime.Now - start;
    Console.WriteLine(c);
    Console.WriteLine(length.ToString());
    Console.WriteLine();
 
    Console.Read();
}

The results were not encouraging.

Inline multiplication
2864262896
00:00:03.9375000

Math.BigMul
2864262896
00:00:07.2031250

Then I remembered to do a Release build and run without debugging. :-D The real results:

Inline multiplication
2864262896
00:00:01.9218750

Math.BigMul
2864262896
00:00:01.9375000

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.

public static long BigMul(int a, int b)
{
    return (a * b);
}

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:

.method public hidebysig static int64 BigMul(int32 a, int32 b) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: conv.i8 
    L_0002: ldarg.1 
    L_0003: conv.i8 
    L_0004: mul 
    L_0005: ret 
}
 
.method public hidebysig static int64 BigMul(int32 a, int32 b) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldarg.1 
    L_0002: mul 
    L_0003: conv.i8 
    L_0004: ret 
}

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.

public static long BigMul(int a, int b)
{
    return (long)a * (long)b;
}

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).

Leave a comment

About this Entry

This page contains a single entry by Greg published on October 2, 2008 8:43 PM.

Debugging with Internet Explorer and Visual Studio was the previous entry in this blog.

Embedding IronPython is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.