From 0b831b5a2304074a722fc820e6fdf2ab1bfb7706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Wed, 15 Oct 2025 04:39:46 +0200 Subject: [PATCH] Reduce PartialFactor GC pressure --- .../PrimeOrderCalculator.Wide.cs | 15 ++- PerfectNumbers.Core/PrimeOrderCalculator.cs | 112 +++++++++++------- 2 files changed, 80 insertions(+), 47 deletions(-) diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs index b1c89c9..4850853 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs @@ -603,12 +603,12 @@ internal static partial class PrimeOrderCalculator if (config.PollardRhoMilliseconds > 0 && pending.Count > 0) { - Stopwatch stopwatch = Stopwatch.StartNew(); - long budgetTicks = TimeSpan.FromMilliseconds(config.PollardRhoMilliseconds).Ticks; + long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds); Stack stack = new(); stack.Push(remaining); pending.Clear(); + long timestamp = 0L; // reused for deadline checks. while (stack.Count > 0) { UInt128 composite = stack.Pop(); @@ -623,13 +623,14 @@ internal static partial class PrimeOrderCalculator continue; } - if (stopwatch.ElapsedTicks > budgetTicks) + timestamp = Stopwatch.GetTimestamp(); + if (timestamp > deadlineTimestamp) { pending.Add(composite); continue; } - if (!TryPollardRhoWide(composite, stopwatch, budgetTicks, out UInt128 factor)) + if (!TryPollardRhoWide(composite, deadlineTimestamp, out UInt128 factor)) { pending.Add(composite); continue; @@ -728,7 +729,7 @@ internal static partial class PrimeOrderCalculator return remaining; } - private static bool TryPollardRhoWide(in UInt128 n, Stopwatch stopwatch, long budgetTicks, out UInt128 factor) + private static bool TryPollardRhoWide(in UInt128 n, long deadlineTimestamp, out UInt128 factor) { factor = UInt128.Zero; if ((n & UInt128.One) == UInt128.Zero) @@ -740,10 +741,12 @@ internal static partial class PrimeOrderCalculator UInt128 c = UInt128.One; UInt128 x = (UInt128)2UL; UInt128 y = x; + long timestamp = 0L; // reused for deadline checks. while (true) { - if (stopwatch.ElapsedTicks > budgetTicks) + timestamp = Stopwatch.GetTimestamp(); + if (timestamp > deadlineTimestamp) { return false; } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.cs b/PerfectNumbers.Core/PrimeOrderCalculator.cs index 7008dad..39d710c 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.cs @@ -873,12 +873,12 @@ internal static partial class PrimeOrderCalculator if (config.PollardRhoMilliseconds > 0 && pending.Count > 0) { - Stopwatch stopwatch = Stopwatch.StartNew(); - long budgetTicks = TimeSpan.FromMilliseconds(config.PollardRhoMilliseconds).Ticks; + long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds); compositeStack = AcquireCompositeStack(Math.Max(pending.Count * 2, 4)); compositeStack.Push(remaining); pending.Clear(); + long timestamp = 0L; // reused for deadline checks. while (compositeStack.Count > 0) { ulong composite = compositeStack.Pop(); @@ -894,13 +894,14 @@ internal static partial class PrimeOrderCalculator continue; } - if (stopwatch.ElapsedTicks > budgetTicks) + timestamp = Stopwatch.GetTimestamp(); + if (timestamp > deadlineTimestamp) { pending.Add(composite); continue; } - if (!TryPollardRho(composite, stopwatch, budgetTicks, out ulong factor)) + if (!TryPollardRho(composite, deadlineTimestamp, out ulong factor)) { pending.Add(composite); continue; @@ -1143,48 +1144,77 @@ internal static partial class PrimeOrderCalculator return true; } - private static bool TryPollardRho(ulong n, Stopwatch stopwatch, long budgetTicks, out ulong factor) - { - factor = 0UL; - if ((n & 1UL) == 0UL) - { - factor = 2UL; - return true; - } + private static long CreateDeadlineTimestamp(int milliseconds) + { + if (milliseconds <= 0) + { + return long.MaxValue; + } - while (true) - { - if (stopwatch.ElapsedTicks > budgetTicks) - { - return false; - } + long stopwatchTicks = ConvertMillisecondsToStopwatchTicks(milliseconds); + long startTimestamp = Stopwatch.GetTimestamp(); + long deadline = startTimestamp + stopwatchTicks; + if (deadline < startTimestamp) + { + return long.MaxValue; + } - ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL; - ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL; - ulong y = x; - ulong d = 1UL; + return deadline; + } - while (d == 1UL) - { - if (stopwatch.ElapsedTicks > budgetTicks) - { - return false; - } + private static long ConvertMillisecondsToStopwatchTicks(int milliseconds) + { + long frequency = Stopwatch.Frequency; + long baseTicks = (frequency / 1000L) * milliseconds; + long remainder = (frequency % 1000L) * milliseconds; + return baseTicks + (remainder / 1000L); + } - x = AdvancePolynomial(x, c, n); - y = AdvancePolynomial(y, c, n); - y = AdvancePolynomial(y, c, n); - ulong diff = x > y ? x - y : y - x; - d = BinaryGcd(diff, n); - } + private static bool TryPollardRho(ulong n, long deadlineTimestamp, out ulong factor) + { + factor = 0UL; + if ((n & 1UL) == 0UL) + { + factor = 2UL; + return true; + } - if (d != n) - { - factor = d; - return true; - } - } - } + long timestamp = 0L; // reused for deadline checks. + while (true) + { + timestamp = Stopwatch.GetTimestamp(); + if (timestamp > deadlineTimestamp) + { + return false; + } + + ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL; + ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL; + ulong y = x; + ulong d = 1UL; + + while (d == 1UL) + { + timestamp = Stopwatch.GetTimestamp(); + if (timestamp > deadlineTimestamp) + { + return false; + } + + x = AdvancePolynomial(x, c, n); + y = AdvancePolynomial(y, c, n); + y = AdvancePolynomial(y, c, n); + ulong diff = x > y ? x - y : y - x; + d = BinaryGcd(diff, n); + } + + if (d != n) + { + factor = d; + return true; + } + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus)