Remove per-thread factorization cache
This commit is contained in:
1 parent
20f30dc182
commit
28ff9292b8
5 files changed
+17
-248
No files matched your search
@@ -1,30 +0,0 @@
|
||||
namespace PerfectNumbers.Core.Cpu;
|
||||
|
||||
internal struct FactorCacheLease
|
||||
{
|
||||
private Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? _cache;
|
||||
|
||||
public void EnsureInitialized(ref Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? cache)
|
||||
{
|
||||
Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? current = cache;
|
||||
if (current is null)
|
||||
{
|
||||
current = ThreadStaticPools.RentMersenneFactorCacheDictionary();
|
||||
cache = current;
|
||||
}
|
||||
|
||||
_cache = current;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? cache = _cache;
|
||||
if (cache is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadStaticPools.ReturnMersenneFactorCacheDictionary(cache);
|
||||
_cache = null;
|
||||
}
|
||||
}
|
||||
@@ -149,9 +149,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
return false;
|
||||
}
|
||||
|
||||
FactorCacheLease factorCacheLease = new FactorCacheLease();
|
||||
Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? factorCache = null;
|
||||
|
||||
// Intentionally recomputes factorizations without a per-thread cache.
|
||||
// The previous factor cache recorded virtually no hits and only slowed down the scan.
|
||||
DivisorCycleCache cycleCache = DivisorCycleCache.Shared;
|
||||
|
||||
ulong stepHigh = step.High;
|
||||
@@ -196,16 +195,10 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
// }
|
||||
// else
|
||||
{
|
||||
if (factorCache is null)
|
||||
{
|
||||
factorCacheLease.EnsureInitialized(ref factorCache);
|
||||
}
|
||||
|
||||
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponent(
|
||||
candidate,
|
||||
prime,
|
||||
divisorData,
|
||||
factorCache,
|
||||
out ulong computedCycle,
|
||||
out bool primeOrderFailed) || computedCycle == 0UL)
|
||||
{
|
||||
@@ -226,8 +219,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
{
|
||||
// A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides
|
||||
// the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p.
|
||||
factorCacheLease.Dispose();
|
||||
factorCache = null;
|
||||
divisor.High = 0UL;
|
||||
divisor.Low = 0UL;
|
||||
step.High = 0UL;
|
||||
@@ -253,9 +244,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
remainder11 = AddMod(remainder11, step11, (byte)11);
|
||||
}
|
||||
|
||||
factorCacheLease.Dispose();
|
||||
factorCache = null; // The pooled dictionary was returned to the thread-static slot.
|
||||
|
||||
processedAll = divisor.CompareTo(limit) > 0;
|
||||
divisor.High = 0UL;
|
||||
divisor.Low = 0UL;
|
||||
|
||||
@@ -52,12 +52,6 @@ public class MersenneDivisorCycles
|
||||
}
|
||||
|
||||
public static bool CycleEqualsExponent(ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent)
|
||||
{
|
||||
Dictionary<ulong, FactorCacheEntry>? cache = null;
|
||||
return CycleEqualsExponent(divisor, exponent, divisorData, ref cache);
|
||||
}
|
||||
|
||||
public static bool CycleEqualsExponent(ulong divisor, ulong exponent, in MontgomeryDivisorData divisorData, ref Dictionary<ulong, FactorCacheEntry>? factorCache)
|
||||
{
|
||||
if (CycleEqualsExponentForMersenneCandidate(divisor, divisorData, exponent))
|
||||
{
|
||||
@@ -87,14 +81,7 @@ public class MersenneDivisorCycles
|
||||
return heuristicCycle == exponent;
|
||||
}
|
||||
|
||||
Dictionary<ulong, FactorCacheEntry>? cache = factorCache;
|
||||
if (cache is null)
|
||||
{
|
||||
cache = new Dictionary<ulong, FactorCacheEntry>(8);
|
||||
factorCache = cache;
|
||||
}
|
||||
|
||||
if (TryCalculateCycleLengthForExponent(divisor, exponent, divisorData, cache, out ulong cycleLength, out bool _) && cycleLength != 0UL)
|
||||
if (TryCalculateCycleLengthForExponent(divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL)
|
||||
{
|
||||
return cycleLength == exponent;
|
||||
}
|
||||
@@ -262,7 +249,6 @@ public class MersenneDivisorCycles
|
||||
ulong divisor,
|
||||
ulong exponent,
|
||||
in MontgomeryDivisorData divisorData,
|
||||
Dictionary<ulong, FactorCacheEntry>? factorCache,
|
||||
out ulong cycleLength,
|
||||
out bool primeOrderFailed)
|
||||
{
|
||||
@@ -326,8 +312,8 @@ public class MersenneDivisorCycles
|
||||
factorCounts[2UL] = twoCount;
|
||||
}
|
||||
|
||||
bool exponentAccumulated = AccumulateFactors(exponent, factorCache, factorCounts, cacheResult: true);
|
||||
bool factorsAccumulated = exponentAccumulated && AccumulateFactors(k, factorCache, factorCounts, cacheResult: false);
|
||||
bool exponentAccumulated = AccumulateFactors(exponent, factorCounts);
|
||||
bool factorsAccumulated = exponentAccumulated && AccumulateFactors(k, factorCounts);
|
||||
|
||||
if (factorsAccumulated)
|
||||
{
|
||||
@@ -342,98 +328,29 @@ public class MersenneDivisorCycles
|
||||
|
||||
private static bool AccumulateFactors(
|
||||
ulong value,
|
||||
Dictionary<ulong, FactorCacheEntry>? cache,
|
||||
Dictionary<ulong, int> counts,
|
||||
bool cacheResult)
|
||||
Dictionary<ulong, int> counts)
|
||||
{
|
||||
if (value <= 1UL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryGetFactorization(value, cache, cacheResult, out FactorCacheEntry? factorization, out bool releaseAfterUse) || factorization is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FactorCacheEntry actualFactorization = factorization;
|
||||
KeyValuePair<ulong, int>[]? pairs = actualFactorization.Pairs;
|
||||
if (pairs is not null)
|
||||
{
|
||||
int length = actualFactorization.Count;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
KeyValuePair<ulong, int> pair = pairs[i];
|
||||
AddFactor(counts, pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (releaseAfterUse)
|
||||
{
|
||||
actualFactorization.ReturnToPool();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryGetFactorization(
|
||||
ulong value,
|
||||
Dictionary<ulong, FactorCacheEntry>? cache,
|
||||
bool cacheResult,
|
||||
out FactorCacheEntry? factorization,
|
||||
out bool releaseAfterUse)
|
||||
{
|
||||
releaseAfterUse = false;
|
||||
|
||||
if (cache is not null && cache.TryGetValue(value, out FactorCacheEntry? cachedEntry))
|
||||
{
|
||||
factorization = cachedEntry;
|
||||
return true;
|
||||
}
|
||||
|
||||
Dictionary<ulong, int> scratch = ThreadStaticPools.RentFactorScratchDictionary();
|
||||
bool factored = TryFactorIntoCountsInternal(value, scratch);
|
||||
if (!factored)
|
||||
bool success = TryFactorIntoCountsInternal(value, scratch);
|
||||
if (!success)
|
||||
{
|
||||
// The scan recalculates factors every time; the old cache never produced hits.
|
||||
ThreadStaticPools.ReturnFactorScratchDictionary(scratch);
|
||||
factorization = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = scratch.Count;
|
||||
FactorCacheEntry entry = ThreadStaticPools.RentFactorCacheEntry();
|
||||
if (count == 0)
|
||||
for (Dictionary<ulong, int>.Enumerator enumerator = scratch.GetEnumerator(); enumerator.MoveNext();)
|
||||
{
|
||||
entry.Initialize(null, 0, fromPool: false);
|
||||
factorization = entry;
|
||||
releaseAfterUse = cache is null || !cacheResult;
|
||||
if (cache is not null && cacheResult)
|
||||
{
|
||||
cache[value] = entry;
|
||||
}
|
||||
|
||||
ThreadStaticPools.ReturnFactorScratchDictionary(scratch);
|
||||
return true;
|
||||
KeyValuePair<ulong, int> pair = enumerator.Current;
|
||||
AddFactor(counts, pair.Key, pair.Value);
|
||||
}
|
||||
|
||||
KeyValuePair<ulong, int>[] pairs = ThreadStaticPools.FactorKeyValuePairPool.Rent(count);
|
||||
((ICollection<KeyValuePair<ulong, int>>)scratch).CopyTo(pairs, 0);
|
||||
ThreadStaticPools.ReturnFactorScratchDictionary(scratch);
|
||||
Array.Sort(pairs, 0, count, FactorPairValueComparer.Instance);
|
||||
|
||||
entry.Initialize(pairs, count, fromPool: true);
|
||||
factorization = entry;
|
||||
|
||||
if (cache is not null && cacheResult)
|
||||
{
|
||||
cache[value] = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
releaseAfterUse = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -686,44 +603,6 @@ public class MersenneDivisorCycles
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FactorCacheEntry
|
||||
{
|
||||
internal FactorCacheEntry? Next;
|
||||
internal KeyValuePair<ulong, int>[]? Pairs;
|
||||
internal int Count;
|
||||
private bool _fromPool;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void Reset()
|
||||
{
|
||||
Pairs = null;
|
||||
Count = 0;
|
||||
_fromPool = false;
|
||||
Next = null;
|
||||
}
|
||||
|
||||
internal void Initialize(KeyValuePair<ulong, int>[]? pairs, int count, bool fromPool)
|
||||
{
|
||||
Pairs = pairs;
|
||||
Count = count;
|
||||
_fromPool = fromPool;
|
||||
Next = null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void ReturnToPool()
|
||||
{
|
||||
KeyValuePair<ulong, int>[]? pairs = Pairs;
|
||||
if (_fromPool && pairs is not null)
|
||||
{
|
||||
ThreadStaticPools.FactorKeyValuePairPool.Return(pairs, clearArray: false);
|
||||
}
|
||||
|
||||
Reset();
|
||||
ThreadStaticPools.ReturnFactorCacheEntry(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static (long nextPosition, long completeCount) FindLast(string path)
|
||||
{
|
||||
using Stream outputStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, BufferSize10M, useAsync: false);
|
||||
|
||||
@@ -31,17 +31,6 @@ namespace PerfectNumbers.Core
|
||||
}
|
||||
|
||||
|
||||
[ThreadStatic]
|
||||
private static ArrayPool<KeyValuePair<ulong, int>>? _factorKeyValuePairPool;
|
||||
|
||||
public static ArrayPool<KeyValuePair<ulong, int>> FactorKeyValuePairPool
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return _factorKeyValuePairPool ??= ArrayPool<KeyValuePair<ulong, int>>.Create();
|
||||
}
|
||||
}
|
||||
[ThreadStatic]
|
||||
private static MersenneCpuDivisorScanSession? _mersenneCpuDivisorSession;
|
||||
|
||||
@@ -278,62 +267,6 @@ namespace PerfectNumbers.Core
|
||||
pool.Add(dictionary);
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? _mersenneFactorCacheDictionary;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry> RentMersenneFactorCacheDictionary()
|
||||
{
|
||||
Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>? cache = _mersenneFactorCacheDictionary;
|
||||
if (cache is null)
|
||||
{
|
||||
return new Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>(8);
|
||||
}
|
||||
|
||||
_mersenneFactorCacheDictionary = null;
|
||||
cache.Clear();
|
||||
return cache;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ReturnMersenneFactorCacheDictionary(Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry> cache)
|
||||
{
|
||||
for (Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry>.Enumerator enumerator = cache.GetEnumerator(); enumerator.MoveNext();)
|
||||
{
|
||||
enumerator.Current.Value.ReturnToPool();
|
||||
}
|
||||
|
||||
cache.Clear();
|
||||
_mersenneFactorCacheDictionary = cache;
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static MersenneDivisorCycles.FactorCacheEntry? _factorCacheEntryPoolHead;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static MersenneDivisorCycles.FactorCacheEntry RentFactorCacheEntry()
|
||||
{
|
||||
MersenneDivisorCycles.FactorCacheEntry? entry = _factorCacheEntryPoolHead;
|
||||
if (entry is null)
|
||||
{
|
||||
entry = new MersenneDivisorCycles.FactorCacheEntry();
|
||||
}
|
||||
else
|
||||
{
|
||||
_factorCacheEntryPoolHead = entry.Next;
|
||||
}
|
||||
|
||||
entry.Reset();
|
||||
return entry;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ReturnFactorCacheEntry(MersenneDivisorCycles.FactorCacheEntry entry)
|
||||
{
|
||||
entry.Next = _factorCacheEntryPoolHead;
|
||||
_factorCacheEntryPoolHead = entry;
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ulong, int>? _factorCountDictionary;
|
||||
|
||||
|
||||
Reference in new issue
Block a user