Persist partial factor primality in pending entries
This commit is contained in:
1 parent
bad5c28fdf
commit
752c07c57e
2 files changed
+36
-112
No files matched your search
@@ -10,10 +10,34 @@ 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)
|
||||
{
|
||||
return new PendingEntry(Value, KnownComposite, true, isPrime);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong CalculateInternal(ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
|
||||
@@ -721,44 +745,6 @@ internal static partial class PrimeOrderCalculator
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool EvaluatePrimalityPartialFactor(ref Dictionary<ulong, bool>? localCache, ulong candidate)
|
||||
{
|
||||
if (TryGetCachedPrimality(candidate, out bool cached))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
ulong globalCacheLimit = (ulong)PerfectNumberConstants.MaxQForDivisorCycles;
|
||||
bool bypassesGlobalCache = candidate > globalCacheLimit;
|
||||
if (bypassesGlobalCache)
|
||||
{
|
||||
Dictionary<ulong, bool>? cache = localCache;
|
||||
if (cache is not null && cache.TryGetValue(candidate, out bool cachedValue))
|
||||
{
|
||||
return cachedValue;
|
||||
}
|
||||
}
|
||||
|
||||
bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(candidate);
|
||||
CachePrimalityResult(candidate, isPrime);
|
||||
|
||||
if (bypassesGlobalCache)
|
||||
{
|
||||
Dictionary<ulong, bool>? cache = localCache;
|
||||
if (cache is null)
|
||||
{
|
||||
cache = ThreadStaticPools.RentUlongBoolDictionary(4);
|
||||
cache.Clear();
|
||||
localCache = cache;
|
||||
}
|
||||
|
||||
cache[candidate] = isPrime;
|
||||
}
|
||||
|
||||
return isPrime;
|
||||
}
|
||||
|
||||
private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config)
|
||||
{
|
||||
if (value <= 1UL)
|
||||
@@ -795,9 +781,6 @@ internal static partial class PrimeOrderCalculator
|
||||
List<PendingEntry> pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2);
|
||||
Stack<ulong>? compositeStack = null;
|
||||
PartialFactorResult result;
|
||||
Dictionary<ulong, bool>? primalityCache = null;
|
||||
|
||||
|
||||
if (!gpuFactored)
|
||||
{
|
||||
// We don't need to worry about leftovers, because we always use indexes within the calculated counts
|
||||
@@ -862,7 +845,7 @@ internal static partial class PrimeOrderCalculator
|
||||
// continue;
|
||||
// }
|
||||
|
||||
bool isPrime = EvaluatePrimalityPartialFactor(ref primalityCache, composite);
|
||||
bool isPrime = GetOrComputePrimality(composite);
|
||||
|
||||
if (isPrime)
|
||||
{
|
||||
@@ -920,9 +903,14 @@ internal static partial class PrimeOrderCalculator
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isPrime = EvaluatePrimalityPartialFactor(ref primalityCache, 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);
|
||||
}
|
||||
@@ -946,7 +934,7 @@ internal static partial class PrimeOrderCalculator
|
||||
}
|
||||
else
|
||||
{
|
||||
cofactorIsPrime = EvaluatePrimalityPartialFactor(ref primalityCache, cofactor);
|
||||
cofactorIsPrime = GetOrComputePrimality(cofactor);
|
||||
}
|
||||
|
||||
ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
|
||||
@@ -1021,11 +1009,6 @@ internal static partial class PrimeOrderCalculator
|
||||
// ThreadStaticPools.IntPool.Return(exponentSlotsArray);
|
||||
|
||||
|
||||
if (primalityCache is not null)
|
||||
{
|
||||
ThreadStaticPools.ReturnUlongBoolDictionary(primalityCache);
|
||||
}
|
||||
|
||||
if (counts is not null)
|
||||
{
|
||||
ThreadStaticPools.ReturnUlongIntDictionary(counts);
|
||||
|
||||
@@ -278,65 +278,6 @@ namespace PerfectNumbers.Core
|
||||
pool.Add(dictionary);
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static Stack<Dictionary<ulong, bool>>? _ulongBoolDictionaryPool;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Dictionary<ulong, bool> RentUlongBoolDictionary(int capacityHint)
|
||||
{
|
||||
Stack<Dictionary<ulong, bool>>? pool = _ulongBoolDictionaryPool;
|
||||
if (pool is null)
|
||||
{
|
||||
pool = new Stack<Dictionary<ulong, bool>>(4);
|
||||
_ulongBoolDictionaryPool = pool;
|
||||
}
|
||||
|
||||
// Keep the previous null-check implementation here for reference.
|
||||
// Do not uncomment this branch; the pool is always initialized above.
|
||||
// if (pool is not null && pool.Count > 0)
|
||||
// {
|
||||
// Dictionary<ulong, bool> dictionary = pool.Pop();
|
||||
// if (dictionary.Count < capacityHint)
|
||||
// {
|
||||
// dictionary.EnsureCapacity(capacityHint);
|
||||
// }
|
||||
//
|
||||
// return dictionary;
|
||||
// }
|
||||
|
||||
if (pool.Count > 0)
|
||||
{
|
||||
Dictionary<ulong, bool> dictionary = pool.Pop();
|
||||
// Callers clear dictionaries after renting to avoid duplicate work.
|
||||
// if (dictionary.Count > 0)
|
||||
// {
|
||||
// dictionary.Clear();
|
||||
// }
|
||||
if (dictionary.Count < capacityHint)
|
||||
{
|
||||
dictionary.EnsureCapacity(capacityHint);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
return new Dictionary<ulong, bool>(capacityHint);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ReturnUlongBoolDictionary(Dictionary<ulong, bool> dictionary)
|
||||
{
|
||||
Stack<Dictionary<ulong, bool>>? pool = _ulongBoolDictionaryPool;
|
||||
if (pool is null)
|
||||
{
|
||||
pool = new Stack<Dictionary<ulong, bool>>(4);
|
||||
_ulongBoolDictionaryPool = pool;
|
||||
}
|
||||
|
||||
// Do not clear pooled dictionaries here; callers must clear on rent.
|
||||
pool.Push(dictionary);
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? _mersenneFactorCacheDictionary;
|
||||
|
||||
|
||||
Reference in new issue
Block a user