Avoid pooling small buffers in CPU divisor pipeline
This commit is contained in:
1 parent
8e9390bc5e
commit
ad22329814
4 files changed
+86
-25
No files matched your search
@@ -63,6 +63,8 @@ public sealed class DivisorCycleCache
|
|||||||
int[]? rentedMissing = null;
|
int[]? rentedMissing = null;
|
||||||
Span<int> missingBuffer = length <= StackBufferThreshold
|
Span<int> missingBuffer = length <= StackBufferThreshold
|
||||||
? stackalloc int[length]
|
? stackalloc int[length]
|
||||||
|
: length < PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? new Span<int>(new int[length])
|
||||||
: new Span<int>(rentedMissing = ThreadLocalArrayPool<int>.Shared.Rent(length), 0, length);
|
: new Span<int>(rentedMissing = ThreadLocalArrayPool<int>.Shared.Rent(length), 0, length);
|
||||||
|
|
||||||
int missingCount = 0;
|
int missingCount = 0;
|
||||||
@@ -128,11 +130,15 @@ public sealed class DivisorCycleCache
|
|||||||
ulong[]? rentedDivisors = null;
|
ulong[]? rentedDivisors = null;
|
||||||
Span<ulong> workDivisors = count <= StackBufferThreshold
|
Span<ulong> workDivisors = count <= StackBufferThreshold
|
||||||
? stackalloc ulong[count]
|
? stackalloc ulong[count]
|
||||||
|
: count < PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? new Span<ulong>(new ulong[count])
|
||||||
: new Span<ulong>(rentedDivisors = ThreadLocalArrayPool<ulong>.Shared.Rent(count), 0, count);
|
: new Span<ulong>(rentedDivisors = ThreadLocalArrayPool<ulong>.Shared.Rent(count), 0, count);
|
||||||
|
|
||||||
ulong[]? rentedResults = null;
|
ulong[]? rentedResults = null;
|
||||||
Span<ulong> workResults = count <= StackBufferThreshold
|
Span<ulong> workResults = count <= StackBufferThreshold
|
||||||
? stackalloc ulong[count]
|
? stackalloc ulong[count]
|
||||||
|
: count < PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? new Span<ulong>(new ulong[count])
|
||||||
: new Span<ulong>(rentedResults = ThreadLocalArrayPool<ulong>.Shared.Rent(count), 0, count);
|
: new Span<ulong>(rentedResults = ThreadLocalArrayPool<ulong>.Shared.Rent(count), 0, count);
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
@@ -150,7 +156,11 @@ public sealed class DivisorCycleCache
|
|||||||
if (rentedDivisors is not null)
|
if (rentedDivisors is not null)
|
||||||
{
|
{
|
||||||
ThreadLocalArrayPool<ulong>.Shared.Return(rentedDivisors, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedDivisors, clearArray: false);
|
||||||
ThreadLocalArrayPool<ulong>.Shared.Return(rentedResults!, clearArray: false);
|
}
|
||||||
|
|
||||||
|
if (rentedResults is not null)
|
||||||
|
{
|
||||||
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedResults, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,21 +182,29 @@ public sealed class DivisorCycleCache
|
|||||||
ulong[]? rentedPow = null;
|
ulong[]? rentedPow = null;
|
||||||
Span<ulong> powSpan = length <= StackBufferThreshold
|
Span<ulong> powSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc ulong[length]
|
||||||
|
: length < PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? new Span<ulong>(new ulong[length])
|
||||||
: new Span<ulong>(rentedPow = ThreadLocalArrayPool<ulong>.Shared.Rent(length), 0, length);
|
: new Span<ulong>(rentedPow = ThreadLocalArrayPool<ulong>.Shared.Rent(length), 0, length);
|
||||||
|
|
||||||
ulong[]? rentedOrder = null;
|
ulong[]? rentedOrder = null;
|
||||||
Span<ulong> orderSpan = length <= StackBufferThreshold
|
Span<ulong> orderSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc ulong[length]
|
||||||
|
: length < PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? new Span<ulong>(new ulong[length])
|
||||||
: new Span<ulong>(rentedOrder = ThreadLocalArrayPool<ulong>.Shared.Rent(length), 0, length);
|
: new Span<ulong>(rentedOrder = ThreadLocalArrayPool<ulong>.Shared.Rent(length), 0, length);
|
||||||
|
|
||||||
ulong[]? rentedResult = null;
|
ulong[]? rentedResult = null;
|
||||||
Span<ulong> resultSpan = length <= StackBufferThreshold
|
Span<ulong> resultSpan = length <= StackBufferThreshold
|
||||||
? stackalloc ulong[length]
|
? stackalloc ulong[length]
|
||||||
|
: length < PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? new Span<ulong>(new ulong[length])
|
||||||
: new Span<ulong>(rentedResult = ThreadLocalArrayPool<ulong>.Shared.Rent(length), 0, length);
|
: new Span<ulong>(rentedResult = ThreadLocalArrayPool<ulong>.Shared.Rent(length), 0, length);
|
||||||
|
|
||||||
byte[]? rentedStatus = null;
|
byte[]? rentedStatus = null;
|
||||||
Span<byte> statusSpan = length <= StackBufferThreshold
|
Span<byte> statusSpan = length <= StackBufferThreshold
|
||||||
? stackalloc byte[length]
|
? stackalloc byte[length]
|
||||||
|
: length < PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? new Span<byte>(new byte[length])
|
||||||
: new Span<byte>(rentedStatus = ThreadLocalArrayPool<byte>.Shared.Rent(length), 0, length);
|
: new Span<byte>(rentedStatus = ThreadLocalArrayPool<byte>.Shared.Rent(length), 0, length);
|
||||||
|
|
||||||
ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors);
|
ref ulong divisorRef = ref MemoryMarshal.GetReference(divisors);
|
||||||
@@ -235,9 +253,21 @@ public sealed class DivisorCycleCache
|
|||||||
if (rentedPow is not null)
|
if (rentedPow is not null)
|
||||||
{
|
{
|
||||||
ThreadLocalArrayPool<ulong>.Shared.Return(rentedPow, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedPow, clearArray: false);
|
||||||
ThreadLocalArrayPool<ulong>.Shared.Return(rentedOrder!, clearArray: false);
|
}
|
||||||
ThreadLocalArrayPool<ulong>.Shared.Return(rentedResult!, clearArray: false);
|
|
||||||
ThreadLocalArrayPool<byte>.Shared.Return(rentedStatus!, clearArray: false);
|
if (rentedOrder is not null)
|
||||||
|
{
|
||||||
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedOrder, clearArray: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rentedResult is not null)
|
||||||
|
{
|
||||||
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedResult, clearArray: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rentedStatus is not null)
|
||||||
|
{
|
||||||
|
ThreadLocalArrayPool<byte>.Shared.Return(rentedStatus, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
execution.Dispose();
|
execution.Dispose();
|
||||||
|
|||||||
@@ -567,7 +567,10 @@ public class MersenneDivisorCycles
|
|||||||
}
|
}
|
||||||
|
|
||||||
int count = factorCounts.Count;
|
int count = factorCounts.Count;
|
||||||
ulong[] primes = ThreadLocalArrayPool<ulong>.Shared.Rent(count);
|
bool rentFromPool = count >= PerfectNumberConstants.PooledArrayThreshold;
|
||||||
|
ulong[] primes = rentFromPool
|
||||||
|
? ThreadLocalArrayPool<ulong>.Shared.Rent(count)
|
||||||
|
: new ulong[count];
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
@@ -606,10 +609,13 @@ public class MersenneDivisorCycles
|
|||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
{
|
||||||
|
if (rentFromPool)
|
||||||
{
|
{
|
||||||
ThreadLocalArrayPool<ulong>.Shared.Return(primes, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(primes, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static ulong PollardRho64(ulong n)
|
private static ulong PollardRho64(ulong n)
|
||||||
{
|
{
|
||||||
@@ -793,7 +799,11 @@ public class MersenneDivisorCycles
|
|||||||
tasks[taskIndex] = Task.Run(() =>
|
tasks[taskIndex] = Task.Run(() =>
|
||||||
{
|
{
|
||||||
ulong rangeLength = localEnd - localStart + 1UL;
|
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;
|
int localCycleIndex = 0;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -840,9 +850,12 @@ public class MersenneDivisorCycles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
{
|
||||||
|
if (rentLocalCycles)
|
||||||
{
|
{
|
||||||
ThreadLocalArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false);
|
ThreadLocalArrayPool<(ulong, ulong)>.Shared.Return(localCycles, clearArray: false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace PerfectNumbers.Core
|
namespace PerfectNumbers.Core
|
||||||
{
|
{
|
||||||
@@ -8,12 +7,20 @@ namespace PerfectNumbers.Core
|
|||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static PartialFactorResult? s_poolHead;
|
private static PartialFactorResult? s_poolHead;
|
||||||
|
|
||||||
|
public static readonly PartialFactorResult Empty = new()
|
||||||
|
{
|
||||||
|
Cofactor = 1UL,
|
||||||
|
FullyFactored = true,
|
||||||
|
};
|
||||||
|
|
||||||
public FactorEntry[]? Factors;
|
public FactorEntry[]? Factors;
|
||||||
private PartialFactorResult? _next;
|
|
||||||
public ulong Cofactor;
|
public ulong Cofactor;
|
||||||
public bool FullyFactored;
|
public bool FullyFactored;
|
||||||
public int Count;
|
public int Count;
|
||||||
|
|
||||||
|
private PartialFactorResult? _next;
|
||||||
|
private bool _factorsFromPool;
|
||||||
|
|
||||||
private PartialFactorResult()
|
private PartialFactorResult()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -30,52 +37,58 @@ namespace PerfectNumbers.Core
|
|||||||
s_poolHead = instance._next;
|
s_poolHead = instance._next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FactorEntry[]? factorArray = factors;
|
||||||
instance._next = null;
|
instance._next = null;
|
||||||
instance.Factors = factors;
|
instance.Factors = factorArray;
|
||||||
instance.Cofactor = cofactor;
|
instance.Cofactor = cofactor;
|
||||||
instance.FullyFactored = fullyFactored;
|
instance.FullyFactored = fullyFactored;
|
||||||
instance.Count = count;
|
instance.Count = count;
|
||||||
|
instance._factorsFromPool = factorArray is not null && factorArray.Length >= PerfectNumberConstants.PooledArrayThreshold;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly PartialFactorResult Empty = new()
|
|
||||||
{
|
|
||||||
Cofactor = 1UL,
|
|
||||||
FullyFactored = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
public PartialFactorResult WithAdditionalPrime(ulong prime)
|
public PartialFactorResult WithAdditionalPrime(ulong prime)
|
||||||
{
|
{
|
||||||
FactorEntry[]? source = Factors;
|
FactorEntry[]? source = Factors;
|
||||||
if (source is null || Count == 0)
|
if (source is null || Count == 0)
|
||||||
{
|
{
|
||||||
FactorEntry[] local = ThreadLocalArrayPool<FactorEntry>.Shared.Rent(1);
|
FactorEntry[] single = new FactorEntry[1];
|
||||||
local[0] = new FactorEntry(prime);
|
single[0] = new FactorEntry(prime);
|
||||||
return Rent(local, 1UL, true, 1);
|
return Rent(single, 1UL, true, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry[] extended = ThreadLocalArrayPool<FactorEntry>.Shared.Rent(Count + 1);
|
int newCount = Count + 1;
|
||||||
|
FactorEntry[] extended = newCount >= PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? ThreadLocalArrayPool<FactorEntry>.Shared.Rent(newCount)
|
||||||
|
: new FactorEntry[newCount];
|
||||||
|
|
||||||
Array.Copy(source, 0, extended, 0, Count);
|
Array.Copy(source, 0, extended, 0, Count);
|
||||||
extended[Count] = new FactorEntry(prime);
|
extended[Count] = new FactorEntry(prime);
|
||||||
Span<FactorEntry> span = extended.AsSpan(0, Count + 1);
|
Span<FactorEntry> span = extended.AsSpan(0, newCount);
|
||||||
span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (this != Empty)
|
if (this == Empty)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FactorEntry[]? factors = Factors;
|
FactorEntry[]? factors = Factors;
|
||||||
if (factors is not null)
|
if (factors is not null)
|
||||||
{
|
{
|
||||||
Factors = null;
|
Factors = null;
|
||||||
|
if (_factorsFromPool)
|
||||||
|
{
|
||||||
ThreadLocalArrayPool<FactorEntry>.Shared.Return(factors, clearArray: false);
|
ThreadLocalArrayPool<FactorEntry>.Shared.Return(factors, clearArray: false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_factorsFromPool = false;
|
||||||
_next = s_poolHead;
|
_next = s_poolHead;
|
||||||
s_poolHead = this;
|
s_poolHead = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1058,7 +1058,10 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
|
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry[] factors = ThreadLocalArrayPool<FactorEntry>.Shared.Rent(counts.Count);
|
int dictionaryCount = counts.Count;
|
||||||
|
FactorEntry[] factors = dictionaryCount >= PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? ThreadLocalArrayPool<FactorEntry>.Shared.Rent(dictionaryCount)
|
||||||
|
: new FactorEntry[dictionaryCount];
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (KeyValuePair<ulong, int> entry in counts)
|
foreach (KeyValuePair<ulong, int> entry in counts)
|
||||||
{
|
{
|
||||||
@@ -1092,7 +1095,9 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
|
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry[] array = ThreadLocalArrayPool<FactorEntry>.Shared.Rent(actualCount);
|
FactorEntry[] array = actualCount >= PerfectNumberConstants.PooledArrayThreshold
|
||||||
|
? ThreadLocalArrayPool<FactorEntry>.Shared.Rent(actualCount)
|
||||||
|
: new FactorEntry[actualCount];
|
||||||
int arrayIndex = 0;
|
int arrayIndex = 0;
|
||||||
for (int i = 0; i < factorCount; i++)
|
for (int i = 0; i < factorCount; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user