Archive for the ‘Code’ Category

Integer Overflow (Part 2)

Wednesday, November 8th, 2006

Update (11/08/06): I “#ifdef’ed out” the ASM in the source code so–at least–the C functions could be compiled and compared on PowerPC Mac’s.

As a follow-up to my last post on this subject, I decided to roll my own C version of the safe-add function and to port the code to GCC using g++. The code, which is here, ports the MS SafeInt class, Sree Kotay’s test and functions, and the various ASM and C functions contributed by me and others. It has had limited testing, so it is offered “as-is” and with no warranties.

WHY

Overall, the topic is interesting and promotes better practices — hopefully. The primary reason for the port was two-fold: I wanted to use the code on non-VC++/Windows-based machines for experimentation and I was interested in the performance and nuances of the code with GCC and inline ASM blocks with other OSes (e.g., Mac OS X). Moreover, this may help broaden the audience for input and research.

THE PORT

The port was pretty straight-forward except for the inline assembly. However, be forewarned that the NDEBUG constant is defined (i.e., no assert statements in the code) for GNU compilations to avoid assert-based incompatibilities between the VC++ and g++. Since NDEBUG is defined for “release” builds within Visual Studio, this didn’t seem too bad of a route. However, it would be nice to port this correctly at some point.

MY C VERSION

My attempt at a semi-portable C function is below:

#ifdef __GNUC__
inline long ericoSafeAdd (long a, long b)
#else
__forceinline long ericoSafeAdd (long a, long b)
#endif
{
    long c=a+b;
     if (((((unsigned long)a) & 0×80000000) != (((unsigned long)c) & 0×80000000))
      && (((unsigned long)a) & 0×80000000) == (((unsigned long)b) & 0×80000000)) {
        SafeIntOnOverflow();
     }
    return c;
}

I decided to go with a 1 branch, 1 compare in best cases using the integer signs. As a note, Sree  cooked up a clever approach using the compare and branch in the calculation of the integer signs. See his latest blog entry on it here.

PERFORMANCE

This function performed well in certain cases, even against Sree’s latest version, but didn’t do as well in other cases. For example, with GCC (*1) with optimization disabled, I received the following:

[eolaughlen@darwin:~/Desktop/safeint] ./safeadd
——————– Testing Addition
——————– Math requiring no overflow detection
——————– This is for speed (overhead)\r\nTest Simple… total: 149994981                 Time = 816.634 ms
Test ASM Safe… total: 149994981              Time = 1370.143 ms
Test MS Safe… total: 149994981                Time = 5959.449 ms
Test sree C Safe… total: 149994981           Time = 1826.744 ms
Test erico ASM Safe… total: 149994981       Time = 1551.537 ms
Test erico C Safe… total: 149994981           Time = 1668.906 ms

——————– Math requiring overflow exception
——————– This is for correctness – ignore timings for ‘Simple’
Test Simple… total: 2053634475                Time = 748.037 ms
Test ASM Safe… total: 2147483638             Time = 660.812 ms EXCEPTION
Test MS Safe… total: 2147483638               Time = 1950.118 ms EXCEPTION
Test sree C Safe… total: 2147483638           Time = 600.973 ms EXCEPTION
Test erico ASM Safe… total: 2147483638      Time = 534.301 ms EXCEPTION
Test erico C Safe… total: 2147483638          Time = 550.815 ms EXCEPTION

hit Enter to continue….

As shown, the ASM version performs best with my C function being second. However, with -O3 defined in the compilation, Sree’s version performs best (~13% difference) with all ASM versions failing due to the optimizations:

[eolaughlen@darwin:~/Desktop/safeint] ./safeadd
——————– Testing Addition
——————– Math requiring no overflow detection
——————– This is for speed (overhead)
Test Simple… total: 149994981                 Time = 178.267 ms
Test ASM Safe…                                     failure
Test MS Safe… total: 149994981                Time = 836.534 ms
Test sree C Safe… total: 149994981            Time = 516.035 ms
Test erico ASM Safe… total: 149994981       Time = 242.764 ms
Test erico C Safe… total: 149994981           Time = 586.174 ms

——————– Math requiring overflow exception
——————– This is for correctness – ignore timings for ‘Simple’
Test Simple… total: 2053634475                Time = 180.317 ms
Test ASM Safe…                                      failure
Test MS Safe… total: 2147483638               Time = 288.723 ms EXCEPTION
Test sree C Safe… total: 2147483638           Time = 182.712 ms EXCEPTION
Test erico ASM Safe… total: 2053634475      Time = 237.482 ms failure
Test erico C Safe… total: 2147483638          Time = 188.184 ms EXCEPTION

hit Enter to continue….

On Windows (*2) Sree’s version performs best in most cases (~10% faster). Every once in a while, my C version would do best, but believe this was data dependent based upon the integer signs.

CONCLUSION

Learn. Experiment. Try it for yourself. As with most development, there may be trade-offs between design versatility and performance and varying approaches may have their rightful use at times.

*1. Intel Core Duo 2 Ghz, Intel MacBook with Mac OS X 10.4.7 and gcc 4.0.1 build 5363
*2. PIII 1.13 Ghz, Dell Inspiron 8100 with Windows XP Home SP2 and VS/VC++ .NET 2003 7.1.3088