Pools renamed to FixedCapacityPools. Other fixed capacity collections added. Queues, concurrent queues and standard stacks replaced with fixed capacity stacks. MethodImplOptions.AggressiveInlining and .AggressiveOptimization added to simple methods and getters / setters. Variables optimized in few places.

This commit is contained in:
mlemanczyk committed 2025-12-11 21:10:19 +01:00
1 parent 547d43de84
commit 260aa19e44
25 files changed
+565 -493

No files matched your search

-104
View File
@@ -1,104 +0,0 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core
{
public static class Pools
{
private const int PoolCapacity = 10240; //PerfectNumberConstants.DefaultSmallPrimeFactorSlotCount << 8;
[ThreadStatic]
private static IntArrayPool? _intArrayPool;
internal static IntArrayPool ExclusiveIntArray
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _intArrayPool ??= new(PoolCapacity);
}
}
[ThreadStatic]
private static UlongArrayPool? _ulongArrayPool;
internal static UlongArrayPool ExclusiveUlongArray
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _ulongArrayPool ??= new(PoolCapacity);
}
}
}
public struct IntArrayPool(int poolCapacity)
{
private readonly int[][] _arrays = new int[poolCapacity][];
private int _count = 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int[] Rent(int length)
{
int available = _count;
if (available == 0)
{
return new int[length];
}
int[][] arrays = _arrays;
available--;
_count = available;
int[] candidate = arrays[available];
return candidate.Length >= length ? candidate : new int[length];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(int[] array)
{
int[][] arrays = _arrays;
int available = _count;
if (available < arrays.Length)
{
arrays[available] = array;
_count = available + 1;
}
}
}
public struct UlongArrayPool(int poolCapacity)
{
private readonly ulong[][] _arrays = new ulong[poolCapacity][];
private int _count = 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong[] Rent(int length)
{
int available = _count;
if (available == 0)
{
return new ulong[length];
}
ulong[][] arrays = _arrays;
available--;
_count = available;
ulong[] candidate = arrays[available];
return candidate.Length >= length ? candidate : new ulong[length];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(ulong[] array)
{
ulong[][] arrays = _arrays;
int available = _count;
if (available < arrays.Length)
{
arrays[available] = array;
_count = available + 1;
}
}
}
}
@@ -0,0 +1,77 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core;
public sealed class ConcurrentFixedCapacityStack<T>(int capacity)
{
private static readonly FixedCapacityArrayPool<T> _arrayPool = FixedCapacityPools<T>.ExclusiveArray;
private readonly T[] _items = _arrayPool.Rent(capacity);
private readonly int _capacity = capacity;
private int _count = 0;
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => Volatile.Read(ref _count);
}
public int Capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _capacity;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Clear() => Interlocked.Exchange(ref _count, 0);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public T? Pop() => TryPop(out T? item) ? item : default;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool TryPop(out T? item)
{
while (true)
{
int observed = Volatile.Read(ref _count);
if (observed == 0)
{
item = default;
return false;
}
int next = observed - 1;
if (Interlocked.CompareExchange(ref _count, next, observed) == observed)
{
item = _items[next];
return true;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Push(T item)
{
int next = Interlocked.Increment(ref _count);
if (next <= _capacity)
{
_items[next - 1] = item;
return;
}
Interlocked.Decrement(ref _count);
throw new InvalidOperationException("Stack is full.");
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return(in T[] items) => _arrayPool.Return(items);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public T[] ToArray()
{
int count = Volatile.Read(ref _count);
T[] result = _arrayPool.Rent(count);
Array.Copy(_items, result, count);
return result;
}
}
@@ -157,18 +157,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
} }
} }
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu) [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
{ public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu) => ThreadStaticPools.RentMersenneCpuDivisorSession(gpu, _orderDevice);
MersenneCpuDivisorScanSession? session = ThreadStaticPools.RentMersenneCpuDivisorSession();
if (session is not null)
{
session.Configure(gpu, _orderDevice);
return session;
}
return new MersenneCpuDivisorScanSession(gpu, _orderDevice);
}
private bool CheckDivisors( private bool CheckDivisors(
PrimeOrderCalculatorAccelerator gpu, PrimeOrderCalculatorAccelerator gpu,
@@ -564,7 +554,7 @@ private bool CheckDivisors64(
out ulong foundDivisor) out ulong foundDivisor)
{ {
bool canAdvance = step <= limit; bool canAdvance = step <= limit;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
if (!canAdvance) if (!canAdvance)
{ {
if (divisor <= limit && residueStepper.IsAdmissible()) if (divisor <= limit && residueStepper.IsAdmissible())
+7 -5
View File
@@ -55,6 +55,7 @@ public sealed class DivisorCycleCache
{ {
if (!skipPrimeOrderHeuristic) if (!skipPrimeOrderHeuristic)
{ {
// TODO: Implement array-free version of GetCycleLengthsCpu so that we don't pay the array costs for single checks.
Span<ulong> result = stackalloc ulong[1]; Span<ulong> result = stackalloc ulong[1];
ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor }; ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor };
GetCycleLengthsCpu(singleDivisor, result); GetCycleLengthsCpu(singleDivisor, result);
@@ -82,7 +83,7 @@ public sealed class DivisorCycleCache
return cycleLength; return cycleLength;
} }
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
cycleLength = MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData, skipPrimeOrderHeuristic: true); cycleLength = MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData, skipPrimeOrderHeuristic: true);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -93,6 +94,7 @@ public sealed class DivisorCycleCache
{ {
if (!skipPrimeOrderHeuristic) if (!skipPrimeOrderHeuristic)
{ {
// TODO: Implement array-free version of GetCycleLengthsHybrid so that we don't pay the array costs for single checks.
Span<ulong> result = stackalloc ulong[1]; Span<ulong> result = stackalloc ulong[1];
ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor }; ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor };
GetCycleLengthsHybrid(gpu, singleDivisor, result); GetCycleLengthsHybrid(gpu, singleDivisor, result);
@@ -120,7 +122,7 @@ public sealed class DivisorCycleCache
return cycleLength; return cycleLength;
} }
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
cycleLength = MersenneDivisorCycles.CalculateCycleLengthHybrid(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true); cycleLength = MersenneDivisorCycles.CalculateCycleLengthHybrid(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -158,7 +160,7 @@ public sealed class DivisorCycleCache
return cycleLength; return cycleLength;
} }
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
cycleLength = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true); cycleLength = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -346,7 +348,7 @@ public sealed class DivisorCycleCache
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ComputeCyclesCpu(ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices) private static void ComputeCyclesCpu(ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
{ {
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
for (int i = 0; i < indices.Length; i++) for (int i = 0; i < indices.Length; i++)
{ {
int targetIndex = indices[i]; int targetIndex = indices[i];
@@ -360,7 +362,7 @@ public sealed class DivisorCycleCache
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ComputeCyclesHybrid(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices) private static void ComputeCyclesHybrid(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
{ {
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
for (int i = 0; i < indices.Length; i++) for (int i = 0; i < indices.Length; i++)
{ {
int targetIndex = indices[i]; int targetIndex = indices[i];
@@ -0,0 +1,40 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core
{
public struct FixedCapacityArrayPool<T>(int poolCapacity)
{
private readonly T[][] _arrays = new T[poolCapacity][];
private int _count = 0;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public T[] Rent(int length)
{
int available = _count;
if (available == 0)
{
return new T[length];
}
T[][] arrays = _arrays;
available--;
_count = available;
T[] candidate = arrays[available];
return candidate.Length >= length ? candidate : new T[length];
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Return(T[] array)
{
T[][] arrays = _arrays;
int available = _count;
if (available < arrays.Length)
{
arrays[available] = array;
_count = available + 1;
}
}
}
}
+37
View File
@@ -0,0 +1,37 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core
{
public static class FixedCapacityPools<T>
{
[ThreadStatic]
private static FixedCapacityArrayPool<T>? _arrayPool;
internal static FixedCapacityArrayPool<T> ExclusiveArray
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _arrayPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
}
}
public static class FixedCapacityPools
{
[ThreadStatic]
private static IntArrayPool? _intArrayPool;
internal static IntArrayPool ExclusiveIntArray
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _intArrayPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
}
[ThreadStatic]
private static UlongArrayPool? _ulongArrayPool;
internal static UlongArrayPool ExclusiveUlongArray
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _ulongArrayPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
}
}
}
+46
View File
@@ -0,0 +1,46 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core;
public sealed class FixedCapacityStack<T>(int capacity)
{
private static readonly FixedCapacityArrayPool<T> _arrayPool = FixedCapacityPools<T>.ExclusiveArray;
public int Count = 0;
private T[] _items = _arrayPool.Rent(capacity);
private int _capacity = capacity;
public int Capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _capacity;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
set
{
if (value < _capacity)
{
_arrayPool.Return(_items);
_items = _arrayPool.Rent(value);
_capacity = value;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Clear() => Count = 0;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public T? Pop() => Count > 0 ? _items[Count--] : default;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Push(T item)
{
int newCount = Count + 1;
if (newCount < _items.Length)
{
_items[newCount] = item;
Count = newCount;
}
}
}
@@ -1,4 +1,4 @@
using System.Collections.Concurrent; using System.Runtime.CompilerServices;
using ILGPU.Runtime; using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core.Gpu.Accelerators;
@@ -7,13 +7,14 @@ public static class AcceleratorStreamPool
{ {
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private static readonly SemaphoreSlim[] _locks = new SemaphoreSlim[PerfectNumberConstants.RollingAccelerators]; private static readonly SemaphoreSlim[] _locks = new SemaphoreSlim[PerfectNumberConstants.RollingAccelerators];
private static readonly ConcurrentQueue<AcceleratorStream>[] _streams = new ConcurrentQueue<AcceleratorStream> [PerfectNumberConstants.RollingAccelerators]; private static readonly ConcurrentFixedCapacityStack<AcceleratorStream>[] _streams = new ConcurrentFixedCapacityStack<AcceleratorStream>[PerfectNumberConstants.RollingAccelerators];
private static SemaphoreSlim CreateLock() => new(PerfectNumberConstants.ThreadsByAccelerator); private static SemaphoreSlim CreateLock() => new(PerfectNumberConstants.ThreadsByAccelerator);
// private static int _rented; // private static int _rented;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static AcceleratorStream Rent(int acceleratorIndex) public static AcceleratorStream Rent(int acceleratorIndex)
{ {
var streamLock = _locks[acceleratorIndex]; var streamLock = _locks[acceleratorIndex];
@@ -21,22 +22,23 @@ public static class AcceleratorStreamPool
streamLock.Wait(); streamLock.Wait();
return queue.TryDequeue(out var stream) return queue.Pop() is { } stream
? stream ? stream
: _accelerators[acceleratorIndex].CreateStream(); : _accelerators[acceleratorIndex].CreateStream();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return(int acceleratorIndex, AcceleratorStream stream) public static void Return(int acceleratorIndex, AcceleratorStream stream)
{ {
var streamLock = _locks[acceleratorIndex]; var streamLock = _locks[acceleratorIndex];
var queue = _streams[acceleratorIndex]; var queue = _streams[acceleratorIndex];
queue.Enqueue(stream); queue.Push(stream);
streamLock.Release(); streamLock.Release();
} }
public static void WarmUp(int acceleratorIndex) public static void WarmUp(int acceleratorIndex)
{ {
_streams[acceleratorIndex] = new(); _streams[acceleratorIndex] = new(PerfectNumberConstants.DefaultPoolCapacity);
_locks[acceleratorIndex] = CreateLock(); _locks[acceleratorIndex] = CreateLock();
} }
} }
@@ -1,4 +1,3 @@
using System.Collections.Concurrent;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
@@ -11,34 +10,31 @@ public sealed class PrimeOrderCalculatorAccelerator
#region Pool #region Pool
// [ThreadStatic] // [ThreadStatic]
private static ConcurrentQueue<PrimeOrderCalculatorAccelerator>? _pool; private static ConcurrentFixedCapacityStack<PrimeOrderCalculatorAccelerator>? _pool;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PrimeOrderCalculatorAccelerator Rent(int primeTesterCapacity) public static PrimeOrderCalculatorAccelerator Rent(int primeTesterCapacity)
{ {
var pool = _pool ??= []; var pool = _pool ??= new(PerfectNumberConstants.DefaultThreadPoolCapacity);
if (!pool.TryDequeue(out var gpu)) if (pool.Pop() is { } gpu)
{
int acceleratorIndex = AcceleratorPool.Shared.Rent();
gpu = new(acceleratorIndex, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity, PerfectNumberConstants.DefaultSmallPrimeFactorSlotCount, PerfectNumberConstants.DefaultSpecialMaxFactorCapacity);
}
else
{ {
gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity); gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity);
}
return gpu; return gpu;
} }
int acceleratorIndex = AcceleratorPool.Shared.Rent();
return new(acceleratorIndex, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity, PerfectNumberConstants.DefaultSmallPrimeFactorSlotCount, PerfectNumberConstants.DefaultSpecialMaxFactorCapacity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Return(PrimeOrderCalculatorAccelerator gpu) => _pool!.Enqueue(gpu); public static void Return(PrimeOrderCalculatorAccelerator gpu) => _pool!.Push(gpu);
internal static void Clear() internal static void Clear()
{ {
var pool = _pool; var pool = _pool;
if (pool != null) if (pool != null)
{ {
while (pool.TryDequeue(out var gpu)) while (pool.Pop() is { } gpu)
{ {
gpu.Dispose(); gpu.Dispose();
} }
@@ -50,7 +46,7 @@ public sealed class PrimeOrderCalculatorAccelerator
var pool = _pool; var pool = _pool;
if (pool != null) if (pool != null)
{ {
while (pool.TryDequeue(out var gpu)) while (pool.Pop() is { } gpu)
{ {
gpu.Dispose(); gpu.Dispose();
} }
@@ -22,9 +22,9 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
private BigInteger _minK = BigInteger.One; private BigInteger _minK = BigInteger.One;
// private bool _isConfigured; // private bool _isConfigured;
private readonly ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>[] _resourcePool = [.. AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>())]; private readonly ConcurrentFixedCapacityStack<MersenneNumberDivisorByDivisorAccelerator>[] _resourcePool = [.. AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentFixedCapacityStack<MersenneNumberDivisorByDivisorAccelerator>(PerfectNumberConstants.DefaultPoolCapacity))];
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = []; private readonly ConcurrentFixedCapacityStack<DivisorScanSession> _sessionPool = new(PerfectNumberConstants.DefaultPoolCapacity);
public int GpuBatchSize public int GpuBatchSize
{ {
@@ -272,7 +272,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
ArrayView1D<ulong, Stride1D.Dense> exponentViewDevice = exponentBuffer.View; ArrayView1D<ulong, Stride1D.Dense> exponentViewDevice = exponentBuffer.View;
ArrayView1D<byte, Stride1D.Dense> hitsView = hitsBuffer.View; ArrayView1D<byte, Stride1D.Dense> hitsView = hitsBuffer.View;
ArrayView1D<int, Stride1D.Dense> hitIndexView = hitIndexBuffer.View; ArrayView1D<int, Stride1D.Dense> hitIndexView = hitIndexBuffer.View;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
static bool ProcessRange( static bool ProcessRange(
PrimeOrderCalculatorAccelerator gpu, PrimeOrderCalculatorAccelerator gpu,
ulong prime, ulong prime,
@@ -291,7 +291,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
Span<int> offsetSpan, Span<int> offsetSpan,
Span<int> countSpan, Span<int> countSpan,
Span<ulong> cycleSpan, Span<ulong> cycleSpan,
Queue<MontgomeryDivisorData> divisorPool, FixedCapacityStack<MontgomeryDivisorData> divisorPool,
int chunkCountBaseline, int chunkCountBaseline,
int acceleratorIndex, int acceleratorIndex,
ref bool composite, ref bool composite,
@@ -598,6 +598,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
_kernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>(); _kernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal void Reset() internal void Reset()
{ {
} }
@@ -706,15 +707,13 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
return sentinel == 0; return sentinel == 0;
} }
public void Return() [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
{ public void Return() => _owner._sessionPool.Push(this);
_owner._sessionPool.Add(this);
} }
}
public ulong DivisorLimit public ulong DivisorLimit
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get
{ {
// The EvenPerfectBitScanner driver configures the GPU tester before exposing it to callers, so the previous synchronization guard remains commented out. // The EvenPerfectBitScanner driver configures the GPU tester before exposing it to callers, so the previous synchronization guard remains commented out.
@@ -732,22 +731,22 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private MersenneNumberDivisorByDivisorAccelerator RentBatchResources(int acceleratorIndex, int capacity) private MersenneNumberDivisorByDivisorAccelerator RentBatchResources(int acceleratorIndex, int capacity)
{ {
var queue = _resourcePool[acceleratorIndex]; var queue = _resourcePool[acceleratorIndex];
var accelerator = AcceleratorPool.Shared.Accelerators[acceleratorIndex]; var accelerator = AcceleratorPool.Shared.Accelerators[acceleratorIndex];
if (!queue.TryTake(out MersenneNumberDivisorByDivisorAccelerator? resources)) if (queue.Pop() is { } resources)
{ {
return new MersenneNumberDivisorByDivisorAccelerator(accelerator, capacity);
}
resources.EnsureCapacity(accelerator, capacity); resources.EnsureCapacity(accelerator, capacity);
return resources; return resources;
} }
private void ReturnBatchResources(int acceleratorIndex, MersenneNumberDivisorByDivisorAccelerator resources) => _resourcePool[acceleratorIndex].Add(resources); return new MersenneNumberDivisorByDivisorAccelerator(accelerator, capacity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void ReturnBatchResources(int acceleratorIndex, MersenneNumberDivisorByDivisorAccelerator resources) => _resourcePool[acceleratorIndex].Push(resources);
private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator> private sealed class AcceleratorReferenceComparer : IEqualityComparer<Accelerator>
{ {
@@ -758,6 +757,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
public int GetHashCode(Accelerator obj) => RuntimeHelpers.GetHashCode(obj); public int GetHashCode(Accelerator obj) => RuntimeHelpers.GetHashCode(obj);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public ulong GetAllowedMaxDivisor(ulong prime) public ulong GetAllowedMaxDivisor(ulong prime)
{ {
// The configuration is immutable after setup, so the cached limit can be read without locking. // The configuration is immutable after setup, so the cached limit can be read without locking.
@@ -774,6 +774,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
return ComputeAllowedMaxDivisorGpu(prime, _divisorLimit); return ComputeAllowedMaxDivisorGpu(prime, _divisorLimit);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu) public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
{ {
// The tester is configured once at startup and the session pool relies on thread-safe collections, so the previous synchronization guard stays commented out. // The tester is configured once at startup and the session pool relies on thread-safe collections, so the previous synchronization guard stays commented out.
@@ -793,7 +794,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
// return new DivisorScanSession(this); // return new DivisorScanSession(this);
// } // }
if (_sessionPool.TryTake(out DivisorScanSession? session)) if (_sessionPool.Pop() is { } session)
{ {
session.Reset(); session.Reset();
return session; return session;
+40
View File
@@ -0,0 +1,40 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core
{
public struct IntArrayPool(int poolCapacity)
{
private readonly int[][] _arrays = new int[poolCapacity][];
private int _count = 0;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public int[] Rent(int length)
{
int available = _count;
if (available == 0)
{
return new int[length];
}
int[][] arrays = _arrays;
available--;
_count = available;
int[] candidate = arrays[available];
return candidate.Length >= length ? candidate : new int[length];
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Return(int[] array)
{
int[][] arrays = _arrays;
int available = _count;
if (available < arrays.Length)
{
arrays[available] = array;
_count = available + 1;
}
}
}
}
+4 -4
View File
@@ -394,7 +394,7 @@ public class MersenneDivisorCycles
if (divisor <= ulong.MaxValue) if (divisor <= ulong.MaxValue)
{ {
ulong divisor64 = (ulong)divisor; ulong divisor64 = (ulong)divisor;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64);
ulong cycle = CalculateCycleLengthCpu(divisor64, divisorData); ulong cycle = CalculateCycleLengthCpu(divisor64, divisorData);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -441,7 +441,7 @@ public class MersenneDivisorCycles
if (divisor <= ulong.MaxValue) if (divisor <= ulong.MaxValue)
{ {
ulong divisor64 = (ulong)divisor; ulong divisor64 = (ulong)divisor;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64);
ulong cycle = CalculateCycleLengthHybrid(gpu, divisor64, divisorData); ulong cycle = CalculateCycleLengthHybrid(gpu, divisor64, divisorData);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -489,7 +489,7 @@ public class MersenneDivisorCycles
if (divisor <= ulong.MaxValue) if (divisor <= ulong.MaxValue)
{ {
ulong divisor64 = (ulong)divisor; ulong divisor64 = (ulong)divisor;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64);
ulong cycle = CalculateCycleLengthGpu(gpu, divisor64, divisorData); ulong cycle = CalculateCycleLengthGpu(gpu, divisor64, divisorData);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -1161,7 +1161,6 @@ public class MersenneDivisorCycles
ulong localStart = threadStart; ulong localStart = threadStart;
ulong localEnd = threadEnd; ulong localEnd = threadEnd;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
tasks[taskIndex] = Task.Run(() => tasks[taskIndex] = Task.Run(() =>
{ {
ulong rangeLength = localEnd - localStart + 1UL; ulong rangeLength = localEnd - localStart + 1UL;
@@ -1169,6 +1168,7 @@ public class MersenneDivisorCycles
// (ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength)); // (ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength));
int localCycleIndex = 0; int localCycleIndex = 0;
FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (ulong divisor = localStart; divisor <= localEnd; divisor++) for (ulong divisor = localStart; divisor <= localEnd; divisor++)
{ {
@@ -1,4 +1,3 @@
using System.Collections.Concurrent;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Numerics; using System.Numerics;
using UInt128 = System.UInt128; using UInt128 = System.UInt128;
@@ -185,36 +184,40 @@ public static class MersennePrimeFactorTester
return result; return result;
} }
private static readonly ConcurrentBag<List<ulong>> ListPool = new(); private static readonly ConcurrentFixedCapacityStack<List<ulong>> ListPool = new(PerfectNumberConstants.DefaultPoolCapacity);
private static readonly ConcurrentBag<Dictionary<ulong, byte>> DictPool = new(); private static readonly ConcurrentFixedCapacityStack<Dictionary<ulong, byte>> DictPool = new(PerfectNumberConstants.DefaultPoolCapacity);
private static readonly Dictionary<ulong, ulong[]> FactorCache = new();
private static readonly object FactorCacheLock = new();
private static List<ulong> RentList() => ListPool.TryTake(out var list) ? list : new List<ulong>(4); [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static List<ulong> RentList() => ListPool.Pop() is { } list ? list : new List<ulong>(4);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void ReturnList(List<ulong> list) private static void ReturnList(List<ulong> list)
{ {
list.Clear(); list.Clear();
ListPool.Add(list); ListPool.Push(list);
} }
private static Dictionary<ulong, byte> RentDictionary() => DictPool.TryTake(out var dict) ? dict : new Dictionary<ulong, byte>(); [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static Dictionary<ulong, byte> RentDictionary() => DictPool.Pop() is { } dict ? dict : [];
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void ReturnDictionary(Dictionary<ulong, byte> dict) private static void ReturnDictionary(Dictionary<ulong, byte> dict)
{ {
dict.Clear(); dict.Clear();
DictPool.Add(dict); DictPool.Push(dict);
} }
private static readonly ConcurrentBag<Dictionary<UInt128, byte>> DictPool128 = new(); private static readonly ConcurrentFixedCapacityStack<Dictionary<UInt128, byte>> DictPool128 = new(PerfectNumberConstants.DefaultPoolCapacity);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static Dictionary<UInt128, byte> RentDictionary128() => private static Dictionary<UInt128, byte> RentDictionary128() =>
DictPool128.TryTake(out var dict) ? dict : new Dictionary<UInt128, byte>(); DictPool128.Pop() is { } dict ? dict : [];
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void ReturnDictionary128(Dictionary<UInt128, byte> dict) private static void ReturnDictionary128(Dictionary<UInt128, byte> dict)
{ {
dict.Clear(); dict.Clear();
DictPool128.Add(dict); DictPool128.Push(dict);
} }
private static ulong[] Factor64(ulong n, CancellationToken ct) private static ulong[] Factor64(ulong n, CancellationToken ct)
@@ -233,10 +236,10 @@ public static class MersennePrimeFactorTester
} }
var dict = RentDictionary(); var dict = RentDictionary();
var queue = new Queue<ulong>(); // TODO: Replace this Queue with the pooled stack from the var queue = ThreadStaticPools.RentUlongStack(PerfectNumberConstants.DefaultPoolCapacity); // TODO: Replace this Queue with the pooled stack from the
// FactorizationBenchmarks fast path so factoring large // FactorizationBenchmarks fast path so factoring large
// composites avoids per-node allocations. // composites avoids per-node allocations.
queue.Enqueue(n); queue.Push(n);
while (queue.Count != 0) while (queue.Count != 0)
{ {
@@ -245,7 +248,7 @@ public static class MersennePrimeFactorTester
break; break;
} }
ulong m = queue.Dequeue(); ulong m = queue.Pop();
if (m == 1UL) if (m == 1UL)
{ {
continue; continue;
@@ -273,10 +276,11 @@ public static class MersennePrimeFactorTester
break; break;
} }
queue.Enqueue(d); queue.Push(d);
queue.Enqueue(m / d); queue.Push(m / d);
} }
ThreadStaticPools.ReturnUlongStack(queue);
ulong[] result = [.. dict.Keys]; ulong[] result = [.. dict.Keys];
// TODO: Rent the result array from ArrayPool once the factoring pipeline consumes spans so we // TODO: Rent the result array from ArrayPool once the factoring pipeline consumes spans so we
// can recycle buffers across large factorizations. // can recycle buffers across large factorizations.
@@ -375,24 +379,9 @@ public static class MersennePrimeFactorTester
} }
x = x.MulMod64(x, n).AddMod64(c, n); x = x.MulMod64(x, n).AddMod64(c, n);
if (ct.IsCancellationRequested)
{
return 0UL;
}
y = y.MulMod64(y, n).AddMod64(c, n); y = y.MulMod64(y, n).AddMod64(c, n);
if (ct.IsCancellationRequested)
{
return 0UL;
}
y = y.MulMod64(y, n).AddMod64(c, n); y = y.MulMod64(y, n).AddMod64(c, n);
diff = x > y ? x - y : y - x; diff = x > y ? x - y : y - x;
if (ct.IsCancellationRequested)
{
return 0UL;
}
d = Gcd(diff, n, ct); d = Gcd(diff, n, ct);
} }
@@ -5,10 +5,14 @@ namespace PerfectNumbers.Core;
public static class MontgomeryDivisorDataPool public static class MontgomeryDivisorDataPool
{ {
[ThreadStatic] [ThreadStatic]
private static Queue<MontgomeryDivisorData>? _pool; private static FixedCapacityStack<MontgomeryDivisorData>? _pool;
public static Queue<MontgomeryDivisorData> Shared => _pool ??= new(); public static FixedCapacityStack<MontgomeryDivisorData> Shared
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _pool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static ulong ComputeMontgomeryResidue(UInt128 value, ulong modulus) => (ulong)(value % modulus); private static ulong ComputeMontgomeryResidue(UInt128 value, ulong modulus) => (ulong)(value % modulus);
private static ulong ComputeMontgomeryNPrime(ulong modulus) private static ulong ComputeMontgomeryNPrime(ulong modulus)
@@ -24,10 +28,11 @@ public static class MontgomeryDivisorDataPool
return unchecked(0UL - inv); return unchecked(0UL - inv);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static MontgomeryDivisorData Rent(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared) public static MontgomeryDivisorData Rent(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
{ {
var pool = _pool ??= new(); var pool = _pool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
if (pool.TryDequeue(out var pooled)) if (pool.Pop() is { } pooled)
{ {
pooled.Modulus = modulus; pooled.Modulus = modulus;
pooled.NPrime = nPrime; pooled.NPrime = nPrime;
@@ -40,12 +45,12 @@ public static class MontgomeryDivisorDataPool
return new(modulus, nPrime, montgomeryOne, montgomeryTwo, montgomeryTwoSquared); return new(modulus, nPrime, montgomeryOne, montgomeryTwo, montgomeryTwoSquared);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return(MontgomeryDivisorData divisorData) => _pool!.Enqueue(divisorData); public static void Return(MontgomeryDivisorData divisorData) => _pool!.Push(divisorData);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static MontgomeryDivisorData FromModulus(this Queue<MontgomeryDivisorData> queue, ulong modulus) public static MontgomeryDivisorData FromModulus(this FixedCapacityStack<MontgomeryDivisorData> queue, ulong modulus)
{ {
ulong nPrime = ComputeMontgomeryNPrime(modulus); ulong nPrime = ComputeMontgomeryNPrime(modulus);
ulong montgomeryOne = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64, modulus); ulong montgomeryOne = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64, modulus);
@@ -60,10 +65,10 @@ public static class MontgomeryDivisorDataPool
montgomeryTwoSquared); montgomeryTwoSquared);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static MontgomeryDivisorData Rent(this Queue<MontgomeryDivisorData> queue, ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared) public static MontgomeryDivisorData Rent(this FixedCapacityStack<MontgomeryDivisorData> queue, ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
{ {
if (queue.TryDequeue(out var pooled)) if (queue.Pop() is { } pooled)
{ {
pooled.Modulus = modulus; pooled.Modulus = modulus;
pooled.NPrime = nPrime; pooled.NPrime = nPrime;
@@ -76,6 +81,6 @@ public static class MontgomeryDivisorDataPool
return new(modulus, nPrime, montgomeryOne, montgomeryTwo, montgomeryTwoSquared); return new(modulus, nPrime, montgomeryOne, montgomeryTwo, montgomeryTwoSquared);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return(this Queue<MontgomeryDivisorData> queue, MontgomeryDivisorData divisorData) => queue.Enqueue(divisorData); public static void Return(this FixedCapacityStack<MontgomeryDivisorData> queue, MontgomeryDivisorData divisorData) => queue.Push(divisorData);
} }
+12 -9
View File
@@ -1,3 +1,5 @@
using System.Numerics;
namespace PerfectNumbers.Core namespace PerfectNumbers.Core
{ {
public sealed class PartialFactorResult public sealed class PartialFactorResult
@@ -77,7 +79,7 @@ namespace PerfectNumbers.Core
return; return;
} }
int newSize = sourceCount + 1; int newSize = (int)BitOperations.RoundUpToPowerOf2((uint)sourceCount + 1U);
ulong[] extendedFactors = sourceFactors.Length >= newSize ulong[] extendedFactors = sourceFactors.Length >= newSize
? sourceFactors ? sourceFactors
: ResizeFactors(sourceFactors, sourceCount, newSize); : ResizeFactors(sourceFactors, sourceCount, newSize);
@@ -87,6 +89,7 @@ namespace PerfectNumbers.Core
? sourceExponents ? sourceExponents
: ResizeExponents(sourceCount, newSize, sourceExponents); : ResizeExponents(sourceCount, newSize, sourceExponents);
newSize = sourceCount + 1;
extendedFactors[sourceCount] = prime; extendedFactors[sourceCount] = prime;
extendedExponents[sourceCount] = 1; extendedExponents[sourceCount] = 1;
Array.Sort(extendedFactors, extendedExponents, 0, newSize); Array.Sort(extendedFactors, extendedExponents, 0, newSize);
@@ -101,24 +104,24 @@ namespace PerfectNumbers.Core
private static int[] ResizeExponents(int sourceCount, int newSize, in int[] sourceExponents) private static int[] ResizeExponents(int sourceCount, int newSize, in int[] sourceExponents)
{ {
int[] extendedExponents = Pools.ExclusiveIntArray.Rent(newSize); int[] extendedExponents = FixedCapacityPools.ExclusiveIntArray.Rent(newSize);
Array.Copy(sourceExponents, 0, extendedExponents, 0, sourceCount); Array.Copy(sourceExponents, 0, extendedExponents, 0, sourceCount);
Pools.ExclusiveIntArray.Return(sourceExponents); FixedCapacityPools.ExclusiveIntArray.Return(sourceExponents);
return extendedExponents; return extendedExponents;
} }
private static ulong[] ResizeFactors(in ulong[] sourceFactors, int sourceCount, int newSize) private static ulong[] ResizeFactors(in ulong[] sourceFactors, int sourceCount, int newSize)
{ {
ulong[] extendedFactors = Pools.ExclusiveUlongArray.Rent(newSize); ulong[] extendedFactors = FixedCapacityPools.ExclusiveUlongArray.Rent(newSize);
Array.Copy(sourceFactors, 0, extendedFactors, 0, sourceCount); Array.Copy(sourceFactors, 0, extendedFactors, 0, sourceCount);
Pools.ExclusiveUlongArray.Return(sourceFactors); FixedCapacityPools.ExclusiveUlongArray.Return(sourceFactors);
return extendedFactors; return extendedFactors;
} }
private void InitializeWithPrime(ulong prime) private void InitializeWithPrime(ulong prime)
{ {
ulong[] factors = Pools.ExclusiveUlongArray.Rent(1); ulong[] factors = FixedCapacityPools.ExclusiveUlongArray.Rent(1);
int[] exponents = Pools.ExclusiveIntArray.Rent(1); int[] exponents = FixedCapacityPools.ExclusiveIntArray.Rent(1);
factors[0] = prime; factors[0] = prime;
exponents[0] = 1; exponents[0] = 1;
Factors = factors; Factors = factors;
@@ -142,8 +145,8 @@ namespace PerfectNumbers.Core
Factors = null; Factors = null;
Exponents = null; Exponents = null;
Pools.ExclusiveUlongArray.Return(factors); FixedCapacityPools.ExclusiveUlongArray.Return(factors);
Pools.ExclusiveIntArray.Return(exponents); FixedCapacityPools.ExclusiveIntArray.Return(exponents);
} }
_next = s_poolHead; _next = s_poolHead;
@@ -7,12 +7,15 @@ public static class PerfectNumberConstants
public const ulong BiggestKnownEvenPerfectP = 136279841UL; public const ulong BiggestKnownEvenPerfectP = 136279841UL;
public const int ConsoleInterval = 100_000; public const int ConsoleInterval = 100_000;
public const int DefaultFactorsBuffer = 32; public const int DefaultFactorsBuffer = 32;
public const int DefaultPoolCapacity = 10_240;
public const int DefaultSmallPrimeFactorSlotCount = 64; public const int DefaultSmallPrimeFactorSlotCount = 64;
public const int DefaultThreadPoolCapacity = 131_072;
public const int DefaultSpecialMaxFactorCapacity = 1024; public const int DefaultSpecialMaxFactorCapacity = 1024;
public const ulong ExtraDivisorCycleSearchLimit = 64UL; public const ulong ExtraDivisorCycleSearchLimit = 64UL;
public static int GpuRatio = 16384 - 1; public static int GpuRatio = 16384 - 1;
public const int MaxQForDivisorCycles = 4_000_000; public const int MaxQForDivisorCycles = 4_000_000;
public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000; public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000;
public const int DefaultStringBuilderCapacity = 10_240;
public const int MaxOddPowersCount = 128; public const int MaxOddPowersCount = 128;
public const int PooledArrayThreshold = 64; public const int PooledArrayThreshold = 64;
public const int Pow2WindowSize = 8; public const int Pow2WindowSize = 8;
+22 -20
View File
@@ -108,7 +108,7 @@ internal static partial class PrimeOrderCalculator
} }
ulong prime64 = (ulong)prime; ulong prime64 = (ulong)prime;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
divisorData = divisorPool.FromModulus(prime64); divisorData = divisorPool.FromModulus(prime64);
ulong order64 = CalculateCpu(prime64, previous, divisorData, config); ulong order64 = CalculateCpu(prime64, previous, divisorData, config);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -226,7 +226,7 @@ internal static partial class PrimeOrderCalculator
private static UInt128 FinishStrictlyWideCpu(in UInt128 prime, in MontgomeryDivisorData? divisorData) private static UInt128 FinishStrictlyWideCpu(in UInt128 prime, in MontgomeryDivisorData? divisorData)
{ {
UInt128 phi = prime - UInt128.One; UInt128 phi = prime - UInt128.One;
Queue<Dictionary<UInt128, int>> uInt128IntDictionaryPool = ThreadStaticPools.UInt128IntDictionaryPool; FixedCapacityStack<Dictionary<UInt128, int>> uInt128IntDictionaryPool = ThreadStaticPools.UInt128IntDictionaryPool;
Dictionary<UInt128, int> counts = uInt128IntDictionaryPool.Rent(capacity: 8); Dictionary<UInt128, int> counts = uInt128IntDictionaryPool.Rent(capacity: 8);
FactorCompletelyWide(phi, counts); FactorCompletelyWide(phi, counts);
int entryCount = counts.Count; int entryCount = counts.Count;
@@ -236,7 +236,7 @@ internal static partial class PrimeOrderCalculator
return phi; return phi;
} }
Queue<List<KeyValuePair<UInt128, int>>> keyValuePairUInt128IntegerPool = ThreadStaticPools.KeyValuePairUInt128IntegerPool; FixedCapacityStack<List<KeyValuePair<UInt128, int>>> keyValuePairUInt128IntegerPool = ThreadStaticPools.KeyValuePairUInt128IntegerPool;
var entries = keyValuePairUInt128IntegerPool.Rent(entryCount); var entries = keyValuePairUInt128IntegerPool.Rent(entryCount);
entries.AddRange(counts); entries.AddRange(counts);
uInt128IntDictionaryPool.Return(counts); uInt128IntDictionaryPool.Return(counts);
@@ -446,7 +446,7 @@ internal static partial class PrimeOrderCalculator
} }
int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecksCapacity; int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecksCapacity;
Queue<List<UInt128>> uInt128ListPool = ThreadStaticPools.UInt128ListPool; FixedCapacityStack<List<UInt128>> uInt128ListPool = ThreadStaticPools.UInt128ListPool;
List<UInt128> candidates = uInt128ListPool.Rent(capacity); List<UInt128> candidates = uInt128ListPool.Rent(capacity);
FactorEntry128[] factorArray = orderFactors.Factors!; FactorEntry128[] factorArray = orderFactors.Factors!;
@@ -580,8 +580,8 @@ internal static partial class PrimeOrderCalculator
int length = factors.Count; int length = factors.Count;
int newLength = length + 1; int newLength = length + 1;
ulong[] tempFactorsArray = Pools.ExclusiveUlongArray.Rent(newLength); ulong[] tempFactorsArray = FixedCapacityPools.ExclusiveUlongArray.Rent(newLength);
int[] tempExponentsArray = Pools.ExclusiveIntArray.Rent(newLength); int[] tempExponentsArray = FixedCapacityPools.ExclusiveIntArray.Rent(newLength);
Span<ulong> ulongBuffer = tempFactorsArray; Span<ulong> ulongBuffer = tempFactorsArray;
factorSpan.CopyTo(ulongBuffer); factorSpan.CopyTo(ulongBuffer);
@@ -629,7 +629,7 @@ internal static partial class PrimeOrderCalculator
continue; continue;
} }
heapCandidateArray ??= Pools.ExclusiveUlongArray.Rent(ExponentHardLimit); heapCandidateArray ??= FixedCapacityPools.ExclusiveUlongArray.Rent(ExponentHardLimit);
heapEvaluationArray ??= ThreadStaticPools.BoolPool.Rent(ExponentHardLimit); heapEvaluationArray ??= ThreadStaticPools.BoolPool.Rent(ExponentHardLimit);
ProcessExponentLoweringPrime(heapCandidateArray.AsSpan(0, exponent), heapEvaluationArray.AsSpan(0, exponent), ref order, primeFactor, exponent, ref stepper); ProcessExponentLoweringPrime(heapCandidateArray.AsSpan(0, exponent), heapEvaluationArray.AsSpan(0, exponent), ref order, primeFactor, exponent, ref stepper);
} }
@@ -638,11 +638,11 @@ internal static partial class PrimeOrderCalculator
if (heapCandidateArray is not null) if (heapCandidateArray is not null)
{ {
Pools.ExclusiveUlongArray.Return(heapCandidateArray); FixedCapacityPools.ExclusiveUlongArray.Return(heapCandidateArray);
ThreadStaticPools.BoolPool.Return(heapEvaluationArray!); ThreadStaticPools.BoolPool.Return(heapEvaluationArray!);
} }
Pools.ExclusiveUlongArray.Return(tempFactorsArray); FixedCapacityPools.ExclusiveUlongArray.Return(tempFactorsArray);
return order; return order;
} }
@@ -789,7 +789,7 @@ internal static partial class PrimeOrderCalculator
continue; continue;
} }
heapCandidateArray ??= Pools.ExclusiveUlongArray.Rent(ExponentHardLimit); heapCandidateArray ??= FixedCapacityPools.ExclusiveUlongArray.Rent(ExponentHardLimit);
if (ValidateOrderForFactor(heapCandidateArray.AsSpan(0, exponent), primeFactor, exponent, order, ref stepper)) if (ValidateOrderForFactor(heapCandidateArray.AsSpan(0, exponent), primeFactor, exponent, order, ref stepper))
{ {
violates = true; violates = true;
@@ -801,7 +801,7 @@ internal static partial class PrimeOrderCalculator
if (heapCandidateArray is not null) if (heapCandidateArray is not null)
{ {
Pools.ExclusiveUlongArray.Return(heapCandidateArray); FixedCapacityPools.ExclusiveUlongArray.Return(heapCandidateArray);
} }
return !violates; return !violates;
@@ -1229,7 +1229,7 @@ internal static partial class PrimeOrderCalculator
continue; continue;
} }
heapCandidateArray ??= Pools.ExclusiveUlongArray.Rent(ExponentHardLimit); heapCandidateArray ??= FixedCapacityPools.ExclusiveUlongArray.Rent(ExponentHardLimit);
if (CheckCandidateViolation(heapCandidateArray.AsSpan(0, exponent), primeFactor, exponent, candidate, prime, ref powUsed, powBudget, ref stepper)) if (CheckCandidateViolation(heapCandidateArray.AsSpan(0, exponent), primeFactor, exponent, candidate, prime, ref powUsed, powBudget, ref stepper))
{ {
violates = true; violates = true;
@@ -1241,7 +1241,7 @@ internal static partial class PrimeOrderCalculator
if (heapCandidateArray is not null) if (heapCandidateArray is not null)
{ {
Pools.ExclusiveUlongArray.Return(heapCandidateArray); FixedCapacityPools.ExclusiveUlongArray.Return(heapCandidateArray);
} }
factorization.Dispose(); factorization.Dispose();
@@ -1334,9 +1334,6 @@ internal static partial class PrimeOrderCalculator
private static bool Pow2EqualsOneCpu(ulong exponent, in MontgomeryDivisorData divisorData) private static bool Pow2EqualsOneCpu(ulong exponent, in MontgomeryDivisorData divisorData)
=> exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; => exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL;
private static readonly ulong[] _emptyUlongArray = [];
private static readonly int[] _emptyIntArray = [];
[MethodImpl(MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveOptimization)]
private static PartialFactorResult PartialFactorCpu(ulong value, in PrimeOrderCalculatorConfig config) private static PartialFactorResult PartialFactorCpu(ulong value, in PrimeOrderCalculatorConfig config)
{ {
@@ -1369,7 +1366,7 @@ internal static partial class PrimeOrderCalculator
if (config.PollardRhoMilliseconds > 0) if (config.PollardRhoMilliseconds > 0)
{ {
long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds); long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds);
Stack<ulong> compositeStack = ThreadStaticPools.RentUlongStack(PrimeOrderConstants.GpuSmallPrimeFactorSlots); FixedCapacityStack<ulong> compositeStack = ThreadStaticPools.RentUlongStack(PrimeOrderConstants.GpuSmallPrimeFactorSlots);
compositeStack.Push(remaining); compositeStack.Push(remaining);
CollectFactorsCpu(primeSlots, exponentSlots, ref factorCount, pending, compositeStack, deadlineTimestamp, out limitReached); CollectFactorsCpu(primeSlots, exponentSlots, ref factorCount, pending, compositeStack, deadlineTimestamp, out limitReached);
@@ -1395,6 +1392,11 @@ internal static partial class PrimeOrderCalculator
bool cofactorContainsComposite = false; bool cofactorContainsComposite = false;
int pendingCount = pending.Count; int pendingCount = pending.Count;
int index = 0; int index = 0;
if (pending.Capacity > PrimeOrderConstants.GpuSmallPrimeFactorSlots)
{
Console.WriteLine($"Initial capacity is too small. Required capacity: {pending.Capacity}");
}
for (; index < pendingCount; index++) for (; index < pendingCount; index++)
{ {
PendingEntry entry = pending[index]; PendingEntry entry = pending[index];
@@ -1470,8 +1472,8 @@ internal static partial class PrimeOrderCalculator
// goto ReturnResult; // goto ReturnResult;
// } // }
ulong[] factorsArray = Pools.ExclusiveUlongArray.Rent(factorCount); ulong[] factorsArray = FixedCapacityPools.ExclusiveUlongArray.Rent(factorCount);
int[] exponentsArray = Pools.ExclusiveIntArray.Rent(factorCount); int[] exponentsArray = FixedCapacityPools.ExclusiveIntArray.Rent(factorCount);
for (int i = 0; i < factorCount; i++) for (int i = 0; i < factorCount; i++)
{ {
@@ -1500,7 +1502,7 @@ internal static partial class PrimeOrderCalculator
} }
[MethodImpl(MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveOptimization)]
private static void CollectFactorsCpu(Span<ulong> primeSlots, Span<int> exponentSlots, ref int factorCount, List<PendingEntry> pending, Stack<ulong> compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached) private static void CollectFactorsCpu(Span<ulong> primeSlots, Span<int> exponentSlots, ref int factorCount, List<PendingEntry> pending, FixedCapacityStack<ulong> compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached)
{ {
while (compositeStack.Count > 0) while (compositeStack.Count > 0)
{ {
@@ -117,7 +117,7 @@ internal static partial class PrimeOrderCalculator
} }
ulong prime64 = (ulong)prime; ulong prime64 = (ulong)prime;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
divisorData = divisorPool.FromModulus(prime64); divisorData = divisorPool.FromModulus(prime64);
ulong order64 = CalculateGpu(gpu, prime64, previous, divisorData, config); ulong order64 = CalculateGpu(gpu, prime64, previous, divisorData, config);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -377,7 +377,7 @@ internal static partial class PrimeOrderCalculator
if (config.PollardRhoMilliseconds > 0) if (config.PollardRhoMilliseconds > 0)
{ {
long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds); long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds);
Stack<ulong> compositeStack = ThreadStaticPools.RentUlongStack(4); FixedCapacityStack<ulong> compositeStack = ThreadStaticPools.RentUlongStack(4);
compositeStack.Push(remaining); compositeStack.Push(remaining);
CollectFactorsCpu(primeSlots, exponentSlots, ref factorCount, pending, compositeStack, deadlineTimestamp, out limitReached); CollectFactorsCpu(primeSlots, exponentSlots, ref factorCount, pending, compositeStack, deadlineTimestamp, out limitReached);
@@ -67,7 +67,7 @@ internal static partial class PrimeOrderCalculator
} }
ulong prime64 = (ulong)prime; ulong prime64 = (ulong)prime;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
divisorData = divisorPool.FromModulus(prime64); divisorData = divisorPool.FromModulus(prime64);
ulong order64 = CalculateHybrid(gpu, prime64, previous, divisorData, config); ulong order64 = CalculateHybrid(gpu, prime64, previous, divisorData, config);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
@@ -83,7 +83,7 @@ internal static partial class PrimeOrderCalculator
} }
[MethodImpl(MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveOptimization)]
private static void CollectFactorsHybrid(PrimeOrderCalculatorAccelerator gpu, Span<ulong> primeSlots, Span<int> exponentSlots, ref ulong[] primeSlotArray, ref int[] exponentSlotArray, ref int factorCount, List<PendingEntry> pending, Stack<ulong> compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached) private static void CollectFactorsHybrid(PrimeOrderCalculatorAccelerator gpu, Span<ulong> primeSlots, Span<int> exponentSlots, ref int factorCount, List<PendingEntry> pending, FixedCapacityStack<ulong> compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached)
{ {
while (compositeStack.Count > 0) while (compositeStack.Count > 0)
{ {
@@ -169,10 +169,10 @@ internal static partial class PrimeOrderCalculator
if (config.PollardRhoMilliseconds > 0) if (config.PollardRhoMilliseconds > 0)
{ {
long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds); long deadlineTimestamp = CreateDeadlineTimestamp(config.PollardRhoMilliseconds);
Stack<ulong> compositeStack = ThreadStaticPools.RentUlongStack(4); FixedCapacityStack<ulong> compositeStack = ThreadStaticPools.RentUlongStack(4);
compositeStack.Push(remaining); compositeStack.Push(remaining);
CollectFactorsHybrid(gpu, primeSlots, exponentSlots, primeSlotArray: ref primeSlotArray, ref exponentSlotArray, ref factorCount, pending, compositeStack, deadlineTimestamp, out limitReached); CollectFactorsHybrid(gpu, primeSlots, exponentSlots, ref factorCount, pending, compositeStack, deadlineTimestamp, out limitReached);
if (limitReached) if (limitReached)
{ {
+6 -8
View File
@@ -1,23 +1,21 @@
using System.Collections.Concurrent; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
public static class StringBuilderPool public static class StringBuilderPool
{ {
private static readonly ConcurrentQueue<StringBuilder> _stringBuilderPool = new(); private static readonly ConcurrentFixedCapacityStack<StringBuilder> _stringBuilderPool = new(PerfectNumberConstants.DefaultPoolCapacity);
public static StringBuilder Rent() [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
{ public static StringBuilder Rent() => _stringBuilderPool.Pop() is { } sb ? sb : new(PerfectNumberConstants.DefaultStringBuilderCapacity);
// TODO: Ensure oversized builders remain eligible for reuse without trimming so call sites keep their full capacity.
return _stringBuilderPool.TryDequeue(out var sb) ? sb : new();
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return(StringBuilder sb) public static void Return(StringBuilder sb)
{ {
_ = sb.Clear(); _ = sb.Clear();
// TODO: Preserve the builder's capacity when returning it so the pool hands the same buffer back without shrinkage. // TODO: Preserve the builder's capacity when returning it so the pool hands the same buffer back without shrinkage.
_stringBuilderPool.Enqueue(sb); _stringBuilderPool.Push(sb);
} }
} }
+14 -22
View File
@@ -1,21 +1,17 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
internal sealed class TaskThreadPool internal sealed class TaskThreadPool
{ {
private readonly struct WorkItem private readonly struct WorkItem(Task task)
{ {
public WorkItem(Task task) public readonly Task Task = task;
{
Task = task;
} }
public Task Task { get; } private readonly ConcurrentFixedCapacityStack<WorkItem> _pendingTaskQueue = new(PerfectNumberConstants.DefaultThreadPoolCapacity);
} private readonly HashSet<Thread> _runningThreads = [];
private readonly ConcurrentQueue<WorkItem> _pendingTaskQueue = new();
private readonly HashSet<Thread> _runningThreads = new();
private readonly object _lock = new(); private readonly object _lock = new();
private readonly Action<Task> _taskExecutor; private readonly Action<Task> _taskExecutor;
private readonly int _maximumConcurrency; private readonly int _maximumConcurrency;
@@ -58,21 +54,15 @@ internal sealed class TaskThreadPool
public void Queue(Task task) public void Queue(Task task)
{ {
if (task is null) ArgumentNullException.ThrowIfNull(task);
{
throw new ArgumentNullException(nameof(task));
}
List<Thread>? threadsToStart; List<Thread>? threadsToStart;
lock (_lock) lock (_lock)
{ {
if (_disposeCancellation.IsCancellationRequested) ObjectDisposedException.ThrowIf(_disposeCancellation.IsCancellationRequested, nameof(TaskThreadPool));
{
throw new ObjectDisposedException(nameof(TaskThreadPool));
}
_pendingTaskQueue.Enqueue(new WorkItem(task)); _pendingTaskQueue.Push(new WorkItem(task));
_pendingTaskCount++; _pendingTaskCount++;
threadsToStart = EnsureWorkerCapacityLocked(null); threadsToStart = EnsureWorkerCapacityLocked(null);
@@ -95,7 +85,7 @@ internal sealed class TaskThreadPool
if (snapshot.Length == 0) if (snapshot.Length == 0)
{ {
return Array.Empty<Task>(); return [];
} }
Task[] tasks = new Task[snapshot.Length]; Task[] tasks = new Task[snapshot.Length];
@@ -105,6 +95,7 @@ internal sealed class TaskThreadPool
tasks[i] = snapshot[i].Task; tasks[i] = snapshot[i].Task;
} }
ConcurrentFixedCapacityStack<WorkItem>.Return(snapshot);
return tasks; return tasks;
} }
@@ -137,7 +128,7 @@ internal sealed class TaskThreadPool
{ {
_runningThreads.Clear(); _runningThreads.Clear();
while (_pendingTaskQueue.TryDequeue(out _)) while (_pendingTaskQueue.Pop() is { })
{ {
} }
@@ -162,7 +153,8 @@ internal sealed class TaskThreadPool
return false; return false;
} }
if (_pendingTaskQueue.TryDequeue(out workItem)) workItem = _pendingTaskQueue.Pop();
if (workItem is { })
{ {
Interlocked.Decrement(ref _pendingTaskCount); Interlocked.Decrement(ref _pendingTaskCount);
return true; return true;
@@ -221,7 +213,7 @@ internal sealed class TaskThreadPool
_runningThreads.Add(thread); _runningThreads.Add(thread);
_currentThreads++; _currentThreads++;
threadsToStart ??= new List<Thread>(); threadsToStart ??= [];
threadsToStart.Add(thread); threadsToStart.Add(thread);
} }
+104 -195
View File
@@ -13,29 +13,23 @@ namespace PerfectNumbers.Core
public static ArrayPool<EInteger> EIntegerPool public static ArrayPool<EInteger> EIntegerPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _eintegerPool ??= ArrayPool<EInteger>.Create();
{
return _eintegerPool ??= ArrayPool<EInteger>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
private static Queue<List<KeyValuePair<UInt128, int>>>? _keyValuePairUInt128IntegerPool; private static FixedCapacityStack<List<KeyValuePair<UInt128, int>>>? _keyValuePairUInt128IntegerPool;
public static Queue<List<KeyValuePair<UInt128, int>>> KeyValuePairUInt128IntegerPool public static FixedCapacityStack<List<KeyValuePair<UInt128, int>>> KeyValuePairUInt128IntegerPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _keyValuePairUInt128IntegerPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
{
return _keyValuePairUInt128IntegerPool ??= [];
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static List<KeyValuePair<UInt128, int>> Rent(this Queue<List<KeyValuePair<UInt128, int>>> pool, int capacity) public static List<KeyValuePair<UInt128, int>> Rent(this FixedCapacityStack<List<KeyValuePair<UInt128, int>>> pool, int capacity)
{ {
if (pool.TryDequeue(out var pooled)) if (pool.Pop() is { } pooled)
{ {
if (capacity > pooled.Capacity) if (capacity > pooled.Capacity)
{ {
@@ -49,25 +43,22 @@ namespace PerfectNumbers.Core
return new(capacity); return new(capacity);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return(this Queue<List<KeyValuePair<UInt128, int>>> pool, List<KeyValuePair<UInt128, int>> list) => pool.Enqueue(list); public static void Return(this FixedCapacityStack<List<KeyValuePair<UInt128, int>>> pool, List<KeyValuePair<UInt128, int>> list) => pool.Push(list);
[ThreadStatic] [ThreadStatic]
private static Queue<List<UInt128>>? _uint128ListPool; private static FixedCapacityStack<List<UInt128>>? _uint128ListPool;
public static Queue<List<UInt128>> UInt128ListPool public static FixedCapacityStack<List<UInt128>> UInt128ListPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _uint128ListPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
{
return _uint128ListPool ??= [];
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static List<UInt128> Rent(this Queue<List<UInt128>> pool, int capacity) public static List<UInt128> Rent(this FixedCapacityStack<List<UInt128>> pool, int capacity)
{ {
if (pool.TryDequeue(out var pooled)) if (pool.Pop() is { } pooled)
{ {
if (capacity > pooled.Capacity) if (capacity > pooled.Capacity)
{ {
@@ -81,25 +72,22 @@ namespace PerfectNumbers.Core
return new(capacity); return new(capacity);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return(this Queue<List<UInt128>> pool, List<UInt128> list) => pool.Enqueue(list); public static void Return(this FixedCapacityStack<List<UInt128>> pool, List<UInt128> list) => pool.Push(list);
[ThreadStatic] [ThreadStatic]
private static Queue<Dictionary<UInt128, int>>? _uint128IntDictionaryPool; private static FixedCapacityStack<Dictionary<UInt128, int>>? _uint128IntDictionaryPool;
public static Queue<Dictionary<UInt128, int>> UInt128IntDictionaryPool public static FixedCapacityStack<Dictionary<UInt128, int>> UInt128IntDictionaryPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _uint128IntDictionaryPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
{
return _uint128IntDictionaryPool ??= [];
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Dictionary<UInt128, int> Rent(this Queue<Dictionary<UInt128, int>> pool, int capacity) public static Dictionary<UInt128, int> Rent(this FixedCapacityStack<Dictionary<UInt128, int>> pool, int capacity)
{ {
if (pool.TryDequeue(out var pooled)) if (pool.Pop() is { } pooled)
{ {
pooled.EnsureCapacity(capacity); pooled.EnsureCapacity(capacity);
pooled.Clear(); pooled.Clear();
@@ -109,19 +97,16 @@ namespace PerfectNumbers.Core
return new(capacity); return new(capacity);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return(this Queue<Dictionary<UInt128, int>> pool, Dictionary<UInt128, int> dictionary) => pool.Enqueue(dictionary); public static void Return(this FixedCapacityStack<Dictionary<UInt128, int>> pool, Dictionary<UInt128, int> dictionary) => pool.Push(dictionary);
[ThreadStatic] [ThreadStatic]
private static ArrayPool<FactorEntry>? _factorEntryPool; private static ArrayPool<FactorEntry>? _factorEntryPool;
public static ArrayPool<FactorEntry> FactorEntryPool public static ArrayPool<FactorEntry> FactorEntryPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _factorEntryPool ??= ArrayPool<FactorEntry>.Create();
{
return _factorEntryPool ??= ArrayPool<FactorEntry>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -129,11 +114,8 @@ namespace PerfectNumbers.Core
public static ArrayPool<FactorEntry128> FactorEntry128Pool public static ArrayPool<FactorEntry128> FactorEntry128Pool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _factorEntry128Pool ??= ArrayPool<FactorEntry128>.Create();
{
return _factorEntry128Pool ??= ArrayPool<FactorEntry128>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -141,45 +123,36 @@ namespace PerfectNumbers.Core
public static ArrayPool<GpuUInt128> GpuUInt128Pool public static ArrayPool<GpuUInt128> GpuUInt128Pool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _gpuUInt128Pool ??= ArrayPool<GpuUInt128>.Create();
{
return _gpuUInt128Pool ??= ArrayPool<GpuUInt128>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
private static MersenneCpuDivisorScanSession? _mersenneCpuDivisorSession; private static MersenneCpuDivisorScanSession? _mersenneCpuDivisorSession;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static MersenneCpuDivisorScanSession? RentMersenneCpuDivisorSession() internal static MersenneCpuDivisorScanSession RentMersenneCpuDivisorSession(Gpu.Accelerators.PrimeOrderCalculatorAccelerator gpu, ComputationDevice orderDevice)
{ {
MersenneCpuDivisorScanSession? session = _mersenneCpuDivisorSession; if (_mersenneCpuDivisorSession is { } session)
if (session is null)
{ {
return null; session.Configure(gpu, orderDevice);
}
_mersenneCpuDivisorSession = null; _mersenneCpuDivisorSession = null;
return session; return session;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] return new MersenneCpuDivisorScanSession(gpu, orderDevice);
internal static void ReturnMersenneCpuDivisorSession(MersenneCpuDivisorScanSession session)
{
_mersenneCpuDivisorSession = session;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void ReturnMersenneCpuDivisorSession(MersenneCpuDivisorScanSession session) => _mersenneCpuDivisorSession = session;
[ThreadStatic] [ThreadStatic]
private static ArrayPool<UInt128>? _uint128Pool; private static ArrayPool<UInt128>? _uint128Pool;
public static ArrayPool<UInt128> UInt128Pool public static ArrayPool<UInt128> UInt128Pool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _uint128Pool ??= ArrayPool<UInt128>.Create();
{
return _uint128Pool ??= ArrayPool<UInt128>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -187,11 +160,8 @@ namespace PerfectNumbers.Core
public static ArrayPool<ulong> UlongPool public static ArrayPool<ulong> UlongPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _ulongPool ??= ArrayPool<ulong>.Create();
{
return _ulongPool ??= ArrayPool<ulong>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -199,11 +169,8 @@ namespace PerfectNumbers.Core
public static ArrayPool<bool> BoolPool public static ArrayPool<bool> BoolPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _boolPool ??= ArrayPool<bool>.Create();
{
return _boolPool ??= ArrayPool<bool>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -211,11 +178,8 @@ namespace PerfectNumbers.Core
public static ArrayPool<byte> BytePool public static ArrayPool<byte> BytePool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _bytePool ??= ArrayPool<byte>.Create();
{
return _bytePool ??= ArrayPool<byte>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -223,11 +187,8 @@ namespace PerfectNumbers.Core
public static ArrayPool<MontgomeryDivisorData> MontgomeryDivisorDataPool public static ArrayPool<MontgomeryDivisorData> MontgomeryDivisorDataPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _montgomeryDivisorDataPool ??= ArrayPool<MontgomeryDivisorData>.Create();
{
return _montgomeryDivisorDataPool ??= ArrayPool<MontgomeryDivisorData>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -235,11 +196,8 @@ namespace PerfectNumbers.Core
internal static ArrayPool<GpuDivisorPartialData> GpuDivisorPartialDataPool internal static ArrayPool<GpuDivisorPartialData> GpuDivisorPartialDataPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _gpuDivisorPartialDataPool ??= ArrayPool<GpuDivisorPartialData>.Create();
{
return _gpuDivisorPartialDataPool ??= ArrayPool<GpuDivisorPartialData>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -247,11 +205,8 @@ namespace PerfectNumbers.Core
public static ArrayPool<int> IntPool public static ArrayPool<int> IntPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _intPool ??= ArrayPool<int>.Create();
{
return _intPool ??= ArrayPool<int>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
@@ -259,28 +214,21 @@ namespace PerfectNumbers.Core
public static ArrayPool<char> CharPool public static ArrayPool<char> CharPool
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get get => _charPool ??= ArrayPool<char>.Create();
{
return _charPool ??= ArrayPool<char>.Create();
}
} }
[ThreadStatic] [ThreadStatic]
private static List<List<ulong>>? _ulongListPool; private static FixedCapacityStack<List<ulong>>? _ulongListPool;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static List<ulong> RentUlongList(int capacityHint) public static List<ulong> RentUlongList(int capacityHint)
{ {
List<List<ulong>>? pool = _ulongListPool; FixedCapacityStack<List<ulong>>? pool = _ulongListPool;
if (pool is not null) if (pool is not null)
{ {
int poolCount = pool.Count; if (pool.Pop() is { } list)
if (poolCount > 0)
{ {
int lastIndex = poolCount - 1;
List<ulong> list = pool[lastIndex];
pool.RemoveAt(lastIndex);
if (list.Capacity < capacityHint) if (list.Capacity < capacityHint)
{ {
list.Capacity = capacityHint; list.Capacity = capacityHint;
@@ -289,27 +237,21 @@ namespace PerfectNumbers.Core
return list; return list;
} }
} }
else
{
_ulongListPool = new(capacityHint);
}
return new List<ulong>(capacityHint); return new List<ulong>(capacityHint);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void ReturnUlongList(List<ulong> list) public static void ReturnUlongList(List<ulong> list) => _ulongListPool!.Push(list);
{
List<List<ulong>>? pool = _ulongListPool;
if (pool is null)
{
pool = new List<List<ulong>>(4);
_ulongListPool = pool;
}
pool.Add(list);
}
[ThreadStatic] [ThreadStatic]
private static ArrayPool<(ulong, ulong)>? _ulongUlongTupleArrayPool; private static ArrayPool<(ulong, ulong)>? _ulongUlongTupleArrayPool;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static (ulong, ulong)[] RentUlongUlongArray(int capacityHint) public static (ulong, ulong)[] RentUlongUlongArray(int capacityHint)
{ {
_ulongUlongTupleArrayPool ??= ArrayPool<(ulong, ulong)>.Create(); _ulongUlongTupleArrayPool ??= ArrayPool<(ulong, ulong)>.Create();
@@ -317,24 +259,20 @@ namespace PerfectNumbers.Core
return pool.Rent(capacityHint); return pool.Rent(capacityHint);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Return((ulong, ulong)[] array) => _ulongUlongTupleArrayPool!.Return(array, clearArray: false); public static void Return((ulong, ulong)[] array) => _ulongUlongTupleArrayPool!.Return(array, clearArray: false);
[ThreadStatic] [ThreadStatic]
private static List<List<PrimeOrderCalculator.PendingEntry>>? _primeOrderPendingEntryListPool; private static FixedCapacityStack<List<PrimeOrderCalculator.PendingEntry>>? _primeOrderPendingEntryListPool;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static List<PrimeOrderCalculator.PendingEntry> RentPrimeOrderPendingEntryList(int capacityHint) internal static List<PrimeOrderCalculator.PendingEntry> RentPrimeOrderPendingEntryList(int capacityHint)
{ {
List<List<PrimeOrderCalculator.PendingEntry>>? pool = _primeOrderPendingEntryListPool; FixedCapacityStack<List<PrimeOrderCalculator.PendingEntry>>? pool = _primeOrderPendingEntryListPool;
if (pool is not null) if (pool is not null)
{ {
int poolCount = pool.Count; if (pool.Pop() is { } list)
if (poolCount > 0)
{ {
int lastIndex = poolCount - 1;
List<PrimeOrderCalculator.PendingEntry> list = pool[lastIndex];
pool.RemoveAt(lastIndex);
if (list.Capacity < capacityHint) if (list.Capacity < capacityHint)
{ {
list.Capacity = capacityHint; list.Capacity = capacityHint;
@@ -343,68 +281,47 @@ namespace PerfectNumbers.Core
return list; return list;
} }
} }
else
{
_primeOrderPendingEntryListPool = new(PerfectNumberConstants.DefaultPoolCapacity);
}
return new List<PrimeOrderCalculator.PendingEntry>(capacityHint); return new List<PrimeOrderCalculator.PendingEntry>(capacityHint);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void ReturnPrimeOrderPendingEntryList(List<PrimeOrderCalculator.PendingEntry> list) internal static void ReturnPrimeOrderPendingEntryList(List<PrimeOrderCalculator.PendingEntry> list) => _primeOrderPendingEntryListPool!.Push(list);
{
List<List<PrimeOrderCalculator.PendingEntry>>? pool = _primeOrderPendingEntryListPool;
if (pool is null)
{
pool = new List<List<PrimeOrderCalculator.PendingEntry>>(4);
_primeOrderPendingEntryListPool = pool;
}
pool.Add(list);
}
[ThreadStatic] [ThreadStatic]
private static List<Stack<ulong>>? _ulongStackPool; private static FixedCapacityStack<FixedCapacityStack<ulong>>? _ulongStackPool;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Stack<ulong> RentUlongStack(int capacityHint) public static FixedCapacityStack<ulong> RentUlongStack(int capacityHint)
{ {
List<Stack<ulong>>? pool = _ulongStackPool; FixedCapacityStack<FixedCapacityStack<ulong>>? pool = _ulongStackPool;
if (pool is not null) if (pool is not null)
{ {
int poolCount = pool.Count; if (pool.Pop() is { } stack)
if (poolCount > 0)
{ {
int lastIndex = poolCount - 1; stack.Capacity = capacityHint;
Stack<ulong> stack = pool[lastIndex];
pool.RemoveAt(lastIndex);
if (stack.Count < capacityHint)
{
stack.EnsureCapacity(capacityHint);
}
return stack; return stack;
} }
} }
else
return new Stack<ulong>(capacityHint);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReturnUlongStack(Stack<ulong> stack)
{ {
List<Stack<ulong>>? pool = _ulongStackPool; _ulongStackPool = new(PrimeOrderConstants.GpuSmallPrimeFactorSlots);
if (pool is null)
{
pool = new List<Stack<ulong>>(4);
_ulongStackPool = pool;
} }
pool.Add(stack); return new FixedCapacityStack<ulong>(capacityHint);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void ReturnUlongStack(FixedCapacityStack<ulong> stack) => _ulongStackPool!.Push(stack);
[ThreadStatic] [ThreadStatic]
private static Dictionary<ulong, int>? _factorCountDictionary; private static Dictionary<ulong, int>? _factorCountDictionary;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Dictionary<ulong, int> RentFactorCountDictionary() public static Dictionary<ulong, int> RentFactorCountDictionary()
{ {
Dictionary<ulong, int>? dictionary = _factorCountDictionary; Dictionary<ulong, int>? dictionary = _factorCountDictionary;
@@ -417,41 +334,34 @@ namespace PerfectNumbers.Core
return dictionary; return dictionary;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void ReturnFactorCountDictionary(Dictionary<ulong, int> dictionary) public static void ReturnFactorCountDictionary(Dictionary<ulong, int> dictionary) => _factorCountDictionary = dictionary;
{
_factorCountDictionary = dictionary;
}
[ThreadStatic] [ThreadStatic]
private static Dictionary<ulong, int>? _factorScratchDictionary; private static Dictionary<ulong, int>? _factorScratchDictionary;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Dictionary<ulong, int> RentFactorScratchDictionary() public static Dictionary<ulong, int> RentFactorScratchDictionary()
{ {
Dictionary<ulong, int>? dictionary = _factorScratchDictionary; if (_factorScratchDictionary is { } dictionary)
if (dictionary is null)
{ {
return new Dictionary<ulong, int>(8);
}
_factorScratchDictionary = null; _factorScratchDictionary = null;
return dictionary; return dictionary;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] return new Dictionary<ulong, int>(8);
public static void ReturnFactorScratchDictionary(Dictionary<ulong, int> dictionary)
{
_factorScratchDictionary = dictionary;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void ReturnFactorScratchDictionary(Dictionary<ulong, int> dictionary) => _factorScratchDictionary = dictionary;
[ThreadStatic] [ThreadStatic]
private static ExponentRemainderStepperCpu _exponentRemainderStepperCpu; private static ExponentRemainderStepperCpu _exponentRemainderStepperCpu;
[ThreadStatic] [ThreadStatic]
private static bool _hasExponentRemainderStepperCpu; private static bool _hasExponentRemainderStepperCpu;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static ExponentRemainderStepperCpu RentExponentStepperCpu(in MontgomeryDivisorData divisorData, ulong cycleLength = 0UL) internal static ExponentRemainderStepperCpu RentExponentStepperCpu(in MontgomeryDivisorData divisorData, ulong cycleLength = 0UL)
{ {
if (_hasExponentRemainderStepperCpu && _exponentRemainderStepperCpu.MatchesDivisor(divisorData, cycleLength)) if (_hasExponentRemainderStepperCpu && _exponentRemainderStepperCpu.MatchesDivisor(divisorData, cycleLength))
@@ -465,10 +375,9 @@ namespace PerfectNumbers.Core
return new ExponentRemainderStepperCpu(divisorData, cycleLength); return new ExponentRemainderStepperCpu(divisorData, cycleLength);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void ReturnExponentStepperCpu(ExponentRemainderStepperCpu stepper) internal static void ReturnExponentStepperCpu(ExponentRemainderStepperCpu stepper)
{ {
stepper.Reset();
_exponentRemainderStepperCpu = stepper; _exponentRemainderStepperCpu = stepper;
_hasExponentRemainderStepperCpu = true; _hasExponentRemainderStepperCpu = true;
} }
+2 -3
View File
@@ -647,9 +647,8 @@ public static partial class ULongExtensions
{ {
ulong tLow = unchecked(a * b); ulong tLow = unchecked(a * b);
ulong m = unchecked(tLow * nPrime); ulong m = unchecked(tLow * nPrime);
ulong mTimesModulusLow = unchecked(m * modulus); ulong result = unchecked(m * modulus);
result = unchecked(a.MulHighCpu(b) + m.MulHighCpu(modulus) + (unchecked(tLow + result) < tLow ? 1UL : 0UL));
ulong result = unchecked(a.MulHighCpu(b) + m.MulHighCpu(modulus) + (unchecked(tLow + mTimesModulusLow) < tLow ? 1UL : 0UL));
if (result >= modulus) if (result >= modulus)
{ {
result -= modulus; result -= modulus;
+40
View File
@@ -0,0 +1,40 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core
{
public struct UlongArrayPool(int poolCapacity)
{
private readonly ulong[][] _arrays = new ulong[poolCapacity][];
private int _count = 0;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public ulong[] Rent(int length)
{
int available = _count;
if (available == 0)
{
return new ulong[length];
}
ulong[][] arrays = _arrays;
available--;
_count = available;
ulong[] candidate = arrays[available];
return candidate.Length >= length ? candidate : new ulong[length];
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Return(ulong[] array)
{
ulong[][] arrays = _arrays;
int available = _count;
if (available < arrays.Length)
{
arrays[available] = array;
_count = available + 1;
}
}
}
}
+27 -22
View File
@@ -1,3 +1,5 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
public sealed class UnboundedTaskScheduler : TaskScheduler public sealed class UnboundedTaskScheduler : TaskScheduler
@@ -5,17 +7,24 @@ public sealed class UnboundedTaskScheduler : TaskScheduler
private static UnboundedTaskScheduler? _instance; private static UnboundedTaskScheduler? _instance;
private static int _configuredThreadCount = Environment.ProcessorCount; private static int _configuredThreadCount = Environment.ProcessorCount;
public static UnboundedTaskScheduler Instance => _instance ??= new UnboundedTaskScheduler(_configuredThreadCount); public static UnboundedTaskScheduler Instance
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _instance ??= new UnboundedTaskScheduler(_configuredThreadCount);
}
public static int ConfiguredThreadCount => _configuredThreadCount; public static int ConfiguredThreadCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _configuredThreadCount;
}
private TaskThreadPool _threadPool; private TaskThreadPool _threadPool;
private UnboundedTaskScheduler(int threadCount) [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
{ private UnboundedTaskScheduler(int threadCount) => _threadPool = new TaskThreadPool(threadCount, ExecuteTask);
_threadPool = new TaskThreadPool(threadCount, ExecuteTask);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void ConfigureThreadCount(int threadCount) public static void ConfigureThreadCount(int threadCount)
{ {
_configuredThreadCount = threadCount; _configuredThreadCount = threadCount;
@@ -29,13 +38,15 @@ public sealed class UnboundedTaskScheduler : TaskScheduler
// } // }
} }
public override int MaximumConcurrencyLevel => int.MaxValue; public override int MaximumConcurrencyLevel
protected override void QueueTask(Task task)
{ {
_threadPool.Queue(task); [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => int.MaxValue;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override void QueueTask(Task task) => _threadPool.Queue(task);
private void UpdateThreadPool(int threadCount) private void UpdateThreadPool(int threadCount)
{ {
if (_threadPool.ThreadCount == threadCount) if (_threadPool.ThreadCount == threadCount)
@@ -49,19 +60,13 @@ public sealed class UnboundedTaskScheduler : TaskScheduler
oldPool.Dispose(); oldPool.Dispose();
} }
private void ExecuteTask(Task task) [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
{ private void ExecuteTask(Task task) => TryExecuteTask(task);
TryExecuteTask(task);
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
{ protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => TryExecuteTask(task);
return TryExecuteTask(task);
}
protected override IEnumerable<Task>? GetScheduledTasks() [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
{ protected override IEnumerable<Task>? GetScheduledTasks() => _threadPool.GetScheduledTasks();
return _threadPool.GetScheduledTasks();
}
} }