Merge pull request #210 from mlemanczyk/codex/optimize-partialfactor-method-for-isprime-caching

Cache composite primality results during partial factorization
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-10-20 01:07:24 +02:00
commit bfc6605e97
4 files changed
+107 -12

No files matched your search

+1
View File
@@ -55,6 +55,7 @@ internal static class Program
ulong remainder = currentP % 6UL;
bool startPrimeProvided = _cliArguments.StartPrimeProvided;
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
UnboundedTaskScheduler.ConfigureThreadCount(threadCount);
int blockSize = Math.Max(1, _cliArguments.BlockSize);
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch);
@@ -10,10 +10,35 @@ namespace PerfectNumbers.Core;
internal static partial class PrimeOrderCalculator
{
internal readonly struct PendingEntry(ulong value, bool knownComposite)
internal readonly struct PendingEntry
{
public readonly ulong Value = value;
public readonly bool KnownComposite = knownComposite;
public readonly ulong Value;
public readonly bool KnownComposite;
public readonly bool HasKnownPrimality;
public readonly bool IsPrime;
public PendingEntry(ulong value, bool knownComposite)
{
Value = value;
KnownComposite = knownComposite;
HasKnownPrimality = knownComposite;
IsPrime = false;
}
private PendingEntry(ulong value, bool knownComposite, bool hasKnownPrimality, bool isPrime)
{
Value = value;
KnownComposite = knownComposite;
HasKnownPrimality = hasKnownPrimality;
IsPrime = isPrime;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public PendingEntry WithPrimality(bool isPrime)
{
bool knownComposite = KnownComposite || !isPrime;
return new PendingEntry(Value, knownComposite, true, isPrime);
}
}
private static ulong CalculateInternal(ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
@@ -720,6 +745,7 @@ internal static partial class PrimeOrderCalculator
return isPrime;
}
private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config)
{
if (value <= 1UL)
@@ -756,7 +782,6 @@ internal static partial class PrimeOrderCalculator
List<PendingEntry> pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2);
Stack<ulong>? compositeStack = null;
PartialFactorResult result;
if (!gpuFactored)
{
// We don't need to worry about leftovers, because we always use indexes within the calculated counts
@@ -829,6 +854,14 @@ internal static partial class PrimeOrderCalculator
continue;
}
long currentTimestamp = Stopwatch.GetTimestamp();
if (currentTimestamp > deadlineTimestamp)
{
pollardRhoDeadlineReached = true;
pending.Add(new PendingEntry(composite, knownComposite: true));
continue;
}
if (!TryPollardRho(composite, deadlineTimestamp, out ulong factor))
{
pending.Add(new PendingEntry(composite, knownComposite: true));
@@ -840,7 +873,10 @@ internal static partial class PrimeOrderCalculator
compositeStack.Push(quotient);
}
pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp;
if (!pollardRhoDeadlineReached)
{
pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp;
}
}
if (pollardRhoDeadlineReached)
@@ -868,9 +904,14 @@ internal static partial class PrimeOrderCalculator
continue;
}
bool isPrime = GetOrComputePrimality(composite);
if (!entry.HasKnownPrimality)
{
bool isPrime = GetOrComputePrimality(composite);
entry = entry.WithPrimality(isPrime);
pending[index] = entry;
}
if (isPrime)
if (entry.IsPrime)
{
AddFactorToCollector(ref useDictionary, ref counts, primeSlots, exponentSlots, ref factorCount, composite, 1);
}
@@ -968,6 +1009,7 @@ internal static partial class PrimeOrderCalculator
// ThreadStaticPools.UlongPool.Return(primeSlotsArray);
// ThreadStaticPools.IntPool.Return(exponentSlotsArray);
if (counts is not null)
{
ThreadStaticPools.ReturnUlongIntDictionary(counts);
+21 -1
View File
@@ -1,3 +1,5 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core;
internal static partial class PrimeOrderCalculator
@@ -10,9 +12,27 @@ internal static partial class PrimeOrderCalculator
internal readonly struct PrimeOrderSearchConfig(uint smallFactorLimit, int pollardRhoMilliseconds, int maxPowChecks, PrimeOrderMode mode)
{
public static readonly PrimeOrderSearchConfig HeuristicDefault = new(smallFactorLimit: 100_000, pollardRhoMilliseconds: 24, maxPowChecks: 24, PrimeOrderMode.Heuristic);
private static PrimeOrderSearchConfig _heuristicDefault = CreateHeuristicDefault(UnboundedTaskScheduler.ConfiguredThreadCount);
public static PrimeOrderSearchConfig HeuristicDefault
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _heuristicDefault;
}
public static readonly PrimeOrderSearchConfig StrictDefault = new(smallFactorLimit: 1_000_000, pollardRhoMilliseconds: 0, maxPowChecks: 0, PrimeOrderMode.Strict);
public static void ConfigureHeuristicDefault(int threadCount)
{
_heuristicDefault = CreateHeuristicDefault(threadCount);
}
public static PrimeOrderSearchConfig CreateHeuristicDefault(int threadCount)
{
int normalizedThreadCount = threadCount < 1 ? 1 : threadCount;
return new(smallFactorLimit: 100_000, pollardRhoMilliseconds: normalizedThreadCount * 24, maxPowChecks: 24, PrimeOrderMode.Heuristic);
}
public readonly uint SmallFactorLimit = smallFactorLimit;
public readonly int PollardRhoMilliseconds = pollardRhoMilliseconds;
public readonly int MaxPowChecks = maxPowChecks;
+36 -4
View File
@@ -6,13 +6,31 @@ namespace PerfectNumbers.Core;
public sealed class UnboundedTaskScheduler : TaskScheduler
{
public static UnboundedTaskScheduler Instance { get; } = new();
private static UnboundedTaskScheduler? _instance;
private static int _configuredThreadCount = Environment.ProcessorCount;
private readonly TaskThreadPool _threadPool;
public static UnboundedTaskScheduler Instance => _instance ??= new UnboundedTaskScheduler(_configuredThreadCount);
private UnboundedTaskScheduler()
public static int ConfiguredThreadCount => _configuredThreadCount;
private TaskThreadPool _threadPool;
private UnboundedTaskScheduler(int threadCount)
{
_threadPool = new TaskThreadPool(10_340, ExecuteTask);
_threadPool = new TaskThreadPool(threadCount, ExecuteTask);
}
public static void ConfigureThreadCount(int threadCount)
{
_configuredThreadCount = threadCount;
PrimeOrderCalculator.PrimeOrderSearchConfig.ConfigureHeuristicDefault(threadCount);
// On EvenPerfectBitScanner's execution path the scheduler instance is created after configuration,
// so updating an existing pool would never run. Leave the update path disabled for now.
// if (_instance is not null)
// {
// _instance.UpdateThreadPool(threadCount);
// }
}
public override int MaximumConcurrencyLevel => int.MaxValue;
@@ -22,6 +40,19 @@ public sealed class UnboundedTaskScheduler : TaskScheduler
_threadPool.Queue(task);
}
private void UpdateThreadPool(int threadCount)
{
if (_threadPool.ThreadCount == threadCount)
{
return;
}
TaskThreadPool newPool = new(threadCount, ExecuteTask);
TaskThreadPool oldPool = _threadPool;
_threadPool = newPool;
oldPool.Dispose();
}
private void ExecuteTask(Task task)
{
TryExecuteTask(task);
@@ -36,4 +67,5 @@ public sealed class UnboundedTaskScheduler : TaskScheduler
{
return _threadPool.GetScheduledTasks();
}
}