| // Installation. | |
| // Abseil, compile with -labseil | |
| // Libdivide as header. | |
| // GMP as header, compile with -lgmp | |
| // Change the private constructor of absl::uin128 to public in order to use public inheritance. | |
| // GMOCK, GUNIT, compile it. | |
| #include "absl/numeric/int128.h" | |
| #include <random> | |
| #include "libdivide.h" | |
| #include "benchmark/benchmark.h" | |
| #include "base/public/gmock.h" | |
| #include "base/public/gunit.h" | |
| #include "gmp.h" | |
| typedef unsigned tu_int __attribute__((mode(TI))); | |
| typedef int ti_int __attribute__((mode(TI))); | |
| typedef uint64_t du_int; | |
| typedef uint32_t su_int; | |
| typedef union { | |
| tu_int all; | |
| struct { | |
| du_int low; | |
| du_int high; | |
| } s; | |
| } utwords; | |
| tu_int LLVM_divmod(tu_int a, tu_int b, tu_int* rem) { | |
| const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT; | |
| const unsigned n_utword_bits = sizeof(tu_int) * CHAR_BIT; | |
| utwords n; | |
| n.all = a; | |
| utwords d; | |
| d.all = b; | |
| utwords q; | |
| utwords r; | |
| unsigned sr; | |
| // special cases, X is unknown, K != 0 | |
| if (n.s.high == 0) { | |
| if (d.s.high == 0) { | |
| // 0 X | |
| // --- | |
| // 0 X | |
| if (rem) *rem = n.s.low % d.s.low; | |
| return n.s.low / d.s.low; | |
| } | |
| // 0 X | |
| // --- | |
| // K X | |
| if (rem) *rem = n.s.low; | |
| return 0; | |
| } | |
| // n.s.high != 0 | |
| if (d.s.low == 0) { | |
| if (d.s.high == 0) { | |
| // K X | |
| // --- | |
| // 0 0 | |
| if (rem) *rem = n.s.high % d.s.low; | |
| return n.s.high / d.s.low; | |
| } | |
| // d.s.high != 0 | |
| if (n.s.low == 0) { | |
| // K 0 | |
| // --- | |
| // K 0 | |
| if (rem) { | |
| r.s.high = n.s.high % d.s.high; | |
| r.s.low = 0; | |
| *rem = r.all; | |
| } | |
| return n.s.high / d.s.high; | |
| } | |
| // K K | |
| // --- | |
| // K 0 | |
| if ((d.s.high & (d.s.high - 1)) == 0) /* if d is a power of 2 */ { | |
| if (rem) { | |
| r.s.low = n.s.low; | |
| r.s.high = n.s.high & (d.s.high - 1); | |
| *rem = r.all; | |
| } | |
| return n.s.high >> __builtin_ctzll(d.s.high); | |
| } | |
| // K K | |
| // --- | |
| // K 0 | |
| sr = __builtin_clzll(d.s.high) - __builtin_clzll(n.s.high); | |
| // 0 <= sr <= n_udword_bits - 2 or sr large | |
| if (sr > n_udword_bits - 2) { | |
| if (rem) *rem = n.all; | |
| return 0; | |
| } | |
| ++sr; | |
| // 1 <= sr <= n_udword_bits - 1 | |
| // q.all = n.all << (n_utword_bits - sr); | |
| q.s.low = 0; | |
| q.s.high = n.s.low << (n_udword_bits - sr); | |
| // r.all = n.all >> sr; | |
| r.s.high = n.s.high >> sr; | |
| r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); | |
| } else /* d.s.low != 0 */ { | |
| if (d.s.high == 0) { | |
| // K X | |
| // --- | |
| // 0 K | |
| if ((d.s.low & (d.s.low - 1)) == 0) /* if d is a power of 2 */ { | |
| if (rem) *rem = n.s.low & (d.s.low - 1); | |
| if (d.s.low == 1) return n.all; | |
| sr = __builtin_ctzll(d.s.low); | |
| q.s.high = n.s.high >> sr; | |
| q.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); | |
| return q.all; | |
| } | |
| // K X | |
| // --- | |
| // 0 K | |
| sr = 1 + n_udword_bits + __builtin_clzll(d.s.low) - | |
| __builtin_clzll(n.s.high); | |
| // 2 <= sr <= n_utword_bits - 1 | |
| // q.all = n.all << (n_utword_bits - sr); | |
| // r.all = n.all >> sr; | |
| if (sr == n_udword_bits) { | |
| q.s.low = 0; | |
| q.s.high = n.s.low; | |
| r.s.high = 0; | |
| r.s.low = n.s.high; | |
| } else if (sr < n_udword_bits) /* 2 <= sr <= n_udword_bits - 1 */ { | |
| q.s.low = 0; | |
| q.s.high = n.s.low << (n_udword_bits - sr); | |
| r.s.high = n.s.high >> sr; | |
| r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); | |
| } else /* n_udword_bits + 1 <= sr <= n_utword_bits - 1 */ { | |
| q.s.low = n.s.low << (n_utword_bits - sr); | |
| q.s.high = (n.s.high << (n_utword_bits - sr)) | | |
| (n.s.low >> (sr - n_udword_bits)); | |
| r.s.high = 0; | |
| r.s.low = n.s.high >> (sr - n_udword_bits); | |
| } | |
| } else { | |
| // K X | |
| // --- | |
| // K K | |
| sr = __builtin_clzll(d.s.high) - __builtin_clzll(n.s.high); | |
| // 0 <= sr <= n_udword_bits - 1 or sr large | |
| if (sr > n_udword_bits - 1) { | |
| if (rem) *rem = n.all; | |
| return 0; | |
| } | |
| ++sr; | |
| // 1 <= sr <= n_udword_bits | |
| // q.all = n.all << (n_utword_bits - sr); | |
| // r.all = n.all >> sr; | |
| q.s.low = 0; | |
| if (sr == n_udword_bits) { | |
| q.s.high = n.s.low; | |
| r.s.high = 0; | |
| r.s.low = n.s.high; | |
| } else { | |
| r.s.high = n.s.high >> sr; | |
| r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); | |
| q.s.high = n.s.low << (n_udword_bits - sr); | |
| } | |
| } | |
| } | |
| // Not a special case | |
| // q and r are initialized with: | |
| // q.all = n.all << (n_utword_bits - sr); | |
| // r.all = n.all >> sr; | |
| // 1 <= sr <= n_utword_bits - 1 | |
| su_int carry = 0; | |
| for (; sr > 0; --sr) { | |
| // r:q = ((r:q) << 1) | carry | |
| r.s.high = (r.s.high << 1) | (r.s.low >> (n_udword_bits - 1)); | |
| r.s.low = (r.s.low << 1) | (q.s.high >> (n_udword_bits - 1)); | |
| q.s.high = (q.s.high << 1) | (q.s.low >> (n_udword_bits - 1)); | |
| q.s.low = (q.s.low << 1) | carry; | |
| // carry = 0; | |
| // if (r.all >= d.all) | |
| // { | |
| // r.all -= d.all; | |
| // carry = 1; | |
| // } | |
| const ti_int s = (ti_int)(d.all - r.all - 1) >> (n_utword_bits - 1); | |
| carry = s & 1; | |
| r.all -= d.all & s; | |
| } | |
| q.all = (q.all << 1) | carry; | |
| if (rem) *rem = r.all; | |
| return q.all; | |
| } | |
| inline du_int Divide128div64to64Primitive(du_int u1, du_int u0, du_int v, | |
| du_int* r) { | |
| // Code taken from Hacker's Delight: | |
| // http://www.hackersdelight.org/HDcode/divlu.c. | |
| // License permits inclusion here per: | |
| // http://www.hackersdelight.org/permissions.htm | |
| const uint64_t b = (1ULL << 32); // Number base (32 bits) | |
| uint64_t un1, un0; // Norm. dividend LSD's | |
| uint64_t vn1, vn0; // Norm. divisor digits | |
| uint64_t q1, q0; // Quotient digits | |
| uint64_t un64, un21, un10; // Dividend digit pairs | |
| uint64_t rhat; // A remainder | |
| int32_t s; // Shift amount for norm | |
| // If overflow, set rem. to an impossible value, | |
| // and return the largest possible quotient | |
| if (u1 >= v) { | |
| *r = (uint64_t)-1; | |
| return (uint64_t)-1; | |
| } | |
| // count leading zeros | |
| s = __builtin_clzll(v); | |
| if (s > 0) { | |
| // Normalize divisor | |
| v = v << s; | |
| un64 = (u1 << s) | (u0 >> (64 - s)); | |
| un10 = u0 << s; // Shift dividend left | |
| } else { | |
| // Avoid undefined behavior of (u0 >> 64). | |
| // The behavior is undefined if the right operand is | |
| // negative, or greater than or equal to the length | |
| // in bits of the promoted left operand. | |
| un64 = u1; | |
| un10 = u0; | |
| } | |
| // Break divisor up into two 32-bit digits | |
| vn1 = v >> 32; | |
| vn0 = v & 0xFFFFFFFF; | |
| // Break right half of dividend into two digits | |
| un1 = un10 >> 32; | |
| un0 = un10 & 0xFFFFFFFF; | |
| // Compute the first quotient digit, q1 | |
| q1 = un64 / vn1; | |
| rhat = un64 - q1 * vn1; | |
| while (q1 >= b || q1 * vn0 > b * rhat + un1) { | |
| q1 = q1 - 1; | |
| rhat = rhat + vn1; | |
| if (rhat >= b) break; | |
| } | |
| // Multiply and subtract | |
| un21 = un64 * b + un1 - q1 * v; | |
| // Compute the second quotient digit | |
| q0 = un21 / vn1; | |
| rhat = un21 - q0 * vn1; | |
| while (q0 >= b || q0 * vn0 > b * rhat + un0) { | |
| q0 = q0 - 1; | |
| rhat = rhat + vn1; | |
| if (rhat >= b) break; | |
| } | |
| *r = (un21 * b + un0 - q0 * v) >> s; | |
| return q1 * b + q0; | |
| } | |
| inline du_int Divide128div64to64(du_int u1, du_int u0, du_int v, du_int* r) { | |
| #if defined(__x86_64__) | |
| du_int result; | |
| __asm__("divq %[v]" : "=a"(result), "=d"(*r) : [v] "r"(v), "a"(u0), "d"(u1)); | |
| return result; | |
| #else | |
| return Divide128div64to64Primitive(u1, u0, v, r); | |
| #endif | |
| } | |
| struct OptimizedDiv { | |
| du_int operator()(du_int u1, du_int u0, du_int v, du_int* r) const { | |
| return Divide128div64to64(u1, u0, v, r); | |
| } | |
| }; | |
| struct PrimitiveDiv { | |
| du_int operator()(du_int u1, du_int u0, du_int v, du_int* r) const { | |
| return Divide128div64to64Primitive(u1, u0, v, r); | |
| } | |
| }; | |
| struct GmpDiv { | |
| du_int operator()(du_int u1, du_int u0, du_int v, du_int* r) const { | |
| mp_limb_t q[2] = {u0, u1}; | |
| mp_limb_t result[2] = {0, 0}; | |
| *r = mpn_divrem_1(result, 0, q, 2, v); | |
| return result[0]; | |
| } | |
| }; | |
| template <typename Div128by64> | |
| tu_int MyDivMod1(tu_int a, tu_int b, tu_int* rem) { | |
| const unsigned n_utword_bits = sizeof(tu_int) * CHAR_BIT; | |
| (void)n_utword_bits; | |
| utwords dividend; | |
| dividend.all = a; | |
| utwords divisor; | |
| divisor.all = b; | |
| utwords quotient; | |
| utwords remainder; | |
| if (divisor.s.high == 0) { | |
| remainder.s.high = 0; | |
| if (dividend.s.high < divisor.s.low) { | |
| quotient.s.low = Div128by64{}(dividend.s.high, dividend.s.low, | |
| divisor.s.low, &remainder.s.low); | |
| quotient.s.high = 0; | |
| } else { | |
| quotient.s.high = | |
| Div128by64{}(0, dividend.s.high, divisor.s.low, ÷nd.s.high); | |
| quotient.s.low = Div128by64{}(dividend.s.high, dividend.s.low, | |
| divisor.s.low, &remainder.s.low); | |
| } | |
| if (rem) *rem = remainder.all; | |
| return quotient.all; | |
| } | |
| int shift = | |
| __builtin_clzll(divisor.s.high) - __builtin_clzll(dividend.s.high); | |
| divisor.all <<= shift; | |
| quotient.s.high = 0; | |
| quotient.s.low = 0; | |
| for (; shift >= 0; --shift) { | |
| quotient.s.low <<= 1; | |
| // const ti_int s = (ti_int)(divisor.all - dividend.all - 1) >> (n_utword_bits - 1); | |
| // quotient.s.low |= s & 1; | |
| if (dividend.all >= divisor.all) { | |
| dividend.all -= divisor.all; | |
| quotient.s.low |= 1; | |
| } | |
| // dividend.all -= divisor.all & s; | |
| divisor.all >>= 1; | |
| } | |
| if (rem) *rem = dividend.all; | |
| return quotient.all; | |
| } | |
| template <typename Div128by64> | |
| tu_int MyDivMod2(tu_int a, tu_int b, tu_int* rem) { | |
| utwords dividend; | |
| dividend.all = a; | |
| utwords divisor; | |
| divisor.all = b; | |
| utwords quotient; | |
| utwords remainder; | |
| if (divisor.all > dividend.all) { | |
| if (rem) *rem = dividend.all; | |
| return 0; | |
| } | |
| if (divisor.s.high == 0) { | |
| remainder.s.high = 0; | |
| if (dividend.s.high < divisor.s.low) { | |
| quotient.s.low = Div128by64{}(dividend.s.high, dividend.s.low, | |
| divisor.s.low, &remainder.s.low); | |
| quotient.s.high = 0; | |
| } else { | |
| quotient.s.high = | |
| Div128by64{}(0, dividend.s.high, divisor.s.low, ÷nd.s.high); | |
| quotient.s.low = Div128by64{}(dividend.s.high, dividend.s.low, | |
| divisor.s.low, &remainder.s.low); | |
| } | |
| if (rem) *rem = remainder.all; | |
| return quotient.all; | |
| } | |
| quotient.s.high = 0; | |
| quotient.s.low = libdivide::libdivide_128_div_128_to_64( | |
| dividend.s.high, dividend.s.low, divisor.s.high, divisor.s.low, | |
| &remainder.s.high, &remainder.s.low); | |
| if (rem) { | |
| *rem = remainder.all; | |
| } | |
| return quotient.all; | |
| } | |
| struct DivisionImpl : public absl::uint128 { | |
| using Base = absl::uint128; | |
| using Base::Base; | |
| DivisionImpl(absl::uint128 u) : Base(u) {} | |
| }; | |
| struct LLVMDivision : public absl::uint128 { | |
| using Base = absl::uint128; | |
| using Base::Base; | |
| LLVMDivision(absl::uint128 u) : Base(u) {} | |
| }; | |
| absl::uint128 operator/(const LLVMDivision& lhs, const LLVMDivision& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords n; | |
| n.all = LLVM_divmod(lhs_u.all, rhs_u.all, nullptr); | |
| return absl::MakeUint128(n.s.high, n.s.low); | |
| } | |
| absl::uint128 operator%(const LLVMDivision& lhs, const LLVMDivision& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords rem; | |
| LLVM_divmod(lhs_u.all, rhs_u.all, &rem.all); | |
| return absl::MakeUint128(rem.s.high, rem.s.low); | |
| } | |
| struct MyDivision1 : public absl::uint128 { | |
| using Base = absl::uint128; | |
| using Base::Base; | |
| MyDivision1(absl::uint128 u) : Base(u) {} | |
| }; | |
| absl::uint128 operator/(const MyDivision1& lhs, const MyDivision1& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords n; | |
| n.all = MyDivMod1<OptimizedDiv>(lhs_u.all, rhs_u.all, nullptr); | |
| return absl::MakeUint128(n.s.high, n.s.low); | |
| } | |
| absl::uint128 operator%(const MyDivision1& lhs, const MyDivision1& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords rem; | |
| MyDivMod1<OptimizedDiv>(lhs_u.all, rhs_u.all, &rem.all); | |
| return absl::MakeUint128(rem.s.high, rem.s.low); | |
| } | |
| struct MyDivision2 : public absl::uint128 { | |
| using Base = absl::uint128; | |
| using Base::Base; | |
| MyDivision2(absl::uint128 u) : Base(u) {} | |
| }; | |
| absl::uint128 operator/(const MyDivision2& lhs, const MyDivision2& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords n; | |
| n.all = MyDivMod1<PrimitiveDiv>(lhs_u.all, rhs_u.all, nullptr); | |
| return absl::MakeUint128(n.s.high, n.s.low); | |
| } | |
| absl::uint128 operator%(const MyDivision2& lhs, const MyDivision2& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords rem; | |
| MyDivMod1<PrimitiveDiv>(lhs_u.all, rhs_u.all, &rem.all); | |
| return absl::MakeUint128(rem.s.high, rem.s.low); | |
| } | |
| struct GmpDivision : public absl::uint128 { | |
| using Base = absl::uint128; | |
| using Base::Base; | |
| GmpDivision(absl::uint128 u) : Base(u) {} | |
| }; | |
| absl::uint128 operator/(const GmpDivision& lhs, const GmpDivision& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords n; | |
| n.all = MyDivMod1<GmpDiv>(lhs_u.all, rhs_u.all, nullptr); | |
| return absl::MakeUint128(n.s.high, n.s.low); | |
| } | |
| absl::uint128 operator%(const GmpDivision& lhs, const GmpDivision& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords rem; | |
| MyDivMod1<GmpDiv>(lhs_u.all, rhs_u.all, &rem.all); | |
| return absl::MakeUint128(rem.s.high, rem.s.low); | |
| } | |
| struct LibDivideDivision : public absl::uint128 { | |
| using Base = absl::uint128; | |
| using Base::Base; | |
| LibDivideDivision(absl::uint128 u) : Base(u) {} | |
| }; | |
| absl::uint128 operator/(const LibDivideDivision& lhs, const LibDivideDivision& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords n; | |
| n.all = MyDivMod2<OptimizedDiv>(lhs_u.all, rhs_u.all, nullptr); | |
| return absl::MakeUint128(n.s.high, n.s.low); | |
| } | |
| absl::uint128 operator%(const LibDivideDivision& lhs, const LibDivideDivision& rhs) { | |
| utwords lhs_u; | |
| utwords rhs_u; | |
| lhs_u.s.low = absl::Uint128Low64(lhs); | |
| lhs_u.s.high = absl::Uint128High64(lhs); | |
| rhs_u.s.low = absl::Uint128Low64(rhs); | |
| rhs_u.s.high = absl::Uint128High64(rhs); | |
| utwords rem; | |
| MyDivMod2<OptimizedDiv>(lhs_u.all, rhs_u.all, &rem.all); | |
| return absl::MakeUint128(rem.s.high, rem.s.low); | |
| } | |
| namespace { | |
| template <typename T> | |
| void TestDivision() { | |
| using std::swap; | |
| // a := q * b + r | |
| T a, b, q, r; | |
| // Zero test. | |
| a = 0; | |
| b = 123; | |
| q = a / b; | |
| r = a % b; | |
| EXPECT_EQ(0, q); | |
| EXPECT_EQ(0, r); | |
| a = T(0x530eda741c71d4c3, 0xbf25975319080000); | |
| q = T(0x4de2cab081, 0x14c34ab4676e4bab); | |
| b = T(0x1110001); | |
| r = T(0x3eb455); | |
| ASSERT_EQ(a, q * b + r); // Sanity-check. | |
| T result_q, result_r; | |
| result_q = a / b; | |
| result_r = a % b; | |
| EXPECT_EQ(q, result_q); | |
| EXPECT_EQ(r, result_r); | |
| // Try the other way around. | |
| swap(q, b); | |
| result_q = a / b; | |
| result_r = a % b; | |
| EXPECT_EQ(q, result_q); | |
| EXPECT_EQ(r, result_r); | |
| // Restore. | |
| swap(b, q); | |
| // Dividend < divisor; result should be q:0 r:<dividend>. | |
| swap(a, b); | |
| result_q = a / b; | |
| result_r = a % b; | |
| EXPECT_EQ(0, result_q); | |
| EXPECT_EQ(a, result_r); | |
| // Try the other way around. | |
| swap(a, q); | |
| result_q = a / b; | |
| result_r = a % b; | |
| EXPECT_EQ(0, result_q); | |
| EXPECT_EQ(a, result_r); | |
| // Restore. | |
| swap(q, a); | |
| swap(b, a); | |
| // Try a large remainder. | |
| b = a / 2 + 1; | |
| T expected_r = T(0x29876d3a0e38ea61, 0xdf92cba98c83ffff); | |
| // Sanity checks. | |
| ASSERT_EQ(a / 2 - 1, expected_r); | |
| ASSERT_EQ(a, b + expected_r); | |
| result_q = a / b; | |
| result_r = a % b; | |
| EXPECT_EQ(1, result_q); | |
| EXPECT_EQ(expected_r, result_r); | |
| } | |
| template <typename T> | |
| void TestRandomInputs() { | |
| const int kNumIters = 1 << 18; | |
| std::minstd_rand random(testing::UnitTest::GetInstance()->random_seed()); | |
| std::uniform_int_distribution<uint64_t> uniform_uint64; | |
| for (int i = 0; i < kNumIters; ++i) { | |
| const T a = T(uniform_uint64(random), uniform_uint64(random)); | |
| const T b = T(uniform_uint64(random), uniform_uint64(random)); | |
| if (b == 0) { | |
| continue; // Avoid a div-by-zero. | |
| } | |
| const T q = a / b; | |
| const T r = a % b; | |
| ASSERT_EQ(a, b * q + r); | |
| } | |
| } | |
| TEST(Uint128, DivideAndMod) { | |
| TestDivision<DivisionImpl>(); | |
| TestDivision<LLVMDivision>(); | |
| TestDivision<MyDivision1>(); | |
| TestDivision<MyDivision2>(); | |
| TestDivision<GmpDivision>(); | |
| TestDivision<LibDivideDivision>(); | |
| } | |
| TEST(Uint128, DivideAndModRandomInputs) { | |
| TestRandomInputs<DivisionImpl>(); | |
| TestRandomInputs<LLVMDivision>(); | |
| TestRandomInputs<MyDivision1>(); | |
| TestRandomInputs<MyDivision2>(); | |
| TestRandomInputs<GmpDivision>(); | |
| TestRandomInputs<LibDivideDivision>(); | |
| } | |
| constexpr size_t kSampleSize = 1000000; | |
| std::mt19937 MakeRandomEngine() { | |
| std::random_device r; | |
| std::seed_seq seed({r(), r(), r(), r(), r(), r(), r(), r()}); | |
| return std::mt19937(seed); | |
| } | |
| template <typename T, typename H = typename std::conditional<false, int64_t, | |
| uint64_t>::type> | |
| std::vector<std::pair<T, T>> GetRandomClass128SampleUniformDivisor() { | |
| std::vector<std::pair<T, T>> values; | |
| std::mt19937 random = MakeRandomEngine(); | |
| std::uniform_int_distribution<H> uniform_h; | |
| values.reserve(kSampleSize); | |
| for (size_t i = 0; i < kSampleSize; ++i) { | |
| T a{absl::MakeUint128(uniform_h(random), uniform_h(random))}; | |
| T b{absl::MakeUint128(uniform_h(random), uniform_h(random))}; | |
| values.emplace_back(std::max(a, b), std::max(T(2), std::min(a, b))); | |
| } | |
| return values; | |
| } | |
| template <typename T> | |
| void BM_DivideClass128UniformDivisor(benchmark::State& state) { | |
| auto values = GetRandomClass128SampleUniformDivisor<T>(); | |
| while (state.KeepRunningBatch(values.size())) { | |
| for (const auto& pair : values) { | |
| benchmark::DoNotOptimize(pair.first / pair.second); | |
| } | |
| } | |
| } | |
| template <typename T> | |
| void BM_RemainderClass128UniformDivisor(benchmark::State& state) { | |
| auto values = GetRandomClass128SampleUniformDivisor<T>(); | |
| while (state.KeepRunningBatch(values.size())) { | |
| for (const auto& pair : values) { | |
| benchmark::DoNotOptimize(pair.first % pair.second); | |
| } | |
| } | |
| } | |
| template <typename T, typename H = typename std::conditional<false, int64_t, | |
| uint64_t>::type> | |
| std::vector<std::pair<T, H>> GetRandomClass128SampleSmallDivisor() { | |
| std::vector<std::pair<T, H>> values; | |
| std::mt19937 random = MakeRandomEngine(); | |
| std::uniform_int_distribution<H> uniform_h; | |
| values.reserve(kSampleSize); | |
| for (size_t i = 0; i < kSampleSize; ++i) { | |
| T a{absl::MakeUint128(uniform_h(random), uniform_h(random))}; | |
| H b{std::max(H{2}, uniform_h(random))}; | |
| values.emplace_back(std::max(a, T(b)), b); | |
| } | |
| return values; | |
| } | |
| template <typename T> | |
| void BM_DivideClass128SmallDivisor(benchmark::State& state) { | |
| auto values = GetRandomClass128SampleSmallDivisor<T>(); | |
| while (state.KeepRunningBatch(values.size())) { | |
| for (const auto& pair : values) { | |
| benchmark::DoNotOptimize(pair.first / pair.second); | |
| } | |
| } | |
| } | |
| template <typename T> | |
| void BM_RemainderClass128SmallDivisor(benchmark::State& state) { | |
| auto values = GetRandomClass128SampleSmallDivisor<T>(); | |
| while (state.KeepRunningBatch(values.size())) { | |
| for (const auto& pair : values) { | |
| benchmark::DoNotOptimize(pair.first % pair.second); | |
| } | |
| } | |
| } | |
| #define BENCH_TYPE(X) \ | |
| BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, X); \ | |
| BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, X); \ | |
| BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, X); \ | |
| BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, X) | |
| BENCH_TYPE(absl::uint128); | |
| BENCH_TYPE(LLVMDivision); | |
| BENCH_TYPE(MyDivision1); | |
| BENCH_TYPE(MyDivision2); | |
| BENCH_TYPE(GmpDivision); | |
| BENCH_TYPE(LibDivideDivision); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment