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;
|
ulong remainder = currentP % 6UL;
|
||||||
bool startPrimeProvided = _cliArguments.StartPrimeProvided;
|
bool startPrimeProvided = _cliArguments.StartPrimeProvided;
|
||||||
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
|
int threadCount = Math.Max(1, _cliArguments.ThreadCount);
|
||||||
|
UnboundedTaskScheduler.ConfigureThreadCount(threadCount);
|
||||||
int blockSize = Math.Max(1, _cliArguments.BlockSize);
|
int blockSize = Math.Max(1, _cliArguments.BlockSize);
|
||||||
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
|
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
|
||||||
int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch);
|
int gpuPrimeBatch = Math.Max(1, _cliArguments.GpuPrimeBatch);
|
||||||
|
|||||||
@@ -10,10 +10,35 @@ namespace PerfectNumbers.Core;
|
|||||||
internal static partial class PrimeOrderCalculator
|
internal static partial class PrimeOrderCalculator
|
||||||
{
|
{
|
||||||
|
|
||||||
internal readonly struct PendingEntry(ulong value, bool knownComposite)
|
internal readonly struct PendingEntry
|
||||||
{
|
{
|
||||||
public readonly ulong Value = value;
|
public readonly ulong Value;
|
||||||
public readonly bool KnownComposite = knownComposite;
|
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)
|
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;
|
return isPrime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config)
|
private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config)
|
||||||
{
|
{
|
||||||
if (value <= 1UL)
|
if (value <= 1UL)
|
||||||
@@ -756,7 +782,6 @@ internal static partial class PrimeOrderCalculator
|
|||||||
List<PendingEntry> pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2);
|
List<PendingEntry> pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2);
|
||||||
Stack<ulong>? compositeStack = null;
|
Stack<ulong>? compositeStack = null;
|
||||||
PartialFactorResult result;
|
PartialFactorResult result;
|
||||||
|
|
||||||
if (!gpuFactored)
|
if (!gpuFactored)
|
||||||
{
|
{
|
||||||
// We don't need to worry about leftovers, because we always use indexes within the calculated counts
|
// 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;
|
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))
|
if (!TryPollardRho(composite, deadlineTimestamp, out ulong factor))
|
||||||
{
|
{
|
||||||
pending.Add(new PendingEntry(composite, knownComposite: true));
|
pending.Add(new PendingEntry(composite, knownComposite: true));
|
||||||
@@ -840,7 +873,10 @@ internal static partial class PrimeOrderCalculator
|
|||||||
compositeStack.Push(quotient);
|
compositeStack.Push(quotient);
|
||||||
}
|
}
|
||||||
|
|
||||||
pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp;
|
if (!pollardRhoDeadlineReached)
|
||||||
|
{
|
||||||
|
pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollardRhoDeadlineReached)
|
if (pollardRhoDeadlineReached)
|
||||||
@@ -868,9 +904,14 @@ internal static partial class PrimeOrderCalculator
|
|||||||
continue;
|
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);
|
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.UlongPool.Return(primeSlotsArray);
|
||||||
// ThreadStaticPools.IntPool.Return(exponentSlotsArray);
|
// ThreadStaticPools.IntPool.Return(exponentSlotsArray);
|
||||||
|
|
||||||
|
|
||||||
if (counts is not null)
|
if (counts is not null)
|
||||||
{
|
{
|
||||||
ThreadStaticPools.ReturnUlongIntDictionary(counts);
|
ThreadStaticPools.ReturnUlongIntDictionary(counts);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
internal static partial class PrimeOrderCalculator
|
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)
|
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 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 uint SmallFactorLimit = smallFactorLimit;
|
||||||
public readonly int PollardRhoMilliseconds = pollardRhoMilliseconds;
|
public readonly int PollardRhoMilliseconds = pollardRhoMilliseconds;
|
||||||
public readonly int MaxPowChecks = maxPowChecks;
|
public readonly int MaxPowChecks = maxPowChecks;
|
||||||
|
|||||||
@@ -6,13 +6,31 @@ namespace PerfectNumbers.Core;
|
|||||||
|
|
||||||
public sealed class UnboundedTaskScheduler : TaskScheduler
|
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;
|
public override int MaximumConcurrencyLevel => int.MaxValue;
|
||||||
@@ -22,6 +40,19 @@ public sealed class UnboundedTaskScheduler : TaskScheduler
|
|||||||
_threadPool.Queue(task);
|
_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)
|
private void ExecuteTask(Task task)
|
||||||
{
|
{
|
||||||
TryExecuteTask(task);
|
TryExecuteTask(task);
|
||||||
@@ -36,4 +67,5 @@ public sealed class UnboundedTaskScheduler : TaskScheduler
|
|||||||
{
|
{
|
||||||
return _threadPool.GetScheduledTasks();
|
return _threadPool.GetScheduledTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user