diff --git a/PerfectNumbers.Core/DivisorCycleCache.cs b/PerfectNumbers.Core/DivisorCycleCache.cs index d67b1ef..c6480ee 100644 --- a/PerfectNumbers.Core/DivisorCycleCache.cs +++ b/PerfectNumbers.Core/DivisorCycleCache.cs @@ -63,7 +63,9 @@ public sealed class DivisorCycleCache int[]? rentedMissing = null; Span missingBuffer = length <= StackBufferThreshold ? stackalloc int[length] - : new Span(rentedMissing = ThreadLocalArrayPool.Shared.Rent(length), 0, length); + : length < PerfectNumberConstants.PooledArrayThreshold + ? new Span(new int[length]) + : new Span(rentedMissing = ThreadLocalArrayPool.Shared.Rent(length), 0, length); int missingCount = 0; @@ -128,12 +130,16 @@ public sealed class DivisorCycleCache ulong[]? rentedDivisors = null; Span workDivisors = count <= StackBufferThreshold ? stackalloc ulong[count] - : new Span(rentedDivisors = ThreadLocalArrayPool.Shared.Rent(count), 0, count); + : count < PerfectNumberConstants.PooledArrayThreshold + ? new Span(new ulong[count]) + : new Span(rentedDivisors = ThreadLocalArrayPool.Shared.Rent(count), 0, count); ulong[]? rentedResults = null; Span workResults = count <= StackBufferThreshold ? stackalloc ulong[count] - : new Span(rentedResults = ThreadLocalArrayPool.Shared.Rent(count), 0, count); + : count < PerfectNumberConstants.PooledArrayThreshold + ? new Span(new ulong[count]) + : new Span(rentedResults = ThreadLocalArrayPool.Shared.Rent(count), 0, count); for (int i = 0; i < count; i++) { @@ -150,7 +156,11 @@ public sealed class DivisorCycleCache if (rentedDivisors is not null) { ThreadLocalArrayPool.Shared.Return(rentedDivisors, clearArray: false); - ThreadLocalArrayPool.Shared.Return(rentedResults!, clearArray: false); + } + + if (rentedResults is not null) + { + ThreadLocalArrayPool.Shared.Return(rentedResults, clearArray: false); } } @@ -172,22 +182,30 @@ public sealed class DivisorCycleCache ulong[]? rentedPow = null; Span powSpan = length <= StackBufferThreshold ? stackalloc ulong[length] - : new Span(rentedPow = ThreadLocalArrayPool.Shared.Rent(length), 0, length); + : length < PerfectNumberConstants.PooledArrayThreshold + ? new Span(new ulong[length]) + : new Span(rentedPow = ThreadLocalArrayPool.Shared.Rent(length), 0, length); ulong[]? rentedOrder = null; Span orderSpan = length <= StackBufferThreshold ? stackalloc ulong[length] - : new Span(rentedOrder = ThreadLocalArrayPool.Shared.Rent(length), 0, length); + : length < PerfectNumberConstants.PooledArrayThreshold + ? new Span(new ulong[length]) + : new Span(rentedOrder = ThreadLocalArrayPool.Shared.Rent(length), 0, length); ulong[]? rentedResult = null; Span resultSpan = length <= StackBufferThreshold ? stackalloc ulong[length] - : new Span(rentedResult = ThreadLocalArrayPool.Shared.Rent(length), 0, length); + : length < PerfectNumberConstants.PooledArrayThreshold + ? new Span(new ulong[length]) + : new Span(rentedResult = ThreadLocalArrayPool.Shared.Rent(length), 0, length); byte[]? rentedStatus = null; Span statusSpan = length <= StackBufferThreshold ? stackalloc byte[length] - : new Span(rentedStatus = ThreadLocalArrayPool.Shared.Rent(length), 0, length); + : length < PerfectNumberConstants.PooledArrayThreshold + ? new Span(new byte[length]) + : new Span(rentedStatus = ThreadLocalArrayPool.Shared.Rent(length), 0, length); ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors); divisorBuffer.View.CopyFromCPU(ref divisorRef, length); @@ -235,9 +253,21 @@ public sealed class DivisorCycleCache if (rentedPow is not null) { ThreadLocalArrayPool.Shared.Return(rentedPow, clearArray: false); - ThreadLocalArrayPool.Shared.Return(rentedOrder!, clearArray: false); - ThreadLocalArrayPool.Shared.Return(rentedResult!, clearArray: false); - ThreadLocalArrayPool.Shared.Return(rentedStatus!, clearArray: false); + } + + if (rentedOrder is not null) + { + ThreadLocalArrayPool.Shared.Return(rentedOrder, clearArray: false); + } + + if (rentedResult is not null) + { + ThreadLocalArrayPool.Shared.Return(rentedResult, clearArray: false); + } + + if (rentedStatus is not null) + { + ThreadLocalArrayPool.Shared.Return(rentedStatus, clearArray: false); } execution.Dispose(); diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index 19a743c..a6b7659 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -567,7 +567,10 @@ public class MersenneDivisorCycles } int count = factorCounts.Count; - ulong[] primes = ThreadLocalArrayPool.Shared.Rent(count); + bool rentFromPool = count >= PerfectNumberConstants.PooledArrayThreshold; + ulong[] primes = rentFromPool + ? ThreadLocalArrayPool.Shared.Rent(count) + : new ulong[count]; try { int index = 0; @@ -607,7 +610,10 @@ public class MersenneDivisorCycles } finally { - ThreadLocalArrayPool.Shared.Return(primes, clearArray: false); + if (rentFromPool) + { + ThreadLocalArrayPool.Shared.Return(primes, clearArray: false); + } } } @@ -793,7 +799,11 @@ public class MersenneDivisorCycles tasks[taskIndex] = Task.Run(() => { ulong rangeLength = localEnd - localStart + 1UL; - (ulong divisor, ulong cycle)[] localCycles = ThreadLocalArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength)); + int localLength = checked((int)rangeLength); + bool rentLocalCycles = localLength >= PerfectNumberConstants.PooledArrayThreshold; + (ulong divisor, ulong cycle)[] localCycles = rentLocalCycles + ? ThreadLocalArrayPool<(ulong, ulong)>.Shared.Rent(localLength) + : new (ulong divisor, ulong cycle)[localLength]; int localCycleIndex = 0; try @@ -841,7 +851,10 @@ public class MersenneDivisorCycles } finally { - ThreadLocalArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false); + if (rentLocalCycles) + { + ThreadLocalArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false); + } } }); } diff --git a/PerfectNumbers.Core/PartialFactorResult.cs b/PerfectNumbers.Core/PartialFactorResult.cs index 568c79b..beb36ee 100644 --- a/PerfectNumbers.Core/PartialFactorResult.cs +++ b/PerfectNumbers.Core/PartialFactorResult.cs @@ -1,19 +1,26 @@ using System; -using System.Buffers; namespace PerfectNumbers.Core { public sealed class PartialFactorResult { - [ThreadStatic] - private static PartialFactorResult? s_poolHead; + [ThreadStatic] + private static PartialFactorResult? s_poolHead; + + public static readonly PartialFactorResult Empty = new() + { + Cofactor = 1UL, + FullyFactored = true, + }; public FactorEntry[]? Factors; - private PartialFactorResult? _next; public ulong Cofactor; public bool FullyFactored; public int Count; + private PartialFactorResult? _next; + private bool _factorsFromPool; + private PartialFactorResult() { } @@ -30,52 +37,58 @@ namespace PerfectNumbers.Core s_poolHead = instance._next; } + FactorEntry[]? factorArray = factors; instance._next = null; - instance.Factors = factors; + instance.Factors = factorArray; instance.Cofactor = cofactor; instance.FullyFactored = fullyFactored; instance.Count = count; + instance._factorsFromPool = factorArray is not null && factorArray.Length >= PerfectNumberConstants.PooledArrayThreshold; return instance; } - public static readonly PartialFactorResult Empty = new() - { - Cofactor = 1UL, - FullyFactored = true, - }; - public PartialFactorResult WithAdditionalPrime(ulong prime) { FactorEntry[]? source = Factors; if (source is null || Count == 0) { - FactorEntry[] local = ThreadLocalArrayPool.Shared.Rent(1); - local[0] = new FactorEntry(prime); - return Rent(local, 1UL, true, 1); + FactorEntry[] single = new FactorEntry[1]; + single[0] = new FactorEntry(prime); + return Rent(single, 1UL, true, 1); } - FactorEntry[] extended = ThreadLocalArrayPool.Shared.Rent(Count + 1); + int newCount = Count + 1; + FactorEntry[] extended = newCount >= PerfectNumberConstants.PooledArrayThreshold + ? ThreadLocalArrayPool.Shared.Rent(newCount) + : new FactorEntry[newCount]; + Array.Copy(source, 0, extended, 0, Count); extended[Count] = new FactorEntry(prime); - Span span = extended.AsSpan(0, Count + 1); + Span span = extended.AsSpan(0, newCount); span.Sort(static (a, b) => a.Value.CompareTo(b.Value)); - return Rent(extended, 1UL, true, Count + 1); + return Rent(extended, 1UL, true, newCount); } public void Dispose() { - if (this != Empty) + if (this == Empty) { - FactorEntry[]? factors = Factors; - if (factors is not null) + return; + } + + FactorEntry[]? factors = Factors; + if (factors is not null) + { + Factors = null; + if (_factorsFromPool) { - Factors = null; ThreadLocalArrayPool.Shared.Return(factors, clearArray: false); } - - _next = s_poolHead; - s_poolHead = this; } + + _factorsFromPool = false; + _next = s_poolHead; + s_poolHead = this; } } } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.cs b/PerfectNumbers.Core/PrimeOrderCalculator.cs index daa0bac..1312815 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.cs @@ -1058,7 +1058,10 @@ internal static partial class PrimeOrderCalculator return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); } - FactorEntry[] factors = ThreadLocalArrayPool.Shared.Rent(counts.Count); + int dictionaryCount = counts.Count; + FactorEntry[] factors = dictionaryCount >= PerfectNumberConstants.PooledArrayThreshold + ? ThreadLocalArrayPool.Shared.Rent(dictionaryCount) + : new FactorEntry[dictionaryCount]; int index = 0; foreach (KeyValuePair entry in counts) { @@ -1092,7 +1095,9 @@ internal static partial class PrimeOrderCalculator return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); } - FactorEntry[] array = ThreadLocalArrayPool.Shared.Rent(actualCount); + FactorEntry[] array = actualCount >= PerfectNumberConstants.PooledArrayThreshold + ? ThreadLocalArrayPool.Shared.Rent(actualCount) + : new FactorEntry[actualCount]; int arrayIndex = 0; for (int i = 0; i < factorCount; i++) {