In part one I talked about the support functions in the C standard libraries of various x86 32-bit compilers that perform arithmetic operations when you use 64-bit integers in your code.

While updating WCRT to work with the latest Visual C++ compilers, I was writing my own implementations of these functions, and naturally I tested them against the versions supplied in the VC CRT to verify they worked.

To my surprise, I found the GCD test I wrote for Long Division ran faster when compiled with WCRT.

C:\gcdtest>gcdtest_gcc-4.5.1.exe
32-bit GCD: 314
64-bit GCD: 851

C:\gcdtest>gcdtest_vc2010.exe
32-bit GCD: 300
64-bit GCD: 835

C:\gcdtest>gcdtest_vc2010-wcrt.exe
32-bit GCD: 300
64-bit GCD: 656

This naturally piqued my curiosity.

It turns out that the 64-bit arithmetic functions in VC have remained unchanged since Visual C++ 4.2 from 1996. The compiler and optimizer have evolved over the years, adapting to new processors, and improving the speed of your code. But if you happen to divide two 64-bit integers you are relying on code that is 14 years old.

This was of course more than enough incentive to spend a little time trying to improve my implementations to see if I could beat the VC CRT.

Here are parts of the result from running this test on an Athlon 64:

Function          WCRT        VC       Diff
-------------------------------------------
HH _alldiv        1709      3907     +56.2%
HL _alldiv        3260      4309     +24.3%
LL _alldiv        1204      2161     +44.2%
...
HH _allmul         293       337     +13.0%
HL _allmul         631       739     +14.6%
LL _allmul         279       291      +4.1%
...
<32 _allshl        657       745     +11.8%
<64 _allshl        568       566      -0.3%
<96 _allshl        654       537     -21.7%
...

As you can see, the difference ranges from a few percent to over 50% in the most favorable test.

The only operations that are slower in general are shifts of more than 64 bits. The shift functions do not contain a lot of code to work with in the first place, but I did choose to sacrifice some speed in shifts above 64 bits to get a small improvement in shifts below 32 bits, because they seem more likely to occur in actual code.

Now of course these improvements do come with some reservations. For one, the CRT functions have to perform well across all processors from 386 up to the cutting edge, while I only wrote mine with Intel Core and AMD Athlon in mind. Also, the test functions I use do not necessarily reflect the average use, so while some of the optimizations I did were favorable to the tests used, they may not be for other cases.

That being said, I think this does show that it is possible to get a non-trivial improvement by optimizing these functions. And if critical parts of your code deal with 64-bit numbers it is certainly worth investigating alternatives.

And of course I can’t help but wonder if the CLR is compiled with Visual C++, so doing arithmetic on 64-bit numbers in C# and other .NET languages ends up at the same runtime functions?