Eliminate GC pressure from divisor cycle factoring

This commit is contained in:
Marek Lemańczyk committed 2025-10-15 19:32:53 +02:00
1 parent c8f8132db7
commit 34fcd62c23
3 files changed
+110 -24

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;
} }
+104 -23
View File
@@ -269,6 +269,23 @@ public class MersenneDivisorCycles
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)
+5
View File
@@ -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;
} }