libdivide is an open source library

for optimizing integer division

libdivide allows you to replace expensive integer divides with comparatively cheap multiplication and bitshifts. Compilers usually do this, but only when the divisor is known at compile time. libdivide allows you to take advantage of it at runtime. The result is that integer division can become faster - a lot faster.

Furthermore, libdivide allows you to divide an SSE2 vector by a runtime constant, which is especially nice because SSE2 has no integer division instructions!

libdivide is free and open source with a permissive license. The name "libdivide" is a bit of a joke, as there is no library per se: the code is packaged entirely as a single header file, with both a C and a C++ API.

What is it good for? libdivide helps optimize the case of dividing many integer numerators by one integer denominator, where the denominator may only be known at runtime. There are many potential use cases: hash tables, scaling algorithms, a quick-'n-dirty speed boost for an interpreter, etc.

What types does it support? libdivide supports signed and unsigned division with 32 bit and 64 bit types (so four types in total). libdivide also supports dividing a packed SSE vector by these types, in a way designed to integrate with the SSE intrinsics supported by gcc, clang, and Visual C++.

Where does it run? libdivide is valid ANSI C and ANSI C++. It is packaged as a single header file for easy integration, and has been tested with clang, gcc, and Visual C++.

What is the license? libdivide uses the zlib license. This permits usage of libdivide in any product - commercial, GPL, or otherwise. No attribution is required. However, you must not claim to have written libdivide, or distribute an altered version without plainly marking it as modified.

libdivide is separately made available under the Boost license, for users who prefer the Boost license or who are already using code under this license.

Where can I get it?

To use libdivide, all you need is the header file:

libdivide.h

libdivide source is now hosted on GitHub:

http://github.com/ridiculousfish/libdivide/

See the enclosed README.txt file to begin development.

How do I use it? See the user documentation

How fast? libdivide's scalar code is up to 16 times faster for powers of 2, 10 times faster for non-powers of 2, compared to naive hardware division. Optimized vector code is a further 2-3 times faster. The performance you will see depends on many factors. Click here to see a partial list. Of course, the best way to determine how libdivide affects your performance is to try it and measure!

The optimization prowess of your compiler. libdivide depends on the compiler to effectively recognize certain sequences (in particular to emit a "high multiply" instruction), and also benefits from effective instruction scheduling and loop unswitching. Compiler optimizations play a significant role in determining the actual performance.

Signed versus unsigned. libdivide provides more benefit for unsigned types.

The bit size of the denominator (64 bit or 32 bit). libdivide provides the most benefit for dividing by 32 bit integers, on both i386 and on x86-64.

The architecture. For 32 bit numerators and denominators, libdivide provides slightly more benefit on x86-64 than on i386. For 64 bit numerators and denominators, libdivide provides much more benefit on x86-64 than on i386. This is because i386 chips have some hardware support for dividing 64 bit values, but not for multiplying 64 bit values. Even so, in my test, libdivide is nearly twice as fast as hardware division when dividing 64 bit integers in i386.

The speed of the hardware divider. Intel's new Core i7 chips have a much faster divider than previous chips, so libdivide provides relatively less benefit on these chips (but still may be several times faster than the hardware divider). libdivide provides more benefit if the native divider is slower. Presumably libdivide would be very useful on ARM chips; however I do not have ARM hardware that I can test on.

The actual value of the denominator. Certain values (e.g. 11) need one less addition and shift operation than others (e.g. 7). Unsurprisingly, powers of 2 are especially fast, because they only require shifts - no multiplication. Furthermore, some hardware dividers are slower at dividing by small numbers than by larger numbers, so libdivide may provide more benefit with small numbers.

What is the overhead? libdivide requires a data structure that is one byte larger than the size of the divisor (so 32 bit division requires storing a 40 bit struct). It does not perform dynamic memory allocation and is in general very lean on memory.

In terms of processor time, pre-computing the proper magic number and shift is on the order of one to three hardware divides, for non-powers of 2. Powers of 2 (and their negatives) are much faster.

Does it work with .NET? libdivide works well with C++/CLI. It is about twice as slow compared to native code, which means it is 2 to 5 times faster than "native" (managed) division.

Why so little assembly? In addition to the portability issues, inline assembly defeats many compiler optimizations such as CSE, unswitching, register allocation, and instruction scheduling. libdivide's functions are designed to be inlined into and optimized along with the callers, so the compiler needs full visibility into what those functions are doing. That said, small amounts of assembly are used when the performance win is large and the codegen is otherwise unattainable (for example, the function libdivide_128_div_64_to_64).

How can I help? You can contribute at the github page. Here is the wishlist. Help with ARM and on Windows is especially appreciated.

Testing and tuning on ARM, as well as NEON implementations of the vector functions.

Figure out how to get Visual C++ to generate mulq and imulq instructions for libdivide__mullhi_u64 and libdivide__mullhi_s64, respectively. Currently this is done in gcc and clang with the __uint128_t type. Until this is done, 64 bit divides in a 64 bit process on Windows will be significantly slower than necessary.

Implementations in VC++ style assembly of libdivide_128_div_64_to_64 and libdivide_64_div_32_to_32. This will speed up the precomputation time.

Vector implementations for SIMD ISAs other than SSE2.

Figure out how to make the C++ API implementation simpler. It is a facade over the C API, but is still a gnarled maze of templates.

Performance improvements are always always welcome!