From ea9bfd213bd025db11ff7c8477d81ad822f53335 Mon Sep 17 00:00:00 2001 From: Anton Lapounov Date: Tue, 21 Jan 2020 19:58:05 -0800 Subject: [PATCH] Even faster modulo computation: remove one of the three multiplications. (#406) --- .../src/System/Collections/HashHelpers.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/HashHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/HashHelpers.cs index b01b01d8746c3f..658ff55ef28654 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/HashHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/HashHelpers.cs @@ -90,21 +90,24 @@ public static int ExpandPrime(int oldSize) } #if BIT64 + // Returns approximate reciprocal of the divisor: ceil(2**64 / divisor) public static ulong GetFastModMultiplier(uint divisor) => ulong.MaxValue / divisor + 1; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint FastMod(uint value, uint divisor, ulong multiplier) { - // Using fastmod from Daniel Lemire https://lemire.me/blog/2019/02/08/faster-remainders-when-the-divisor-is-a-constant-beating-compilers-and-libdivide/ + // We use modified Daniel Lemire's fastmod algorithm (https://github.com/dotnet/runtime/pull/406), + // which allows to avoid the long multiplication if the divisor is less than 2**31. + Debug.Assert(divisor <= int.MaxValue); ulong lowbits = multiplier * value; // 64bit * 64bit => 128bit isn't currently supported by Math https://github.com/dotnet/corefx/issues/41822 - // otherwise we'd want this to be (uint)Math.MultiplyHigh(lowbits, divisor) - uint high = (uint)((((ulong)(uint)lowbits * divisor >> 32) + (lowbits >> 32) * divisor) >> 32); + // otherwise we'd want this to be (uint)Math.BigMul(lowbits, divisor, out _) + uint highbits = (uint)((((lowbits >> 32) + 1) * divisor) >> 32); - Debug.Assert(high == value % divisor); - return high; + Debug.Assert(highbits == value % divisor); + return highbits; } #endif }