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:
4 files changed
+107
-12
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user