Optimizing 128-bit Division
danlark.org
danlark.org
Yet, I was not expecting it to be here. Overall, I put some knowledge hidden in Hacker's Delight book, Knuth, GMP and GNU in the article with my knowledge of low level optimizations. In the end it turned out to be a good thing to write and to submit into LLVM
I faced a similar problem implemented 64/64 division on 32-bit x86. I cribbed some code off the internet and came up with this:
https://github.com/titzer/virgil/blob/2c674dbe3512da23c4a644...
This is the code that my compiler generates for a 64/64 divide when it doesn't know that the divisor is max 32 bits. It works by using the x86 64/32 div instruction. The same trick would work on x86-64 by using the 128/64 divisions.
I am not sure how good the machine code you get out of your C++ is in the end, but it's hard to beat this handwritten assembly. Of course, you can do better on x86-64 because there are more registers and you don't have to spill on to the stack.
https://github.com/llvm/llvm-project/blob/master/compiler-rt...
Is it possible you can make a Boost licensed version of it so we can add it to D?
If you want to workaround this for now, I suggest looking into libdivide (https://github.com/ridiculousfish/libdivide), it is published with the boost license and the library contains all the needed artifacts I described in the article (unfortunately, not combined).
if (dd.all >= dr.all) {
dd.all -= dr.all;
quo.s.low |= 1;
}
could be bool c = dd.all >= dr.all;
dd.all -= (-c & dr.all);
quo.s.low |= c;
Clang might implement this with `cmov` instructions, coded either way.For the record, I have never followed this advice.
Since then, all algorithms in compiler-rt have been ported to Rust and live in the compiler-builtins crate. This [1] is the source code file for unsigned division. The actual logic is inside a macro and used to implement both 128 bit division in terms of 64 bit numbers and 64 bit division in terms of 32 bit numbers.
I wonder if similar optimizations can be done to that code.
[1]: https://github.com/rust-lang/rust/pull/38482
[0]: https://github.com/rust-lang/compiler-builtins/blob/master/s...
I will try to implement the same optimizations in Rust in the upcoming weeks
UPD And we opened an issue :) https://github.com/rust-lang/compiler-builtins/issues/368
lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/%3famp
So a lot of work for nothing. At least LLVM did profit.
https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-...
For the curious, the actual title is "128 bit division".
Yet, I added several tests like dividend < divisor, close to zero remainders, a lot of random stuff just to make sure each time I add a new approach, it is correct.
FWIW, I dimly recall a numeric library, maybe a float to string, which tested everything for verification. Took a few days to run.
Then maybe use the spot checks to test for regressions. Weird compiler, toolchain, processor combos. That sort of thing.
You can definitely test against all ~2^31 IEEE 754 binary32 values to make sure that float-to-decimal conversion is correct; that's what I've done with the Rust standard library (took 2 hours per test). I believe testing all ~2^63 binary64 values are also feasible by now, but only with dedicated hardwares. For that reason I believe the library had only tested with binary32.