Reapply modulo helpers and inline fast remainders

This commit is contained in:
Marek Lemańczyk committed 2025-09-22 19:26:07 +02:00
1 parent 5db0c4fa95
commit ca866ffc5b
43 files changed
+982 -134

No files matched your search

@@ -22,10 +22,10 @@ public class Mod10UInt128Benchmarks
ulong value = (ulong)Value;
ulong high = value >> 64;
ulong low = value;
ulong highRem = high.Mod10();
ulong lowRem = low.Mod10();
ulong combined = lowRem + highRem * 6UL;
return combined.Mod10();
ulong highRem = high % 10UL;
ulong lowRem = low % 10UL;
ulong combined = lowRem + highRem * 6UL;
return combined % 10UL;
}
[Benchmark]
@@ -42,11 +42,11 @@ public class Mod10UInt128Benchmarks
while (high != zero)
{
// 2^64 ≡ 6 (mod 10)
result = (result + (ulong)high * 6UL).Mod10();
high >>= 64;
}
result = (result + (ulong)high * 6UL) % 10UL;
high >>= 64;
}
return result.Mod10();
return result % 10UL;
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod10UIntBenchmarks
{
[Params(10U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 10U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod10();
}
}
@@ -11,37 +11,44 @@ public class Mod10_8_5_3Benchmarks
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModuloOperator()
{
ulong value = Value;
return (value % 10UL, value % 8UL, value % 3UL, value % 5UL);
}
[Benchmark]
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo()
{
ulong mod10, mod8, mod3, mod5;
UInt128 zero = UInt128.Zero;
if (Value == zero)
{
mod3 = 0UL;
mod5 = 0UL;
mod8 = 0UL;
mod10 = 0UL;
return (mod10, mod8, mod3, mod5);
}
ulong mod10, mod8, mod3, mod5;
UInt128 zero = UInt128.Zero;
if (Value == zero)
{
mod3 = 0UL;
mod5 = 0UL;
mod8 = 0UL;
mod10 = 0UL;
return (mod10, mod8, mod3, mod5);
}
UInt128 high = Value >> 64;
ulong result = Value;
mod8 = result & 7UL;
// 2^64 ≡ 1 (mod 3)
ulong modRem = (result % 3UL) + ((ulong)high % 3UL);
mod3 = modRem >= 3UL ? modRem - 3UL : modRem;
// 2^64 ≡ 1 (mod 5)
modRem = (result % 5UL) + ((ulong)high % 5UL);
mod5 = modRem >= 5UL ? modRem - 5UL : modRem;
UInt128 high = Value >> 64;
ulong result = Value;
mod8 = result & 7UL;
// 2^64 ≡ 1 (mod 3)
ulong modRem = (result % 3UL) + ((ulong)high % 3UL);
mod3 = modRem >= 3UL ? modRem - 3UL : modRem;
// 2^64 ≡ 1 (mod 5)
modRem = (result % 5UL) + ((ulong)high % 5UL);
mod5 = modRem >= 5UL ? modRem - 5UL : modRem;
while (high != zero)
{
// 2^64 ≡ 6 (mod 10)
result = (result + (ulong)high * 6UL) % 10UL;
high >>= 64;
}
while (high != zero)
{
// 2^64 ≡ 6 (mod 10)
result = (result + (ulong)high * 6UL) % 10UL;
high >>= 64;
}
mod10 = result % 10UL;
mod10 = result % 10UL;
return (mod10, mod8, mod3, mod5);
}
@@ -26,7 +26,7 @@ public class Mod10_8_5_3StepsBenchmarks
{
Value.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3);
// Step residues for q += 2*p
mod10 = mod10.Mod10();
mod10 %= 10UL;
return (mod10, mod8, mod3, mod5);
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod11UInt128Benchmarks
{
[Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 11UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod11();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod11UIntBenchmarks
{
[Params(11U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 11U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod11();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod11ULongBenchmarks
{
[Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 11UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod11();
}
}
@@ -0,0 +1,32 @@
using BenchmarkDotNet.Attributes;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[MemoryDiagnoser]
public class Mod11_7_5_3Benchmarks
{
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) ModuloOperator()
{
ulong value = Value;
return (value % 11UL, value % 7UL, value % 5UL, value % 3UL);
}
[Benchmark]
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) ExtensionMethods()
{
ulong value = Value;
return (value.Mod11(), value.Mod7(), value.Mod5(), value.Mod3());
}
[Benchmark]
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) CombinedMethod()
{
Value.Mod11_7_5_3(out ulong mod11, out ulong mod7, out ulong mod5, out ulong mod3);
return (mod11, mod7, mod5, mod3);
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod128UInt128Benchmarks
{
[Params(128UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 511UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 128UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod128();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod128UIntBenchmarks
{
[Params(128U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 128U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod128();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod128ULongBenchmarks
{
[Params(128UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 128UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod128();
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod3UInt128Benchmarks
{
[Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 3UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod3();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod3UIntBenchmarks
{
[Params(3U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 3U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod3();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod3ULongBenchmarks
{
[Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 3UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod3();
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod5UInt128Benchmarks
{
[Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 5UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod5();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod5UIntBenchmarks
{
[Params(5U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 5U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod5();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod5ULongBenchmarks
{
[Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 5UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod5();
}
}
@@ -0,0 +1,81 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 3)]
[MemoryDiagnoser]
public class Mod6ComparisonBenchmarks
{
private const int Iterations = 10_000_000;
private const ulong Divisor = 6UL;
private static readonly ulong FastDivMul = (ulong)(((UInt128)1 << 64) / Divisor);
[Params(3UL, 131071UL, ulong.MaxValue - 1024UL)]
public ulong StartValue { get; set; }
[Benchmark(Baseline = true, OperationsPerInvoke = Iterations)]
public ulong ModuloOperator()
{
ulong checksum = 0UL;
ulong value = StartValue;
for (int i = 0; i < Iterations; i++)
{
checksum += value % Divisor;
value += (ulong)((i & 15) + 1);
}
return checksum;
}
[Benchmark(OperationsPerInvoke = Iterations)]
public ulong DivisionBased()
{
ulong checksum = 0UL;
ulong value = StartValue;
for (int i = 0; i < Iterations; i++)
{
ulong quotient = value / Divisor;
checksum += value - quotient * Divisor;
value += (ulong)((i & 15) + 1);
}
return checksum;
}
[Benchmark(OperationsPerInvoke = Iterations)]
public ulong FastDivHigh()
{
ulong checksum = 0UL;
ulong value = StartValue;
for (int i = 0; i < Iterations; i++)
{
ulong quotient = value.FastDiv64(Divisor, FastDivMul);
checksum += value - quotient * Divisor;
value += (ulong)((i & 15) + 1);
}
return checksum;
}
[Benchmark(OperationsPerInvoke = Iterations)]
public ulong ExtensionMethod()
{
ulong checksum = 0UL;
ulong value = StartValue;
for (int i = 0; i < Iterations; i++)
{
checksum += value.Mod6();
value += (ulong)((i & 15) + 1);
}
return checksum;
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod6UInt128Benchmarks
{
[Params(6UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 63UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 6UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod6();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod6UIntBenchmarks
{
[Params(6U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 6U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod6();
}
}
@@ -0,0 +1,43 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 3)]
[MemoryDiagnoser]
public class Mod6ULongBenchmarks
{
private static readonly ulong Divisor = 6UL;
private static readonly ulong FastDivMul = (ulong)(((UInt128)1 << 64) / Divisor);
[Params(3UL, 131071UL, ulong.MaxValue)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % Divisor;
}
[Benchmark]
public ulong DivisionBased()
{
ulong quotient = Value / Divisor;
return Value - quotient * Divisor;
}
[Benchmark]
public ulong FastDivHigh()
{
ulong quotient = Value.FastDiv64(Divisor, FastDivMul);
return Value - quotient * Divisor;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod6();
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod7UInt128Benchmarks
{
[Params(7UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 7UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod7();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod7UIntBenchmarks
{
[Params(7U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 7U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod7();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod7ULongBenchmarks
{
[Params(7UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 63UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 7UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod7();
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod8UInt128Benchmarks
{
[Params(8UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 8UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod8();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod8UIntBenchmarks
{
[Params(8U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 8U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod8();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod8ULongBenchmarks
{
[Params(8UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 8UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod8();
}
}
@@ -1,3 +1,4 @@
using System;
using BenchmarkDotNet.Running;
namespace EvenPerfectBitScanner.Benchmarks;