From f2ce40ade98b1c05598e42209ac00f9336c06f0b Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sun, 26 Oct 2025 20:00:32 +0100 Subject: [PATCH] Additional logging added. --- PerfectNumbers.Core/MersenneDivisorCycles.cs | 134 ++++++++++-------- .../MersennePrimeFactorTester.cs | 16 ++- .../PrimeOrderCalculator.Cpu.cs | 47 ++++-- 3 files changed, 123 insertions(+), 74 deletions(-) diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index 516c512..78c60e0 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -360,79 +360,81 @@ public class MersenneDivisorCycles return true; } - private static bool TryFactorIntoCountsInternal(ulong value, Dictionary counts) - { - // The EvenPerfectBitScanner only requests factors for values >= 2 here. Leave the guard documented without executing it. - // if (value <= 1UL) - // { - // return true; - // } + // private static ulong _tryFactorIntoCountsInternalHits; + // private static ulong _tryCalculateCycleLengthHeuristicHits; - ulong remaining = value; - uint[] smallPrimes = PrimesGenerator.SmallPrimes; - ulong[] smallPrimesSquared = PrimesGenerator.SmallPrimesPow2; - int smallLength = smallPrimes.Length; + private static bool TryFactorIntoCountsInternal(ulong value, Dictionary counts) + { + // The EvenPerfectBitScanner only requests factors for values >= 2 here. Leave the guard documented without executing it. + // if (value <= 1UL) + // { + // return true; + // } - for (int i = 0; i < smallLength; i++) - { - ulong prime = smallPrimes[i]; - if (smallPrimesSquared[i] > remaining) - { - break; - } + ulong remaining = value; + uint[] smallPrimes = PrimesGenerator.SmallPrimes; + ulong[] smallPrimesSquared = PrimesGenerator.SmallPrimesPow2; + int smallLength = smallPrimes.Length; - // TODO: Reuse the remainder-tracking update from the cycle heuristic so repeated divisions disappear from this loop. - while ((remaining % prime) == 0UL) - { - AddFactor(counts, prime); - remaining /= prime; - } - } + for (int i = 0; i < smallLength; i++) + { + ulong prime = smallPrimes[i]; + if (smallPrimesSquared[i] > remaining) + { + break; + } - if (remaining == 1UL) - { - return true; - } + // TODO: Reuse the remainder-tracking update from the cycle heuristic so repeated divisions disappear from this loop. + while ((remaining % prime) == 0UL) + { + AddFactor(counts, prime); + remaining /= prime; + } + } - // TODO: Benchmark PrimeTester.IsPrimeCpu against Open.Numeric.Primes on the EvenPerfectBitScanner workloads to confirm the faster option for wide composites. - // if (HeuristicPrimeTester.Exclusive.IsPrimeCpu(remaining, CancellationToken.None)) - if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None)) - { - AddFactor(counts, remaining); - return true; - } + // TODO: Benchmark PrimeTester.IsPrimeInternal against Open.Numeric.Primes on the EvenPerfectBitScanner workloads to confirm the faster option for wide composites. + // if (HeuristicPrimeTester.Exclusive.IsPrime(remaining, CancellationToken.None)) - // TODO: Evaluate folding the remainder-tracking mechanism into PollardRho64 so the walk leverages previously computed residues. - ulong factor = PollardRho64(remaining); - if (factor == 0UL || factor == remaining) - { - // Pollard-Rho can still stall on rare stubborn composites during test and benchmark runs, so keep the failure guard in place - // and let the caller fall back to the cached cycle calculator. - return false; - } + // Atomic.Add(ref _tryFactorIntoCountsInternalHits, 1UL); + // Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}"); - // TODO: Investigate computing this quotient via remainder tracking or reciprocal multiplication to avoid the division. - ulong quotient = remaining / factor; - // Pollard-Rho never returns factors <= 1 on the EvenPerfectBitScanner path; keep the old guard documented - // for completeness without branching in the hot path. - // if (factor > 1UL && !TryFactorIntoCountsInternal(factor, counts)) - // { - // return false; - // } + if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None)) + { + AddFactor(counts, remaining); + return true; + } - if (!TryFactorIntoCountsInternal(factor, counts)) - { - return false; - } + // TODO: Evaluate folding the remainder-tracking mechanism into PollardRho64 so the walk leverages previously computed residues. + ulong factor = PollardRho64(remaining); + if (factor == 0UL || factor == remaining) + { + // Pollard-Rho can still stall on rare stubborn composites during test and benchmark runs, so keep the failure guard in place + // and let the caller fall back to the cached cycle calculator. + return false; + } - // Guard the recursive calls to terminate once Pollard-Rho reduces either branch to 1. - if (quotient > 1UL && !TryFactorIntoCountsInternal(quotient, counts)) - { - return false; - } + // TODO: Investigate computing this quotient via remainder tracking or reciprocal multiplication to avoid the division. + ulong quotient = remaining / factor; + // Pollard-Rho never returns factors <= 1 on the EvenPerfectBitScanner path; keep the old guard documented + // for completeness without branching in the hot path. + // if (factor > 1UL && !TryFactorIntoCountsInternal(factor, counts)) + // { + // return false; + // } - return true; - } + if (!TryFactorIntoCountsInternal(factor, counts)) + { + return false; + } + + // Guard the recursive calls to terminate once Pollard-Rho reduces either branch to 1. + if (quotient > 1UL && !TryFactorIntoCountsInternal(quotient, counts)) + { + return false; + } + + return true; + } private static void AddFactor(Dictionary counts, ulong factor) { @@ -928,6 +930,12 @@ public class MersenneDivisorCycles return true; } + // if (!skipPrimeOrderHeuristic) + // { + // Atomic.Add(ref _tryCalculateCycleLengthHeuristicHits, 1UL); + // Console.WriteLine($"MersenneDivisorCycles.CalculateCycleLengthFallback hits {Volatile.Read(ref _tryCalculateCycleLengthHeuristicHits)}"); + // } + // if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None)) if (!skipPrimeOrderHeuristic && PrimeTester.IsPrimeCpu(divisor, CancellationToken.None)) { diff --git a/PerfectNumbers.Core/MersennePrimeFactorTester.cs b/PerfectNumbers.Core/MersennePrimeFactorTester.cs index 0bf2818..6e2cc41 100644 --- a/PerfectNumbers.Core/MersennePrimeFactorTester.cs +++ b/PerfectNumbers.Core/MersennePrimeFactorTester.cs @@ -2,11 +2,15 @@ using System.Collections.Concurrent; using System.Runtime.CompilerServices; using System.Numerics; using UInt128 = System.UInt128; +using ILGPU; namespace PerfectNumbers.Core; public static class MersennePrimeFactorTester { + // private static ulong _isPrimeFactorHits; + // private static ulong _factor64Hits; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsPrimeFactor(ulong p, ulong q, CancellationToken ct) { @@ -25,7 +29,11 @@ public static class MersennePrimeFactorTester return false; } - // if (!HeuristicPrimeTester.Exclusive.IsPrimeCpu(q, ct)) + // if (!HeuristicPrimeTester.Exclusive.IsPrime(q, ct)) + + // Atomic.Add(ref _isPrimeFactorHits, 1UL); + // Console.WriteLine($"MersennePrimeFactorTester.IsPrimeFactor hits {Volatile.Read(ref _isPrimeFactorHits)}"); + if (!PrimeTester.IsPrimeCpu(q, ct)) { return false; @@ -234,7 +242,11 @@ public static class MersennePrimeFactorTester continue; } - // if (HeuristicPrimeTester.Exclusive.IsPrimeCpu(m, ct)) + + // Atomic.Add(ref _factor64Hits, 1UL); + // Console.WriteLine($"MersennePrimeFactorTester.Factor64 hits {Volatile.Read(ref _factor64Hits)}"); + + // if (HeuristicPrimeTester.Exclusive.IsPrime(m, ct)) if (PrimeTester.IsPrimeCpu(m, ct)) { dict[m] = 1; diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 00981e2..9b83a9c 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using ILGPU; using PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core; @@ -1019,6 +1020,10 @@ internal static partial class PrimeOrderCalculator return exponent.Pow2MontgomeryModWindowedCpu(divisorData, keepMontgomery: false) == 1UL; } + // private static ulong _partialFactorHits; + // private static ulong _partialFactorPendingHits; + // private static ulong _partialFactorCofactorHits; + private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config) { if (value <= 1UL) @@ -1070,7 +1075,7 @@ internal static partial class PrimeOrderCalculator // if (!gpuPopulated) // { // throw new InvalidOperationException($"GPU didn't populate factors for {value}"); - // counts.Clear(); + // counts.Clear(); remaining = PopulateSmallPrimeFactorsCpu(value, limit, counts); // } @@ -1123,8 +1128,12 @@ internal static partial class PrimeOrderCalculator // } // bool isPrime = primeTester.HeuristicIsPrimeGpu(composite); - // bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None); - bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None); + // bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None); + + // Atomic.Add(ref _partialFactorHits, 1UL); + // Console.WriteLine($"Partial factor hits {Volatile.Read(ref _partialFactorHits)}"); + + bool isPrime = PrimeTester.IsPrimeGpu(composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); if (isPrime) @@ -1186,10 +1195,14 @@ internal static partial class PrimeOrderCalculator if (!entry.HasKnownPrimality) { // bool isPrime = primeTester.HeuristicIsPrimeGpu(composite); - // bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None); - bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None); + // bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None); + + // Atomic.Add(ref _partialFactorPendingHits, 1UL); + // Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}"); + + bool isPrime = PrimeTester.IsPrimeGpu(composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); - + entry = entry.WithPrimality(isPrime); pending[index] = entry; } @@ -1219,7 +1232,11 @@ internal static partial class PrimeOrderCalculator else { // cofactorIsPrime = primeTester.HeuristicIsPrimeGpu(cofactor); - // cofactorIsPrime = PrimeTester.IsPrimeCpu(cofactor, CancellationToken.None); + // cofactorIsPrime = PrimeTester.IsPrimeInternal(cofactor, CancellationToken.None); + + // Atomic.Add(ref _partialFactorCofactorHits, 1UL); + // Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}"); + cofactorIsPrime = PrimeTester.IsPrimeCpu(cofactor, CancellationToken.None); // cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor); } @@ -1741,6 +1758,9 @@ internal static partial class PrimeOrderCalculator { FactorCompletely(value, counts, knownComposite: false); } + + // private static ulong _factorCompletelyHits; + // private static ulong _factorCompletelyPollardRhoHits; private static void FactorCompletely(ulong value, Dictionary counts, bool knownComposite) { @@ -1749,7 +1769,9 @@ internal static partial class PrimeOrderCalculator return; } - PrimeTester primeTester = PrimeTester.Exclusive; + // Atomic.Add(ref _factorCompletelyHits, 1UL); + // Console.WriteLine($"Factor completely hits {Volatile.Read(ref _factorCompletelyHits)}"); + // PrimeTester primeTester = PrimeTester.Exclusive; // bool isPrime = primeTester.HeuristicIsPrimeGpu(value); // bool isPrime = PrimeTester.IsPrimeCpu(value, CancellationToken.None); @@ -1764,6 +1786,9 @@ internal static partial class PrimeOrderCalculator ulong factor = PollardRhoStrict(value); ulong quotient = value / factor; + // Atomic.Add(ref _factorCompletelyPollardRhoHits, 1UL); + // Console.WriteLine($"Factor completely after PollardRho hits {Volatile.Read(ref _factorCompletelyPollardRhoHits)}"); + // isPrime = primeTester.HeuristicIsPrimeGpu(factor); // isPrime = PrimeTester.IsPrimeCpu(factor, CancellationToken.None); isPrime = PrimeTester.IsPrimeCpu(factor, CancellationToken.None); @@ -1797,7 +1822,11 @@ internal static partial class PrimeOrderCalculator else { // isPrime = primeTester.HeuristicIsPrimeGpu(quotient); - // isPrime = PrimeTester.IsPrimeCpu(quotient, CancellationToken.None); + // isPrime = PrimeTester.IsPrimeInternal(quotient, CancellationToken.None); + + // Atomic.Add(ref _partialFactorCofactorHits, 1UL); + // Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}"); + isPrime = PrimeTester.IsPrimeCpu(quotient, CancellationToken.None); // isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(quotient); }