Eliminate GC pressure from divisor cycle factoring
This commit is contained in:
1 parent
c8f8132db7
commit
34fcd62c23
3 files changed
+111
-25
No files matched your search
@@ -199,7 +199,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
cacheForCycle = factorCache;
|
cacheForCycle = factorCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponent(candidate, prime, divisorData, cacheForCycle, out ulong divisorCycle))
|
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponent(candidate, prime, divisorData, cacheForCycle, cacheForCycle is not null, out ulong divisorCycle))
|
||||||
{
|
{
|
||||||
divisorCycle = 0UL;
|
divisorCycle = 0UL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,11 +264,28 @@ public class MersenneDivisorCycles
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryCalculateCycleLengthForExponent(
|
public static bool TryCalculateCycleLengthForExponent(
|
||||||
ulong divisor,
|
ulong divisor,
|
||||||
ulong exponent,
|
ulong exponent,
|
||||||
in MontgomeryDivisorData divisorData,
|
in MontgomeryDivisorData divisorData,
|
||||||
Dictionary<ulong, FactorCacheEntry>? factorCache,
|
Dictionary<ulong, FactorCacheEntry>? factorCache,
|
||||||
out ulong cycleLength)
|
out ulong cycleLength)
|
||||||
|
{
|
||||||
|
return TryCalculateCycleLengthForExponent(
|
||||||
|
divisor,
|
||||||
|
exponent,
|
||||||
|
divisorData,
|
||||||
|
factorCache,
|
||||||
|
usePooledCache: false,
|
||||||
|
out cycleLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryCalculateCycleLengthForExponent(
|
||||||
|
ulong divisor,
|
||||||
|
ulong exponent,
|
||||||
|
in MontgomeryDivisorData divisorData,
|
||||||
|
Dictionary<ulong, FactorCacheEntry>? factorCache,
|
||||||
|
bool usePooledCache,
|
||||||
|
out ulong cycleLength)
|
||||||
{
|
{
|
||||||
cycleLength = 0UL;
|
cycleLength = 0UL;
|
||||||
|
|
||||||
@@ -316,7 +333,9 @@ public class MersenneDivisorCycles
|
|||||||
factorCounts[2UL] = twoCount;
|
factorCounts[2UL] = twoCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AccumulateFactors(exponent, factorCache, factorCounts, cacheResult: true) || !AccumulateFactors(k, factorCache, factorCounts, cacheResult: false))
|
bool poolExponentFactors = usePooledCache || factorCache is null;
|
||||||
|
|
||||||
|
if (!AccumulateFactors(exponent, factorCache, factorCounts, cacheResult: true, poolExponentFactors) || !AccumulateFactors(k, factorCache, factorCounts, cacheResult: false, allowPooledStorage: true))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -334,25 +353,36 @@ public class MersenneDivisorCycles
|
|||||||
ulong value,
|
ulong value,
|
||||||
Dictionary<ulong, FactorCacheEntry>? cache,
|
Dictionary<ulong, FactorCacheEntry>? cache,
|
||||||
Dictionary<ulong, int> counts,
|
Dictionary<ulong, int> counts,
|
||||||
bool cacheResult)
|
bool cacheResult,
|
||||||
|
bool allowPooledStorage)
|
||||||
{
|
{
|
||||||
if (value <= 1UL)
|
if (value <= 1UL)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryGetFactorization(value, cache, cacheResult, out FactorCacheEntry factorization))
|
bool poolStorage = allowPooledStorage || cache is null || !cacheResult;
|
||||||
|
|
||||||
|
if (!TryGetFactorization(value, cache, cacheResult, poolStorage, out FactorCacheEntry factorization))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong[] primes = factorization.Primes;
|
FactorEntry[]? entries = factorization.Entries;
|
||||||
byte[] exponents = factorization.Exponents;
|
if (entries is not null)
|
||||||
int length = factorization.Count;
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
{
|
||||||
AddFactor(counts, primes[i], exponents[i]);
|
int length = factorization.Count;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
FactorEntry entry = entries[i];
|
||||||
|
AddFactor(counts, entry.Value, entry.Exponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache is null || !cacheResult)
|
||||||
|
{
|
||||||
|
factorization.ReturnToPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -362,6 +392,7 @@ public class MersenneDivisorCycles
|
|||||||
ulong value,
|
ulong value,
|
||||||
Dictionary<ulong, FactorCacheEntry>? cache,
|
Dictionary<ulong, FactorCacheEntry>? cache,
|
||||||
bool cacheResult,
|
bool cacheResult,
|
||||||
|
bool allowPooledStorage,
|
||||||
out FactorCacheEntry factorization)
|
out FactorCacheEntry factorization)
|
||||||
{
|
{
|
||||||
if (cache is not null && cache.TryGetValue(value, out factorization))
|
if (cache is not null && cache.TryGetValue(value, out factorization))
|
||||||
@@ -380,18 +411,26 @@ public class MersenneDivisorCycles
|
|||||||
}
|
}
|
||||||
|
|
||||||
int count = scratch.Count;
|
int count = scratch.Count;
|
||||||
ulong[] primes = new ulong[count];
|
if (count == 0)
|
||||||
byte[] exponents = new byte[count];
|
{
|
||||||
|
factorization = default!;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fromPool = allowPooledStorage;
|
||||||
|
FactorEntry[] entries = fromPool
|
||||||
|
? ThreadStaticPools.FactorEntryPool.Rent(count)
|
||||||
|
: new FactorEntry[count];
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (KeyValuePair<ulong, int> entry in scratch)
|
foreach (KeyValuePair<ulong, int> entry in scratch)
|
||||||
{
|
{
|
||||||
primes[index] = entry.Key;
|
entries[index] = new FactorEntry(entry.Key, entry.Value);
|
||||||
exponents[index] = checked((byte)entry.Value);
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.Sort(primes, exponents);
|
Array.Sort(entries, 0, count, FactorEntryValueComparer.Instance);
|
||||||
factorization = new FactorCacheEntry(primes, exponents, count);
|
factorization = new FactorCacheEntry(entries, count, fromPool);
|
||||||
|
|
||||||
if (cache is not null && cacheResult)
|
if (cache is not null && cacheResult)
|
||||||
{
|
{
|
||||||
@@ -612,19 +651,61 @@ public class MersenneDivisorCycles
|
|||||||
return BinaryPrimitives.ReadUInt64LittleEndian(buffer);
|
return BinaryPrimitives.ReadUInt64LittleEndian(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class FactorEntryValueComparer : IComparer<FactorEntry>
|
||||||
|
{
|
||||||
|
public static readonly FactorEntryValueComparer Instance = new FactorEntryValueComparer();
|
||||||
|
|
||||||
|
private FactorEntryValueComparer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Compare(FactorEntry x, FactorEntry y)
|
||||||
|
{
|
||||||
|
if (x.Value < y.Value)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.Value > y.Value)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public readonly struct FactorCacheEntry
|
public readonly struct FactorCacheEntry
|
||||||
{
|
{
|
||||||
public FactorCacheEntry(ulong[] primes, byte[] exponents, int count)
|
private readonly bool _fromPool;
|
||||||
{
|
|
||||||
Primes = primes;
|
|
||||||
Exponents = exponents;
|
|
||||||
Count = count;
|
|
||||||
}
|
|
||||||
public ulong[] Primes { get; }
|
|
||||||
|
|
||||||
public byte[] Exponents { get; }
|
internal FactorCacheEntry(FactorEntry[] entries, int count, bool fromPool)
|
||||||
|
{
|
||||||
|
Entries = entries;
|
||||||
|
Count = count;
|
||||||
|
_fromPool = fromPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FactorEntry[]? Entries { get; }
|
||||||
|
|
||||||
public int Count { get; }
|
public int Count { get; }
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal void ReturnToPool()
|
||||||
|
{
|
||||||
|
if (!_fromPool)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FactorEntry[]? entries = Entries;
|
||||||
|
if (entries is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadStaticPools.FactorEntryPool.Return(entries, clearArray: false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (long nextPosition, long completeCount) FindLast(string path)
|
public static (long nextPosition, long completeCount) FindLast(string path)
|
||||||
|
|||||||
@@ -63,6 +63,11 @@ namespace PerfectNumbers.Core
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReturnMersenneFactorCacheDictionary(Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry> cache)
|
public static void ReturnMersenneFactorCacheDictionary(Dictionary<ulong, MersenneDivisorCycles.FactorCacheEntry> cache)
|
||||||
{
|
{
|
||||||
|
foreach (KeyValuePair<ulong, MersenneDivisorCycles.FactorCacheEntry> entry in cache)
|
||||||
|
{
|
||||||
|
entry.Value.ReturnToPool();
|
||||||
|
}
|
||||||
|
|
||||||
cache.Clear();
|
cache.Clear();
|
||||||
_mersenneFactorCacheDictionary = cache;
|
_mersenneFactorCacheDictionary = cache;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user