Isolate CPU divisor pools per thread
This commit is contained in:
1 parent
cd69c08fb4
commit
8e9390bc5e
7 files changed
+61
-34
No files matched your search
@@ -63,7 +63,7 @@ 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]
|
||||||
: new Span<int>(rentedMissing = ArrayPool<int>.Shared.Rent(length), 0, length);
|
: new Span<int>(rentedMissing = ThreadLocalArrayPool<int>.Shared.Rent(length), 0, length);
|
||||||
|
|
||||||
int missingCount = 0;
|
int missingCount = 0;
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ public sealed class DivisorCycleCache
|
|||||||
|
|
||||||
if (rentedMissing is not null)
|
if (rentedMissing is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<int>.Shared.Return(rentedMissing, clearArray: false);
|
ThreadLocalArrayPool<int>.Shared.Return(rentedMissing, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,12 +128,12 @@ 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]
|
||||||
: new Span<ulong>(rentedDivisors = ArrayPool<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]
|
||||||
: new Span<ulong>(rentedResults = ArrayPool<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++)
|
||||||
{
|
{
|
||||||
@@ -149,8 +149,8 @@ public sealed class DivisorCycleCache
|
|||||||
|
|
||||||
if (rentedDivisors is not null)
|
if (rentedDivisors is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(rentedDivisors, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedDivisors, clearArray: false);
|
||||||
ArrayPool<ulong>.Shared.Return(rentedResults!, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedResults!, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,22 +172,22 @@ 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]
|
||||||
: new Span<ulong>(rentedPow = ArrayPool<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]
|
||||||
: new Span<ulong>(rentedOrder = ArrayPool<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]
|
||||||
: new Span<ulong>(rentedResult = ArrayPool<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]
|
||||||
: new Span<byte>(rentedStatus = ArrayPool<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);
|
||||||
divisorBuffer.View.CopyFromCPU(ref divisorRef, length);
|
divisorBuffer.View.CopyFromCPU(ref divisorRef, length);
|
||||||
@@ -234,10 +234,10 @@ public sealed class DivisorCycleCache
|
|||||||
resultSpan.CopyTo(destination);
|
resultSpan.CopyTo(destination);
|
||||||
if (rentedPow is not null)
|
if (rentedPow is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(rentedPow, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedPow, clearArray: false);
|
||||||
ArrayPool<ulong>.Shared.Return(rentedOrder!, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedOrder!, clearArray: false);
|
||||||
ArrayPool<ulong>.Shared.Return(rentedResult!, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(rentedResult!, clearArray: false);
|
||||||
ArrayPool<byte>.Shared.Return(rentedStatus!, clearArray: false);
|
ThreadLocalArrayPool<byte>.Shared.Return(rentedStatus!, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
execution.Dispose();
|
execution.Dispose();
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ public class MersenneDivisorCycles
|
|||||||
}
|
}
|
||||||
|
|
||||||
int count = factorCounts.Count;
|
int count = factorCounts.Count;
|
||||||
ulong[] primes = ArrayPool<ulong>.Shared.Rent(count);
|
ulong[] primes = ThreadLocalArrayPool<ulong>.Shared.Rent(count);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
@@ -607,7 +607,7 @@ public class MersenneDivisorCycles
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(primes, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(primes, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,7 +793,7 @@ 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 = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength));
|
(ulong divisor, ulong cycle)[] localCycles = ThreadLocalArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength));
|
||||||
int localCycleIndex = 0;
|
int localCycleIndex = 0;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -841,7 +841,7 @@ public class MersenneDivisorCycles
|
|||||||
}
|
}
|
||||||
finally
|
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;
|
ulong start = 3UL;
|
||||||
var locker = new object();
|
var locker = new object();
|
||||||
|
|
||||||
var pool = ArrayPool<ulong>.Shared;
|
var pool = ThreadLocalArrayPool<ulong>.Shared;
|
||||||
ulong batchSizeUL = (ulong)batchSize, d, end;
|
ulong batchSizeUL = (ulong)batchSize, d, end;
|
||||||
int count = (int)Math.Min(batchSizeUL, maxDivisor), i, idx;
|
int count = (int)Math.Min(batchSizeUL, maxDivisor), i, idx;
|
||||||
ulong[] divisors, outCycles, validDivisors;
|
ulong[] divisors, outCycles, validDivisors;
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ namespace PerfectNumbers.Core
|
|||||||
FactorEntry[]? source = Factors;
|
FactorEntry[]? source = Factors;
|
||||||
if (source is null || Count == 0)
|
if (source is null || Count == 0)
|
||||||
{
|
{
|
||||||
FactorEntry[] local = ArrayPool<FactorEntry>.Shared.Rent(1);
|
FactorEntry[] local = ThreadLocalArrayPool<FactorEntry>.Shared.Rent(1);
|
||||||
local[0] = new FactorEntry(prime);
|
local[0] = new FactorEntry(prime);
|
||||||
return Rent(local, 1UL, true, 1);
|
return Rent(local, 1UL, true, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry[] extended = ArrayPool<FactorEntry>.Shared.Rent(Count + 1);
|
FactorEntry[] extended = ThreadLocalArrayPool<FactorEntry>.Shared.Rent(Count + 1);
|
||||||
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, Count + 1);
|
||||||
@@ -70,7 +70,7 @@ namespace PerfectNumbers.Core
|
|||||||
if (factors is not null)
|
if (factors is not null)
|
||||||
{
|
{
|
||||||
Factors = null;
|
Factors = null;
|
||||||
ArrayPool<FactorEntry>.Shared.Return(factors, clearArray: false);
|
ThreadLocalArrayPool<FactorEntry>.Shared.Return(factors, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_next = s_poolHead;
|
_next = s_poolHead;
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ namespace PerfectNumbers.Core
|
|||||||
{
|
{
|
||||||
if (_primes is not null)
|
if (_primes is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(_primes, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(_primes, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_exponents is not null)
|
if (_exponents is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<byte>.Shared.Return(_exponents, clearArray: false);
|
ThreadLocalArrayPool<byte>.Shared.Return(_exponents, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_arraysFromPool = false;
|
_arraysFromPool = false;
|
||||||
@@ -119,12 +119,12 @@ namespace PerfectNumbers.Core
|
|||||||
{
|
{
|
||||||
if (_primes is not null)
|
if (_primes is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(_primes, clearArray: false);
|
ThreadLocalArrayPool<ulong>.Shared.Return(_primes, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_exponents is not null)
|
if (_exponents is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<byte>.Shared.Return(_exponents, clearArray: false);
|
ThreadLocalArrayPool<byte>.Shared.Return(_exponents, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_arraysFromPool = false;
|
_arraysFromPool = false;
|
||||||
@@ -134,8 +134,8 @@ namespace PerfectNumbers.Core
|
|||||||
|
|
||||||
if (capacity >= PerfectNumberConstants.PooledArrayThreshold)
|
if (capacity >= PerfectNumberConstants.PooledArrayThreshold)
|
||||||
{
|
{
|
||||||
_primes = ArrayPool<ulong>.Shared.Rent(capacity);
|
_primes = ThreadLocalArrayPool<ulong>.Shared.Rent(capacity);
|
||||||
_exponents = ArrayPool<byte>.Shared.Rent(capacity);
|
_exponents = ThreadLocalArrayPool<byte>.Shared.Rent(capacity);
|
||||||
_arraysFromPool = true;
|
_arraysFromPool = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
tempArray = ArrayPool<FactorEntry128>.Shared.Rent(length + 1);
|
tempArray = ThreadLocalArrayPool<FactorEntry128>.Shared.Rent(length + 1);
|
||||||
Span<FactorEntry128> buffer = tempArray.AsSpan(0, length);
|
Span<FactorEntry128> buffer = tempArray.AsSpan(0, length);
|
||||||
factorSpan.CopyTo(buffer);
|
factorSpan.CopyTo(buffer);
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
{
|
{
|
||||||
if (tempArray is not null)
|
if (tempArray is not null)
|
||||||
{
|
{
|
||||||
ArrayPool<FactorEntry128>.Shared.Return(tempArray, clearArray: false);
|
ThreadLocalArrayPool<FactorEntry128>.Shared.Return(tempArray, clearArray: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -664,7 +664,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return new PartialFactorResult128(null, value, false, 0);
|
return new PartialFactorResult128(null, value, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry128[]? rented = counts.Count > 0 ? ArrayPool<FactorEntry128>.Shared.Rent(counts.Count) : null;
|
FactorEntry128[]? rented = counts.Count > 0 ? ThreadLocalArrayPool<FactorEntry128>.Shared.Rent(counts.Count) : null;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (rented is not null)
|
if (rented is not null)
|
||||||
{
|
{
|
||||||
@@ -682,7 +682,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
|
span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
|
||||||
resultArray = new FactorEntry128[index];
|
resultArray = new FactorEntry128[index];
|
||||||
span.CopyTo(resultArray);
|
span.CopyTo(resultArray);
|
||||||
ArrayPool<FactorEntry128>.Shared.Return(rented, clearArray: false);
|
ThreadLocalArrayPool<FactorEntry128>.Shared.Return(rented, clearArray: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fullyFactored = cofactor == UInt128.One;
|
bool fullyFactored = cofactor == UInt128.One;
|
||||||
|
|||||||
@@ -1058,7 +1058,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
|
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry[] factors = ArrayPool<FactorEntry>.Shared.Rent(counts.Count);
|
FactorEntry[] factors = ThreadLocalArrayPool<FactorEntry>.Shared.Rent(counts.Count);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (KeyValuePair<ulong, int> entry in counts)
|
foreach (KeyValuePair<ulong, int> entry in counts)
|
||||||
{
|
{
|
||||||
@@ -1092,7 +1092,7 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
|
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry[] array = ArrayPool<FactorEntry>.Shared.Rent(actualCount);
|
FactorEntry[] array = ThreadLocalArrayPool<FactorEntry>.Shared.Rent(actualCount);
|
||||||
int arrayIndex = 0;
|
int arrayIndex = 0;
|
||||||
for (int i = 0; i < factorCount; i++)
|
for (int i = 0; i < factorCount; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
|
internal static class ThreadLocalArrayPool<T>
|
||||||
|
{
|
||||||
|
[ThreadStatic]
|
||||||
|
private static ArrayPool<T>? s_threadPool;
|
||||||
|
|
||||||
|
public static ArrayPool<T> Shared
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ArrayPool<T>? pool = s_threadPool;
|
||||||
|
if (pool is null)
|
||||||
|
{
|
||||||
|
pool = ArrayPool<T>.Create();
|
||||||
|
s_threadPool = pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user