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 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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
@@ -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)
|
private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config)
|
||||||
{
|
{
|
||||||
if (value <= 1UL)
|
if (value <= 1UL)
|
||||||
@@ -795,9 +781,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;
|
||||||
Dictionary<ulong, bool>? primalityCache = null;
|
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@@ -862,7 +845,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// continue;
|
// continue;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
bool isPrime = EvaluatePrimalityPartialFactor(ref primalityCache, composite);
|
bool isPrime = GetOrComputePrimality(composite);
|
||||||
|
|
||||||
if (isPrime)
|
if (isPrime)
|
||||||
{
|
{
|
||||||
@@ -920,9 +903,14 @@ internal static partial class PrimeOrderCalculator
|
|||||||
continue;
|
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);
|
AddFactorToCollector(ref useDictionary, ref counts, primeSlots, exponentSlots, ref factorCount, composite, 1);
|
||||||
}
|
}
|
||||||
@@ -946,7 +934,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cofactorIsPrime = EvaluatePrimalityPartialFactor(ref primalityCache, cofactor);
|
cofactorIsPrime = GetOrComputePrimality(cofactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
|
ArrayPool<FactorEntry> pool = ThreadStaticPools.FactorEntryPool;
|
||||||
@@ -1021,11 +1009,6 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// ThreadStaticPools.IntPool.Return(exponentSlotsArray);
|
// ThreadStaticPools.IntPool.Return(exponentSlotsArray);
|
||||||
|
|
||||||
|
|
||||||
if (primalityCache is not null)
|
|
||||||
{
|
|
||||||
ThreadStaticPools.ReturnUlongBoolDictionary(primalityCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counts is not null)
|
if (counts is not null)
|
||||||
{
|
{
|
||||||
ThreadStaticPools.ReturnUlongIntDictionary(counts);
|
ThreadStaticPools.ReturnUlongIntDictionary(counts);
|
||||||
|
|||||||
@@ -278,65 +278,6 @@ namespace PerfectNumbers.Core
|
|||||||
pool.Add(dictionary);
|
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]
|
[ThreadStatic]
|
||||||
private static Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? _mersenneFactorCacheDictionary;
|
private static Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? _mersenneFactorCacheDictionary;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user