Merge pull request #173 from mlemanczyk/codex/analyze-and-reduce-gc-cost-in-primeordercalculator

Reduce Pollard Rho stopwatch allocations to lower GC pressure
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-10-15 04:39:59 +02:00
commit a70113a99d
2 files changed
+80 -47

No files matched your search

@@ -603,12 +603,12 @@ internal static partial class PrimeOrderCalculator
if (config.PollardRhoMilliseconds > 0 && pending.Count > 0) if (config.PollardRhoMilliseconds > 0 && pending.Count > 0)
{ {
Stopwatch stopwatch = Stopwatch.StartNew(); long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds);
long budgetTicks = TimeSpan.FromMilliseconds(config.PollardRhoMilliseconds).Ticks;
Stack<UInt128> stack = new(); Stack<UInt128> stack = new();
stack.Push(remaining); stack.Push(remaining);
pending.Clear(); pending.Clear();
long timestamp = 0L; // reused for deadline checks.
while (stack.Count > 0) while (stack.Count > 0)
{ {
UInt128 composite = stack.Pop(); UInt128 composite = stack.Pop();
@@ -623,13 +623,14 @@ internal static partial class PrimeOrderCalculator
continue; continue;
} }
if (stopwatch.ElapsedTicks > budgetTicks) timestamp = Stopwatch.GetTimestamp();
if (timestamp > deadlineTimestamp)
{ {
pending.Add(composite); pending.Add(composite);
continue; continue;
} }
if (!TryPollardRhoWide(composite, stopwatch, budgetTicks, out UInt128 factor)) if (!TryPollardRhoWide(composite, deadlineTimestamp, out UInt128 factor))
{ {
pending.Add(composite); pending.Add(composite);
continue; continue;
@@ -728,7 +729,7 @@ internal static partial class PrimeOrderCalculator
return remaining; 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; factor = UInt128.Zero;
if ((n & UInt128.One) == UInt128.Zero) if ((n & UInt128.One) == UInt128.Zero)
@@ -740,10 +741,12 @@ internal static partial class PrimeOrderCalculator
UInt128 c = UInt128.One; UInt128 c = UInt128.One;
UInt128 x = (UInt128)2UL; UInt128 x = (UInt128)2UL;
UInt128 y = x; UInt128 y = x;
long timestamp = 0L; // reused for deadline checks.
while (true) while (true)
{ {
if (stopwatch.ElapsedTicks > budgetTicks) timestamp = Stopwatch.GetTimestamp();
if (timestamp > deadlineTimestamp)
{ {
return false; return false;
} }
+71 -41
View File
@@ -873,12 +873,12 @@ internal static partial class PrimeOrderCalculator
if (config.PollardRhoMilliseconds > 0 && pending.Count > 0) if (config.PollardRhoMilliseconds > 0 && pending.Count > 0)
{ {
Stopwatch stopwatch = Stopwatch.StartNew(); long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds);
long budgetTicks = TimeSpan.FromMilliseconds(config.PollardRhoMilliseconds).Ticks;
compositeStack = AcquireCompositeStack(Math.Max(pending.Count * 2, 4)); compositeStack = AcquireCompositeStack(Math.Max(pending.Count * 2, 4));
compositeStack.Push(remaining); compositeStack.Push(remaining);
pending.Clear(); pending.Clear();
long timestamp = 0L; // reused for deadline checks.
while (compositeStack.Count > 0) while (compositeStack.Count > 0)
{ {
ulong composite = compositeStack.Pop(); ulong composite = compositeStack.Pop();
@@ -894,13 +894,14 @@ internal static partial class PrimeOrderCalculator
continue; continue;
} }
if (stopwatch.ElapsedTicks > budgetTicks) timestamp = Stopwatch.GetTimestamp();
if (timestamp > deadlineTimestamp)
{ {
pending.Add(composite); pending.Add(composite);
continue; continue;
} }
if (!TryPollardRho(composite, stopwatch, budgetTicks, out ulong factor)) if (!TryPollardRho(composite, deadlineTimestamp, out ulong factor))
{ {
pending.Add(composite); pending.Add(composite);
continue; continue;
@@ -1143,48 +1144,77 @@ internal static partial class PrimeOrderCalculator
return true; return true;
} }
private static bool TryPollardRho(ulong n, Stopwatch stopwatch, long budgetTicks, out ulong factor) private static long CreateDeadlineTimestamp(int milliseconds)
{ {
factor = 0UL; if (milliseconds <= 0)
if ((n & 1UL) == 0UL) {
{ return long.MaxValue;
factor = 2UL; }
return true;
}
while (true) long stopwatchTicks = ConvertMillisecondsToStopwatchTicks(milliseconds);
{ long startTimestamp = Stopwatch.GetTimestamp();
if (stopwatch.ElapsedTicks > budgetTicks) long deadline = startTimestamp + stopwatchTicks;
{ if (deadline < startTimestamp)
return false; {
} return long.MaxValue;
}
ulong c = (DeterministicRandom.NextUInt64() % (n - 1UL)) + 1UL; return deadline;
ulong x = (DeterministicRandom.NextUInt64() % (n - 2UL)) + 2UL; }
ulong y = x;
ulong d = 1UL;
while (d == 1UL) private static long ConvertMillisecondsToStopwatchTicks(int milliseconds)
{ {
if (stopwatch.ElapsedTicks > budgetTicks) long frequency = Stopwatch.Frequency;
{ long baseTicks = (frequency / 1000L) * milliseconds;
return false; long remainder = (frequency % 1000L) * milliseconds;
} return baseTicks + (remainder / 1000L);
}
x = AdvancePolynomial(x, c, n); private static bool TryPollardRho(ulong n, long deadlineTimestamp, out ulong factor)
y = AdvancePolynomial(y, c, n); {
y = AdvancePolynomial(y, c, n); factor = 0UL;
ulong diff = x > y ? x - y : y - x; if ((n & 1UL) == 0UL)
d = BinaryGcd(diff, n); {
} factor = 2UL;
return true;
}
if (d != n) long timestamp = 0L; // reused for deadline checks.
{ while (true)
factor = d; {
return 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus) private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus)