diff --git a/PerfectNumbers.Core/DivisorCycleCache.cs b/PerfectNumbers.Core/DivisorCycleCache.cs index b01e5e2..d67b1ef 100644 --- a/PerfectNumbers.Core/DivisorCycleCache.cs +++ b/PerfectNumbers.Core/DivisorCycleCache.cs @@ -63,7 +63,7 @@ public sealed class DivisorCycleCache int[]? rentedMissing = null; Span missingBuffer = length <= StackBufferThreshold ? stackalloc int[length] - : new Span(rentedMissing = ArrayPool.Shared.Rent(length), 0, length); + : new Span(rentedMissing = ThreadLocalArrayPool.Shared.Rent(length), 0, length); int missingCount = 0; @@ -106,7 +106,7 @@ public sealed class DivisorCycleCache if (rentedMissing is not null) { - ArrayPool.Shared.Return(rentedMissing, clearArray: false); + ThreadLocalArrayPool.Shared.Return(rentedMissing, clearArray: false); } } @@ -128,12 +128,12 @@ public sealed class DivisorCycleCache ulong[]? rentedDivisors = null; Span workDivisors = count <= StackBufferThreshold ? stackalloc ulong[count] - : new Span(rentedDivisors = ArrayPool.Shared.Rent(count), 0, count); + : new Span(rentedDivisors = ThreadLocalArrayPool.Shared.Rent(count), 0, count); ulong[]? rentedResults = null; Span workResults = count <= StackBufferThreshold ? stackalloc ulong[count] - : new Span(rentedResults = ArrayPool.Shared.Rent(count), 0, count); + : new Span(rentedResults = ThreadLocalArrayPool.Shared.Rent(count), 0, count); for (int i = 0; i < count; i++) { @@ -149,8 +149,8 @@ public sealed class DivisorCycleCache if (rentedDivisors is not null) { - ArrayPool.Shared.Return(rentedDivisors, clearArray: false); - ArrayPool.Shared.Return(rentedResults!, clearArray: false); + ThreadLocalArrayPool.Shared.Return(rentedDivisors, clearArray: false); + ThreadLocalArrayPool.Shared.Return(rentedResults!, clearArray: false); } } @@ -172,22 +172,22 @@ public sealed class DivisorCycleCache ulong[]? rentedPow = null; Span powSpan = length <= StackBufferThreshold ? stackalloc ulong[length] - : new Span(rentedPow = ArrayPool.Shared.Rent(length), 0, length); + : new Span(rentedPow = ThreadLocalArrayPool.Shared.Rent(length), 0, length); ulong[]? rentedOrder = null; Span orderSpan = length <= StackBufferThreshold ? stackalloc ulong[length] - : new Span(rentedOrder = ArrayPool.Shared.Rent(length), 0, length); + : new Span(rentedOrder = ThreadLocalArrayPool.Shared.Rent(length), 0, length); ulong[]? rentedResult = null; Span resultSpan = length <= StackBufferThreshold ? stackalloc ulong[length] - : new Span(rentedResult = ArrayPool.Shared.Rent(length), 0, length); + : new Span(rentedResult = ThreadLocalArrayPool.Shared.Rent(length), 0, length); byte[]? rentedStatus = null; Span statusSpan = length <= StackBufferThreshold ? stackalloc byte[length] - : new Span(rentedStatus = ArrayPool.Shared.Rent(length), 0, length); + : new Span(rentedStatus = ThreadLocalArrayPool.Shared.Rent(length), 0, length); ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors); divisorBuffer.View.CopyFromCPU(ref divisorRef, length); @@ -234,10 +234,10 @@ public sealed class DivisorCycleCache resultSpan.CopyTo(destination); if (rentedPow is not null) { - ArrayPool.Shared.Return(rentedPow, clearArray: false); - ArrayPool.Shared.Return(rentedOrder!, clearArray: false); - ArrayPool.Shared.Return(rentedResult!, clearArray: false); - ArrayPool.Shared.Return(rentedStatus!, clearArray: false); + ThreadLocalArrayPool.Shared.Return(rentedPow, clearArray: false); + ThreadLocalArrayPool.Shared.Return(rentedOrder!, clearArray: false); + ThreadLocalArrayPool.Shared.Return(rentedResult!, clearArray: false); + ThreadLocalArrayPool.Shared.Return(rentedStatus!, clearArray: false); } execution.Dispose(); diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index e208ec1..19a743c 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -567,7 +567,7 @@ public class MersenneDivisorCycles } int count = factorCounts.Count; - ulong[] primes = ArrayPool.Shared.Rent(count); + ulong[] primes = ThreadLocalArrayPool.Shared.Rent(count); try { int index = 0; @@ -607,7 +607,7 @@ public class MersenneDivisorCycles } finally { - ArrayPool.Shared.Return(primes, clearArray: false); + ThreadLocalArrayPool.Shared.Return(primes, clearArray: false); } } @@ -793,7 +793,7 @@ public class MersenneDivisorCycles tasks[taskIndex] = Task.Run(() => { ulong rangeLength = localEnd - localStart + 1UL; - (ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength)); + (ulong divisor, ulong cycle)[] localCycles = ThreadLocalArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength)); int localCycleIndex = 0; try @@ -841,7 +841,7 @@ public class MersenneDivisorCycles } finally { - ArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false); + ThreadLocalArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false); } }); } @@ -856,7 +856,7 @@ public class MersenneDivisorCycles ulong start = 3UL; var locker = new object(); - var pool = ArrayPool.Shared; + var pool = ThreadLocalArrayPool.Shared; ulong batchSizeUL = (ulong)batchSize, d, end; int count = (int)Math.Min(batchSizeUL, maxDivisor), i, idx; ulong[] divisors, outCycles, validDivisors; diff --git a/PerfectNumbers.Core/PartialFactorResult.cs b/PerfectNumbers.Core/PartialFactorResult.cs index bd5b713..568c79b 100644 --- a/PerfectNumbers.Core/PartialFactorResult.cs +++ b/PerfectNumbers.Core/PartialFactorResult.cs @@ -49,12 +49,12 @@ namespace PerfectNumbers.Core FactorEntry[]? source = Factors; if (source is null || Count == 0) { - FactorEntry[] local = ArrayPool.Shared.Rent(1); + FactorEntry[] local = ThreadLocalArrayPool.Shared.Rent(1); local[0] = new FactorEntry(prime); return Rent(local, 1UL, true, 1); } - FactorEntry[] extended = ArrayPool.Shared.Rent(Count + 1); + FactorEntry[] extended = ThreadLocalArrayPool.Shared.Rent(Count + 1); Array.Copy(source, 0, extended, 0, Count); extended[Count] = new FactorEntry(prime); Span span = extended.AsSpan(0, Count + 1); @@ -70,7 +70,7 @@ namespace PerfectNumbers.Core if (factors is not null) { Factors = null; - ArrayPool.Shared.Return(factors, clearArray: false); + ThreadLocalArrayPool.Shared.Return(factors, clearArray: false); } _next = s_poolHead; diff --git a/PerfectNumbers.Core/PrimeFactorPartialResult.cs b/PerfectNumbers.Core/PrimeFactorPartialResult.cs index 549f401..bdb8d0d 100644 --- a/PerfectNumbers.Core/PrimeFactorPartialResult.cs +++ b/PerfectNumbers.Core/PrimeFactorPartialResult.cs @@ -81,12 +81,12 @@ namespace PerfectNumbers.Core { if (_primes is not null) { - ArrayPool.Shared.Return(_primes, clearArray: false); + ThreadLocalArrayPool.Shared.Return(_primes, clearArray: false); } if (_exponents is not null) { - ArrayPool.Shared.Return(_exponents, clearArray: false); + ThreadLocalArrayPool.Shared.Return(_exponents, clearArray: false); } _arraysFromPool = false; @@ -119,12 +119,12 @@ namespace PerfectNumbers.Core { if (_primes is not null) { - ArrayPool.Shared.Return(_primes, clearArray: false); + ThreadLocalArrayPool.Shared.Return(_primes, clearArray: false); } if (_exponents is not null) { - ArrayPool.Shared.Return(_exponents, clearArray: false); + ThreadLocalArrayPool.Shared.Return(_exponents, clearArray: false); } _arraysFromPool = false; @@ -134,8 +134,8 @@ namespace PerfectNumbers.Core if (capacity >= PerfectNumberConstants.PooledArrayThreshold) { - _primes = ArrayPool.Shared.Rent(capacity); - _exponents = ArrayPool.Shared.Rent(capacity); + _primes = ThreadLocalArrayPool.Shared.Rent(capacity); + _exponents = ThreadLocalArrayPool.Shared.Rent(capacity); _arraysFromPool = true; } else diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs index dee949d..206fe70 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Wide.cs @@ -162,7 +162,7 @@ internal static partial class PrimeOrderCalculator return order; } - tempArray = ArrayPool.Shared.Rent(length + 1); + tempArray = ThreadLocalArrayPool.Shared.Rent(length + 1); Span buffer = tempArray.AsSpan(0, length); factorSpan.CopyTo(buffer); @@ -200,7 +200,7 @@ internal static partial class PrimeOrderCalculator { if (tempArray is not null) { - ArrayPool.Shared.Return(tempArray, clearArray: false); + ThreadLocalArrayPool.Shared.Return(tempArray, clearArray: false); } } } @@ -664,7 +664,7 @@ internal static partial class PrimeOrderCalculator return new PartialFactorResult128(null, value, false, 0); } - FactorEntry128[]? rented = counts.Count > 0 ? ArrayPool.Shared.Rent(counts.Count) : null; + FactorEntry128[]? rented = counts.Count > 0 ? ThreadLocalArrayPool.Shared.Rent(counts.Count) : null; int index = 0; if (rented is not null) { @@ -682,7 +682,7 @@ internal static partial class PrimeOrderCalculator span.Sort(static (a, b) => a.Value.CompareTo(b.Value)); resultArray = new FactorEntry128[index]; span.CopyTo(resultArray); - ArrayPool.Shared.Return(rented, clearArray: false); + ThreadLocalArrayPool.Shared.Return(rented, clearArray: false); } bool fullyFactored = cofactor == UInt128.One; diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.cs b/PerfectNumbers.Core/PrimeOrderCalculator.cs index d08a13e..daa0bac 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.cs @@ -1058,7 +1058,7 @@ internal static partial class PrimeOrderCalculator return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); } - FactorEntry[] factors = ArrayPool.Shared.Rent(counts.Count); + FactorEntry[] factors = ThreadLocalArrayPool.Shared.Rent(counts.Count); int index = 0; foreach (KeyValuePair entry in counts) { @@ -1092,7 +1092,7 @@ internal static partial class PrimeOrderCalculator return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0); } - FactorEntry[] array = ArrayPool.Shared.Rent(actualCount); + FactorEntry[] array = ThreadLocalArrayPool.Shared.Rent(actualCount); int arrayIndex = 0; for (int i = 0; i < factorCount; i++) { diff --git a/PerfectNumbers.Core/ThreadLocalArrayPool.cs b/PerfectNumbers.Core/ThreadLocalArrayPool.cs new file mode 100644 index 0000000..28ed389 --- /dev/null +++ b/PerfectNumbers.Core/ThreadLocalArrayPool.cs @@ -0,0 +1,27 @@ +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace PerfectNumbers.Core; + +internal static class ThreadLocalArrayPool +{ + [ThreadStatic] + private static ArrayPool? s_threadPool; + + public static ArrayPool Shared + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ArrayPool? pool = s_threadPool; + if (pool is null) + { + pool = ArrayPool.Create(); + s_threadPool = pool; + } + + return pool; + } + } +}