% 10 operations replaces with UInt128/ulong.Mod10 methods

This commit is contained in:
Marek Lemańczyk committed 2025-09-21 07:42:25 +02:00
1 parent 3e0102a404
commit 412ce4fd50
12 files changed
+106 -45

No files matched your search

@@ -0,0 +1,52 @@
using BenchmarkDotNet.Attributes;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[MemoryDiagnoser]
public class Mod10UInt128Benchmarks
{
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public UInt128 StandardModulo()
{
return Value % 10UL;
}
[Benchmark]
public ulong ModWithHighLowModulo()
{
// Split and fold under mod 10: 2^64 ≡ 6 (mod 10)
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();
}
[Benchmark]
public ulong ModWithLoopModulo()
{
UInt128 value = Value;
UInt128 zero = UInt128.Zero;
if (value == zero)
return 0UL;
ulong result = (ulong)value;
UInt128 high = value >> 64;
while (high != zero)
{
// 2^64 ≡ 6 (mod 10)
result = (result + (ulong)high * 6UL).Mod10();
high >>= 64;
}
return result.Mod10();
}
}
@@ -5,7 +5,7 @@ using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks; namespace EvenPerfectBitScanner.Benchmarks;
[MemoryDiagnoser] [MemoryDiagnoser]
public class Mod10Benchmarks public class Mod10ULongBenchmarks
{ {
[Params(3UL, 8191UL, 131071UL, 2147483647UL)] [Params(3UL, 8191UL, 131071UL, 2147483647UL)]
public ulong Value { get; set; } public ulong Value { get; set; }
@@ -13,14 +13,7 @@ public class Mod10Benchmarks
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public ulong LegacyModulo() public ulong LegacyModulo()
{ {
ulong value = Value; return Value % 10UL;
UInt128 zero = UInt128.Zero;
if (value == zero)
{
return 0UL;
}
return value % 10UL;
} }
[Benchmark] [Benchmark]
@@ -0,0 +1,33 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[MemoryDiagnoser]
public class Mod10_8_5_3StepsBenchmarks
{
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo()
{
UInt128 value = Value;
ulong mod10 = (ulong)(value % 10UL) % 10UL;
ulong mod8 = (ulong)(value % 8UL);
ulong mod3 = (ulong)(value % 3UL);
ulong mod5 = (ulong)(value % 5UL);
return (mod10, mod8, mod3, mod5);
}
[Benchmark]
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo()
{
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();
return (mod10, mod8, mod3, mod5);
}
}
+1 -1
View File
@@ -1060,7 +1060,7 @@ internal static class Program
// Optional: RLE blacklist and binary-threshold filters on p (safe only when configured) // Optional: RLE blacklist and binary-threshold filters on p (safe only when configured)
if (p <= _rleHardMaxP) if (p <= _rleHardMaxP)
{ {
if (!_rleOnlyLast7 || (p % 10UL) == 7UL) if (!_rleOnlyLast7 || p.Mod10() == 7UL)
{ {
if (RleBlacklist.IsLoaded() && RleBlacklist.Matches(p)) if (RleBlacklist.IsLoaded() && RleBlacklist.Matches(p))
{ {
@@ -20,10 +20,8 @@ public class MersenneNumberResidueCpuTester
// Current residues // Current residues
q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3); q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3);
// Step residues for q += 2*p // Step residues for q += 2*p
ulong step10 = (ulong)(twoP % 10UL) % 10UL; twoP.Mod10_8_5_3(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
ulong step8 = (ulong)(twoP % 8UL); step10 = step10.Mod10();
ulong step3 = (ulong)(twoP % 3UL);
ulong step5 = (ulong)(twoP % 5UL);
// Allowed last-digit sets for q depending on last digit of M_p // Allowed last-digit sets for q depending on last digit of M_p
// lastIsSeven == true => allow {7,9} // lastIsSeven == true => allow {7,9}
+5 -5
View File
@@ -201,8 +201,8 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
{ {
ulong idx = (ulong)index.X; ulong idx = (ulong)index.X;
// residue automaton // residue automaton
ulong step10 = ((exponent % 10UL) << 1) % 10UL; ulong step10 = (exponent.Mod10() << 1).Mod10();
ulong r10 = q0m10 + (step10 * idx) % 10UL; ulong r10 = q0m10 + (step10 * idx).Mod10();
r10 -= (r10 >= 10UL) ? 10UL : 0UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL;
bool shouldCheck = r10 != 5UL; // skip q ≡ 5 (mod 10); other residues handled by r8/r3/r5 bool shouldCheck = r10 != 5UL; // skip q ≡ 5 (mod 10); other residues handled by r8/r3/r5
if (shouldCheck) if (shouldCheck)
@@ -358,7 +358,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastSeven) ArrayView1D<ulong, Stride1D.Dense> smallPrimesPow2LastSeven)
{ {
ulong idx = (ulong)index.X; ulong idx = (ulong)index.X;
ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL; ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL;
bool shouldCheck = r10 != 5UL; bool shouldCheck = r10 != 5UL;
if (shouldCheck) if (shouldCheck)
{ {
@@ -489,7 +489,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
ResidueAutomatonArgs ra, ArrayView<int> found, ArrayView1D<uint, Stride1D.Dense> smallCycles) ResidueAutomatonArgs ra, ArrayView<int> found, ArrayView1D<uint, Stride1D.Dense> smallCycles)
{ {
ulong idx = (ulong)index.X; ulong idx = (ulong)index.X;
ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL; ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL;
bool shouldCheck = r10 != 5UL; bool shouldCheck = r10 != 5UL;
if (shouldCheck) if (shouldCheck)
{ {
@@ -573,7 +573,7 @@ public ref struct GpuKernelLease(IDisposable limiter, GpuContextLease gpu, Kerne
ResidueAutomatonArgs ra, ArrayView<int> found, ArrayView1D<uint, Stride1D.Dense> smallCycles) ResidueAutomatonArgs ra, ArrayView<int> found, ArrayView1D<uint, Stride1D.Dense> smallCycles)
{ {
ulong idx = (ulong)index.X; ulong idx = (ulong)index.X;
ulong r10 = ra.Q0M10 + (ra.Step10 * idx) % 10UL; r10 -= (r10 >= 10UL) ? 10UL : 0UL; ulong r10 = ra.Q0M10 + (ra.Step10 * idx).Mod10(); r10 -= (r10 >= 10UL) ? 10UL : 0UL;
bool shouldCheck = r10 != 5UL; bool shouldCheck = r10 != 5UL;
if (shouldCheck) if (shouldCheck)
{ {
@@ -23,7 +23,7 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u
var pow2Kernel = gpuLease.Pow2ModKernel; var pow2Kernel = gpuLease.Pow2ModKernel;
var incKernel = gpuLease.IncrementalKernel; var incKernel = gpuLease.IncrementalKernel;
ulong step10 = ((exponent % 10UL) << 1) % 10UL; ulong step10 = (exponent.Mod10() << 1).Mod10();
ulong step8 = ((exponent & 7UL) << 1) & 7UL; ulong step8 = ((exponent & 7UL) << 1) & 7UL;
ulong step3 = ((exponent % 3UL) << 1) % 3UL; ulong step3 = ((exponent % 3UL) << 1) % 3UL;
ulong step5 = ((exponent % 5UL) << 1) % 5UL; ulong step5 = ((exponent % 5UL) << 1) % 5UL;
@@ -27,7 +27,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
var foundBuffer = accelerator.Allocate1D<int>(1); var foundBuffer = accelerator.Allocate1D<int>(1);
UInt128 exponent128 = exponent; UInt128 exponent128 = exponent;
exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3); exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3);
ulong step10 = (exponentMod10 << 1) % 10UL; ulong step10 = (exponentMod10 << 1).Mod10();
ulong step8 = (exponentMod8 << 1) & 7UL; ulong step8 = (exponentMod8 << 1) & 7UL;
ulong step3 = (exponentMod3 << 1) % 3UL; ulong step3 = (exponentMod3 << 1) % 3UL;
ulong step5 = (exponentMod5 << 1) % 5UL; ulong step5 = (exponentMod5 << 1) % 5UL;
@@ -24,7 +24,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
UInt128 kStart = 1UL; UInt128 kStart = 1UL;
byte last = lastIsSeven ? (byte)1 : (byte)0; byte last = lastIsSeven ? (byte)1 : (byte)0;
var kernel = gpuLease.Pow2ModKernel; var kernel = gpuLease.Pow2ModKernel;
ulong step10 = ((exponent % 10UL) << 1) % 10UL; ulong step10 = (exponent.Mod10() << 1).Mod10();
ulong step8 = ((exponent & 7UL) << 1) & 7UL; ulong step8 = ((exponent & 7UL) << 1) & 7UL;
ulong step3 = ((exponent % 3UL) << 1) % 3UL; ulong step3 = ((exponent % 3UL) << 1) % 3UL;
ulong step5 = ((exponent % 5UL) << 1) % 5UL; ulong step5 = ((exponent % 5UL) << 1) % 5UL;
+4 -19
View File
@@ -30,12 +30,12 @@ public sealed class MersenneResidueAutomaton
_currentQ = _step + 1UL; // start at k = 1 _currentQ = _step + 1UL; // start at k = 1
// init residues // init residues
Mod10R = Mod10(_currentQ); Mod10R = _currentQ.Mod10();
Mod8R = Mod8(_currentQ); Mod8R = Mod8(_currentQ);
Mod3R = Mod3(_currentQ); Mod3R = Mod3(_currentQ);
Mod5R = Mod5(_currentQ); Mod5R = Mod5(_currentQ);
_step10 = Mod10((ulong)(_step % 10UL)); _step10 = _step.Mod10().Mod10();
_step8 = (ulong)(_step & 7UL); // % 8 _step8 = (ulong)(_step & 7UL); // % 8
_step3 = Mod3(_step); _step3 = Mod3(_step);
_step5 = Mod5(_step); _step5 = Mod5(_step);
@@ -72,21 +72,6 @@ public sealed class MersenneResidueAutomaton
Mod5R = r5; Mod5R = r5;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong Mod10(UInt128 value)
{
// Split and fold under mod 10: 2^64 ≡ 6 (mod 10)
ulong high = (ulong)(value >> 64);
ulong low = (ulong)value;
ulong highRem = Mod10(high);
ulong lowRem = Mod10(low);
ulong combined = lowRem + highRem * 6UL;
return Mod10(combined);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong Mod10(ulong value) => value % 10UL;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong Mod8(UInt128 value) => (ulong)value & 7UL; private static ulong Mod8(UInt128 value) => (ulong)value & 7UL;
@@ -196,8 +181,8 @@ public sealed class Ending7Automaton
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong AlignTo7(ulong start) private static ulong AlignTo7(ulong start)
{ {
ulong r = start % 10UL; ulong r = start.Mod10();
ulong add = (7UL + 10UL - r) % 10UL; ulong add = (7UL + 10UL - r).Mod10();
return start + add; return start + add;
} }
+2 -2
View File
@@ -59,7 +59,7 @@ public sealed class PrimeTester(bool useInternal = false)
} }
else else
{ {
if ((n % 10UL) == 1UL && SharesFactorWithMaxExponent(n)) if (n.Mod10() == 1UL && SharesFactorWithMaxExponent(n))
{ {
result = false; result = false;
} }
@@ -266,7 +266,7 @@ public sealed class PrimeTester(bool useInternal = false)
return; return;
} }
if ((n % 10UL) == 1UL) if (n.Mod10() == 1UL)
{ {
// Early reject special GCD heuristic with floor(log2 n) // Early reject special GCD heuristic with floor(log2 n)
ulong m = 63UL - (ulong)ILGPU.Algorithms.XMath.LeadingZeroCount(n); ulong m = 63UL - (ulong)ILGPU.Algorithms.XMath.LeadingZeroCount(n);
+2 -2
View File
@@ -215,11 +215,11 @@ public static class UInt128Extensions
while (high != zero) while (high != zero)
{ {
// 2^64 ≡ 6 (mod 10) // 2^64 ≡ 6 (mod 10)
result = (result + (ulong)high * 6UL) % 10UL; result = (result + (ulong)high * 6UL).Mod10();
high >>= 64; high >>= 64;
} }
return result % 10UL; return result.Mod10();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]