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:
1 parent
547d43de84
commit
260aa19e44
25 files changed
+1366
-1294
No files matched your search
@@ -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,20 +157,10 @@ 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)
|
private bool CheckDivisors(
|
||||||
{
|
|
||||||
session.Configure(gpu, _orderDevice);
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MersenneCpuDivisorScanSession(gpu, _orderDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckDivisors(
|
|
||||||
PrimeOrderCalculatorAccelerator gpu,
|
PrimeOrderCalculatorAccelerator gpu,
|
||||||
ulong prime,
|
ulong prime,
|
||||||
BigInteger allowedMax,
|
BigInteger allowedMax,
|
||||||
@@ -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())
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
resources.EnsureCapacity(accelerator, capacity);
|
return new MersenneNumberDivisorByDivisorAccelerator(accelerator, capacity);
|
||||||
return resources;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReturnBatchResources(int acceleratorIndex, MersenneNumberDivisorByDivisorAccelerator resources) => _resourcePool[acceleratorIndex].Add(resources);
|
[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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -12,102 +11,102 @@ public static class MersennePrimeFactorTester
|
|||||||
// private static ulong _isPrimeFactorHits;
|
// private static ulong _isPrimeFactorHits;
|
||||||
// private static ulong _factor64Hits;
|
// private static ulong _factor64Hits;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsPrimeFactor(ulong p, ulong q, CancellationToken ct)
|
public static bool IsPrimeFactor(ulong p, ulong q, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (q < 2UL)
|
if (q < 2UL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((q & 7UL) != 1UL && (q & 7UL) != 7UL)
|
if ((q & 7UL) != 1UL && (q & 7UL) != 7UL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!HeuristicPrimeTester.Exclusive.IsPrime(q, ct))
|
// if (!HeuristicPrimeTester.Exclusive.IsPrime(q, ct))
|
||||||
|
|
||||||
// Atomic.Add(ref _isPrimeFactorHits, 1UL);
|
// Atomic.Add(ref _isPrimeFactorHits, 1UL);
|
||||||
// Console.WriteLine($"MersennePrimeFactorTester.IsPrimeFactor hits {Volatile.Read(ref _isPrimeFactorHits)}");
|
// Console.WriteLine($"MersennePrimeFactorTester.IsPrimeFactor hits {Volatile.Read(ref _isPrimeFactorHits)}");
|
||||||
|
|
||||||
if (!PrimeTester.IsPrimeCpu(q))
|
if (!PrimeTester.IsPrimeCpu(q))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p < 2UL || (p & 1UL) == 0)
|
if (p < 2UL || (p & 1UL) == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong value = checked(p << 1);
|
ulong value = checked(p << 1);
|
||||||
// TODO: Replace this `%` with the benchmarked Math.DivRem fast path so the hot filter avoids
|
// TODO: Replace this `%` with the benchmarked Math.DivRem fast path so the hot filter avoids
|
||||||
// 128-bit division when evaluating candidates.
|
// 128-bit division when evaluating candidates.
|
||||||
if ((q - 1UL) % value != 0UL)
|
if ((q - 1UL) % value != 0UL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong order = GetOrderOf2ModPrime(q, ct);
|
ulong order = GetOrderOf2ModPrime(q, ct);
|
||||||
return !ct.IsCancellationRequested && order == p;
|
return !ct.IsCancellationRequested && order == p;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Replace this dictionary with the divisor-cycle order cache once the benchmarks confirm the
|
// TODO: Replace this dictionary with the divisor-cycle order cache once the benchmarks confirm the
|
||||||
// shared cache can stream snapshot results without extra locking.
|
// shared cache can stream snapshot results without extra locking.
|
||||||
private static readonly Dictionary<UInt128, UInt128> _orderCache = [];
|
private static readonly Dictionary<UInt128, UInt128> _orderCache = [];
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static ulong GetOrderOf2ModPrime(ulong q, CancellationToken ct) =>
|
internal static ulong GetOrderOf2ModPrime(ulong q, CancellationToken ct) =>
|
||||||
// TODO: Collapse this wrapper once all callers promote operands to UInt128 so we
|
// TODO: Collapse this wrapper once all callers promote operands to UInt128 so we
|
||||||
// can jump straight to OrderOf2ModPrime without bouncing through multiple overloads.
|
// can jump straight to OrderOf2ModPrime without bouncing through multiple overloads.
|
||||||
(ulong)GetOrderOf2ModPrime((UInt128)q, ct);
|
(ulong)GetOrderOf2ModPrime((UInt128)q, ct);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static UInt128 GetOrderOf2ModPrime(UInt128 q, CancellationToken ct) =>
|
internal static UInt128 GetOrderOf2ModPrime(UInt128 q, CancellationToken ct) =>
|
||||||
// TODO: Inline this shim once callers can invoke OrderOf2ModPrime directly; the extra
|
// TODO: Inline this shim once callers can invoke OrderOf2ModPrime directly; the extra
|
||||||
// hop shows up on hot perf traces while we chase large-order factors.
|
// hop shows up on hot perf traces while we chase large-order factors.
|
||||||
OrderOf2ModPrime(q, ct);
|
OrderOf2ModPrime(q, ct);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static UInt128 OrderOf2ModPrime(UInt128 q, CancellationToken ct)
|
private static UInt128 OrderOf2ModPrime(UInt128 q, CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
|
||||||
lock (_orderCache)
|
lock (_orderCache)
|
||||||
{
|
{
|
||||||
// TODO: Replace this lock with the lock-free order cache once the divisor-cycle snapshot exposes
|
// TODO: Replace this lock with the lock-free order cache once the divisor-cycle snapshot exposes
|
||||||
// deterministic ordering for single-cycle computations; we measured heavy contention in the
|
// deterministic ordering for single-cycle computations; we measured heavy contention in the
|
||||||
// factor benchmarks when many threads warm the cache concurrently.
|
// factor benchmarks when many threads warm the cache concurrently.
|
||||||
if (_orderCache.TryGetValue(q, out var cached))
|
if (_orderCache.TryGetValue(q, out var cached))
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPool<UInt128> uInt128Pool = ThreadStaticPools.UInt128Pool;
|
ArrayPool<UInt128> uInt128Pool = ThreadStaticPools.UInt128Pool;
|
||||||
|
|
||||||
UInt128 phi = q - 1;
|
UInt128 phi = q - 1;
|
||||||
UInt128[] factors = phi <= ulong.MaxValue
|
UInt128[] factors = phi <= ulong.MaxValue
|
||||||
? Array.ConvertAll(Factor64((ulong)phi, ct), x => (UInt128)x)
|
? Array.ConvertAll(Factor64((ulong)phi, ct), x => (UInt128)x)
|
||||||
: Factor128(phi, ct);
|
: Factor128(phi, ct);
|
||||||
// TODO: Pull these factor arrays from ArrayPool once the factoring helpers adopt the pooled
|
// TODO: Pull these factor arrays from ArrayPool once the factoring helpers adopt the pooled
|
||||||
// buffers measured faster in FactorizationBenchmarks.
|
// buffers measured faster in FactorizationBenchmarks.
|
||||||
UInt128 order = phi;
|
UInt128 order = phi;
|
||||||
|
|
||||||
UInt128 candidate, prime;
|
UInt128 candidate, prime;
|
||||||
int i, factorsCount = factors.Length;
|
int i, factorsCount = factors.Length;
|
||||||
for (i = 0; i < factorsCount; i++)
|
for (i = 0; i < factorsCount; i++)
|
||||||
{
|
{
|
||||||
prime = factors[i];
|
prime = factors[i];
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
uInt128Pool.Return(factors);
|
uInt128Pool.Return(factors);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Swap this `%`/`/` loop for the Math.DivRem-based reducer highlighted in the
|
// TODO: Swap this `%`/`/` loop for the Math.DivRem-based reducer highlighted in the
|
||||||
// order benchmarks so we avoid repeated 128-bit division while trimming the order.
|
// order benchmarks so we avoid repeated 128-bit division while trimming the order.
|
||||||
@@ -130,440 +129,430 @@ public static class MersennePrimeFactorTester
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uInt128Pool.Return(factors);
|
uInt128Pool.Return(factors);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Integrate divisor-cycle data here so repeated order refinement uses cached cycle
|
// TODO: Integrate divisor-cycle data here so repeated order refinement uses cached cycle
|
||||||
// lengths when available and computes missing orders on the configured device without
|
// lengths when available and computes missing orders on the configured device without
|
||||||
// persisting them in the shared cache.
|
// persisting them in the shared cache.
|
||||||
if (!ct.IsCancellationRequested)
|
if (!ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
lock (_orderCache)
|
lock (_orderCache)
|
||||||
{
|
{
|
||||||
_orderCache[q] = order;
|
_orderCache[q] = order;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uInt128Pool.Return(factors);
|
uInt128Pool.Return(factors);
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static UInt128 ModPowWithCancellation(UInt128 baseValue, UInt128 exponent, UInt128 modulus, CancellationToken ct)
|
private static UInt128 ModPowWithCancellation(UInt128 baseValue, UInt128 exponent, UInt128 modulus, CancellationToken ct)
|
||||||
{
|
{
|
||||||
// Keep this helper local to the factoring path so we can preserve the token-aware early outs
|
// Keep this helper local to the factoring path so we can preserve the token-aware early outs
|
||||||
// without carrying a separate overload on the shared UInt128 extensions.
|
// without carrying a separate overload on the shared UInt128 extensions.
|
||||||
if (modulus <= UInt128.One)
|
if (modulus <= UInt128.One)
|
||||||
{
|
{
|
||||||
return UInt128.Zero;
|
return UInt128.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt128 result = UInt128.One;
|
UInt128 result = UInt128.One;
|
||||||
UInt128 baseResidue = baseValue % modulus;
|
UInt128 baseResidue = baseValue % modulus;
|
||||||
|
|
||||||
while (exponent != UInt128.Zero)
|
while (exponent != UInt128.Zero)
|
||||||
{
|
{
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return UInt128.Zero;
|
return UInt128.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((exponent & UInt128.One) != UInt128.Zero)
|
if ((exponent & UInt128.One) != UInt128.Zero)
|
||||||
{
|
{
|
||||||
result = result.MulMod(baseResidue, modulus);
|
result = result.MulMod(baseResidue, modulus);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return UInt128.Zero;
|
return UInt128.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
baseResidue = baseResidue.MulMod(baseResidue, modulus);
|
baseResidue = baseResidue.MulMod(baseResidue, modulus);
|
||||||
exponent >>= 1;
|
exponent >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
private static void ReturnList(List<ulong> list)
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
{
|
private static void ReturnList(List<ulong> list)
|
||||||
list.Clear();
|
{
|
||||||
ListPool.Add(list);
|
list.Clear();
|
||||||
}
|
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 : [];
|
||||||
|
|
||||||
private static void ReturnDictionary(Dictionary<ulong, byte> dict)
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
{
|
private static void ReturnDictionary(Dictionary<ulong, byte> dict)
|
||||||
dict.Clear();
|
{
|
||||||
DictPool.Add(dict);
|
dict.Clear();
|
||||||
}
|
DictPool.Push(dict);
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly ConcurrentBag<Dictionary<UInt128, byte>> DictPool128 = new();
|
private static readonly ConcurrentFixedCapacityStack<Dictionary<UInt128, byte>> DictPool128 = new(PerfectNumberConstants.DefaultPoolCapacity);
|
||||||
|
|
||||||
private static Dictionary<UInt128, byte> RentDictionary128() =>
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
DictPool128.TryTake(out var dict) ? dict : new Dictionary<UInt128, byte>();
|
private static Dictionary<UInt128, byte> RentDictionary128() =>
|
||||||
|
DictPool128.Pop() is { } dict ? dict : [];
|
||||||
|
|
||||||
private static void ReturnDictionary128(Dictionary<UInt128, byte> dict)
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
{
|
private static void ReturnDictionary128(Dictionary<UInt128, byte> dict)
|
||||||
dict.Clear();
|
{
|
||||||
DictPool128.Add(dict);
|
dict.Clear();
|
||||||
}
|
DictPool128.Push(dict);
|
||||||
|
}
|
||||||
|
|
||||||
private static ulong[] Factor64(ulong n, CancellationToken ct)
|
private static ulong[] Factor64(ulong n, CancellationToken ct)
|
||||||
{
|
{
|
||||||
// lock (FactorCacheLock)
|
// lock (FactorCacheLock)
|
||||||
// {
|
// {
|
||||||
// if (FactorCache.TryGetValue(n, out var cached))
|
// if (FactorCache.TryGetValue(n, out var cached))
|
||||||
// {
|
// {
|
||||||
// return cached;
|
// return cached;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (n == 1UL)
|
if (n == 1UL)
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong m = queue.Dequeue();
|
ulong m = queue.Pop();
|
||||||
if (m == 1UL)
|
if (m == 1UL)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Atomic.Add(ref _factor64Hits, 1UL);
|
// Atomic.Add(ref _factor64Hits, 1UL);
|
||||||
// Console.WriteLine($"MersennePrimeFactorTester.Factor64 hits {Volatile.Read(ref _factor64Hits)}");
|
// Console.WriteLine($"MersennePrimeFactorTester.Factor64 hits {Volatile.Read(ref _factor64Hits)}");
|
||||||
|
|
||||||
// if (HeuristicPrimeTester.Exclusive.IsPrime(m, ct))
|
// if (HeuristicPrimeTester.Exclusive.IsPrime(m, ct))
|
||||||
if (PrimeTester.IsPrimeCpu(m))
|
if (PrimeTester.IsPrimeCpu(m))
|
||||||
{
|
{
|
||||||
dict[m] = 1;
|
dict[m] = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong d = PollardRho(m, ct);
|
ulong d = PollardRho(m, ct);
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.Enqueue(d);
|
queue.Push(d);
|
||||||
queue.Enqueue(m / d);
|
queue.Push(m / d);
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong[] result = [.. dict.Keys];
|
ThreadStaticPools.ReturnUlongStack(queue);
|
||||||
// TODO: Rent the result array from ArrayPool once the factoring pipeline consumes spans so we
|
ulong[] result = [.. dict.Keys];
|
||||||
// can recycle buffers across large factorizations.
|
// TODO: Rent the result array from ArrayPool once the factoring pipeline consumes spans so we
|
||||||
// lock (FactorCacheLock)
|
// can recycle buffers across large factorizations.
|
||||||
// {
|
// lock (FactorCacheLock)
|
||||||
// FactorCache[n] = result;
|
// {
|
||||||
// }
|
// FactorCache[n] = result;
|
||||||
|
// }
|
||||||
|
|
||||||
ReturnDictionary(dict);
|
ReturnDictionary(dict);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UInt128[] Factor128(UInt128 n, CancellationToken ct)
|
private static UInt128[] Factor128(UInt128 n, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (n == 1)
|
if (n == 1)
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var dict = RentDictionary128();
|
var dict = RentDictionary128();
|
||||||
var queue = new Queue<UInt128>(); // TODO: Replace this with the pooled UInt128 stack once the
|
var queue = new Queue<UInt128>(); // TODO: Replace this with the pooled UInt128 stack once the
|
||||||
// wide-factorization benchmarks finalize the faster span
|
// wide-factorization benchmarks finalize the faster span
|
||||||
// based traversal.
|
// based traversal.
|
||||||
queue.Enqueue(n);
|
queue.Enqueue(n);
|
||||||
|
|
||||||
while (queue.Count != 0)
|
while (queue.Count != 0)
|
||||||
{
|
{
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt128 m = queue.Dequeue();
|
UInt128 m = queue.Dequeue();
|
||||||
if (m == 1)
|
if (m == 1)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPrime128(m, ct))
|
if (IsPrime128(m, ct))
|
||||||
{
|
{
|
||||||
dict[m] = 1;
|
dict[m] = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt128 d = PollardRho128(m, ct);
|
UInt128 d = PollardRho128(m, ct);
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.Enqueue(d);
|
queue.Enqueue(d);
|
||||||
queue.Enqueue(m / d);
|
queue.Enqueue(m / d);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = ThreadStaticPools.UInt128Pool.Rent(dict.Count);
|
var result = ThreadStaticPools.UInt128Pool.Rent(dict.Count);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (var key in dict.Keys)
|
foreach (var key in dict.Keys)
|
||||||
{
|
{
|
||||||
result[index++] = key;
|
result[index++] = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnDictionary128(dict);
|
ReturnDictionary128(dict);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong PollardRho(ulong n, CancellationToken ct)
|
private static ulong PollardRho(ulong n, CancellationToken ct)
|
||||||
{
|
{
|
||||||
if ((n & 1UL) == 0UL)
|
if ((n & 1UL) == 0UL)
|
||||||
{
|
{
|
||||||
return 2UL;
|
return 2UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong c, d, x, y, diff;
|
ulong c, d, x, y, diff;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return 0UL;
|
return 0UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = (DeterministicRandomCpu.NextUInt64() % (n - 1UL)) + 1UL;
|
c = (DeterministicRandomCpu.NextUInt64() % (n - 1UL)) + 1UL;
|
||||||
x = (DeterministicRandomCpu.NextUInt64() % (n - 2UL)) + 2UL;
|
x = (DeterministicRandomCpu.NextUInt64() % (n - 2UL)) + 2UL;
|
||||||
y = x;
|
y = x;
|
||||||
d = 1UL;
|
d = 1UL;
|
||||||
|
|
||||||
while (d == 1UL)
|
while (d == 1UL)
|
||||||
{
|
{
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return 0UL;
|
return 0UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = x.MulMod64(x, n).AddMod64(c, n);
|
x = x.MulMod64(x, n).AddMod64(c, n);
|
||||||
if (ct.IsCancellationRequested)
|
y = y.MulMod64(y, n).AddMod64(c, n);
|
||||||
{
|
y = y.MulMod64(y, n).AddMod64(c, n);
|
||||||
return 0UL;
|
diff = x > y ? x - y : y - x;
|
||||||
}
|
d = Gcd(diff, n, ct);
|
||||||
|
}
|
||||||
|
|
||||||
y = y.MulMod64(y, n).AddMod64(c, n);
|
if (d != n)
|
||||||
if (ct.IsCancellationRequested)
|
{
|
||||||
{
|
return d;
|
||||||
return 0UL;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
y = y.MulMod64(y, n).AddMod64(c, n);
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
diff = x > y ? x - y : y - x;
|
private static ulong Gcd(ulong a, ulong b, CancellationToken ct)
|
||||||
if (ct.IsCancellationRequested)
|
{
|
||||||
{
|
ulong t;
|
||||||
return 0UL;
|
while (b != 0UL)
|
||||||
}
|
{
|
||||||
|
if (ct.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return 1UL; // We're returning 1 to avoid division by 0 in any place
|
||||||
|
}
|
||||||
|
|
||||||
d = Gcd(diff, n, ct);
|
t = a % b; // TODO: Replace this with the binary GCD helper used in the gcd benchmarks so we
|
||||||
}
|
// avoid slow modulo operations while factoring large composites.
|
||||||
|
a = b;
|
||||||
|
b = t;
|
||||||
|
}
|
||||||
|
|
||||||
if (d != n)
|
return a;
|
||||||
{
|
}
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
// TODO: Port this Miller–Rabin routine to UInt128 intrinsics so we avoid the BigInteger.ModPow calls that lag by orders of magnitude on the large inputs highlighted in the Pow2MontgomeryMod benchmarks.
|
||||||
private static ulong Gcd(ulong a, ulong b, CancellationToken ct)
|
private static bool IsPrime128(UInt128 n, CancellationToken ct)
|
||||||
{
|
{
|
||||||
ulong t;
|
if (n <= 3)
|
||||||
while (b != 0UL)
|
{
|
||||||
{
|
return n >= 2;
|
||||||
if (ct.IsCancellationRequested)
|
}
|
||||||
{
|
|
||||||
return 1UL; // We're returning 1 to avoid division by 0 in any place
|
|
||||||
}
|
|
||||||
|
|
||||||
t = a % b; // TODO: Replace this with the binary GCD helper used in the gcd benchmarks so we
|
if ((n & 1) == 0)
|
||||||
// avoid slow modulo operations while factoring large composites.
|
{
|
||||||
a = b;
|
return false;
|
||||||
b = t;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return a;
|
BigInteger bn = (BigInteger)n;
|
||||||
}
|
BigInteger d = bn - 1;
|
||||||
|
int s = 0;
|
||||||
|
while ((d & 1) == 0)
|
||||||
|
{
|
||||||
|
d >>= 1;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Port this Miller–Rabin routine to UInt128 intrinsics so we avoid the BigInteger.ModPow calls that lag by orders of magnitude on the large inputs highlighted in the Pow2MontgomeryMod benchmarks.
|
BigInteger[] bases = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];
|
||||||
private static bool IsPrime128(UInt128 n, CancellationToken ct)
|
for (int i = 0; i < bases.Length; i++)
|
||||||
{
|
{
|
||||||
if (n <= 3)
|
BigInteger a = bases[i];
|
||||||
{
|
if (a >= bn)
|
||||||
return n >= 2;
|
{
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ((n & 1) == 0)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInteger bn = (BigInteger)n;
|
BigInteger x = BigInteger.ModPow(a, d, bn);
|
||||||
BigInteger d = bn - 1;
|
if (x == 1 || x == bn - 1)
|
||||||
int s = 0;
|
{
|
||||||
while ((d & 1) == 0)
|
continue;
|
||||||
{
|
}
|
||||||
d >>= 1;
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigInteger[] bases = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];
|
bool cont = false;
|
||||||
for (int i = 0; i < bases.Length; i++)
|
for (int r = 1; r < s; r++)
|
||||||
{
|
{
|
||||||
BigInteger a = bases[i];
|
if (ct.IsCancellationRequested)
|
||||||
if (a >= bn)
|
{
|
||||||
{
|
return false;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ct.IsCancellationRequested)
|
x = BigInteger.ModPow(x, 2, bn);
|
||||||
{
|
if (x == bn - 1)
|
||||||
return false;
|
{
|
||||||
}
|
cont = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BigInteger x = BigInteger.ModPow(a, d, bn);
|
if (!cont)
|
||||||
if (x == 1 || x == bn - 1)
|
{
|
||||||
{
|
return false;
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cont = false;
|
return true;
|
||||||
for (int r = 1; r < s; r++)
|
}
|
||||||
{
|
|
||||||
if (ct.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = BigInteger.ModPow(x, 2, bn);
|
private static UInt128 PollardRho128(UInt128 n, CancellationToken ct)
|
||||||
if (x == bn - 1)
|
{
|
||||||
{
|
if ((n & 1) == 0)
|
||||||
cont = true;
|
{
|
||||||
break;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!cont)
|
UInt128 c, d, x, y, diff;
|
||||||
{
|
while (true)
|
||||||
return false;
|
{
|
||||||
}
|
if (ct.IsCancellationRequested)
|
||||||
}
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
c = (DeterministicRandomCpu.NextUInt128() % (n - 1)) + 1;
|
||||||
}
|
x = (DeterministicRandomCpu.NextUInt128() % (n - 2)) + 2;
|
||||||
|
y = x;
|
||||||
|
d = 1;
|
||||||
|
|
||||||
private static UInt128 PollardRho128(UInt128 n, CancellationToken ct)
|
while (d == 1)
|
||||||
{
|
{
|
||||||
if ((n & 1) == 0)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return 2;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt128 c, d, x, y, diff;
|
x = x.MulMod(x, n).AddMod(c, n);
|
||||||
while (true)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
if (ct.IsCancellationRequested)
|
return 0;
|
||||||
{
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = (DeterministicRandomCpu.NextUInt128() % (n - 1)) + 1;
|
y = y.MulMod(y, n).AddMod(c, n);
|
||||||
x = (DeterministicRandomCpu.NextUInt128() % (n - 2)) + 2;
|
if (ct.IsCancellationRequested)
|
||||||
y = x;
|
{
|
||||||
d = 1;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
while (d == 1)
|
y = y.MulMod(y, n).AddMod(c, n);
|
||||||
{
|
diff = x > y ? x - y : y - x;
|
||||||
if (ct.IsCancellationRequested)
|
if (ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = x.MulMod(x, n).AddMod(c, n);
|
d = Gcd128(diff, n, ct); // TODO: Move this to the binary GCD implementation once the
|
||||||
if (ct.IsCancellationRequested)
|
// UInt128 benchmarks validate the optimized routine.
|
||||||
{
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
y = y.MulMod(y, n).AddMod(c, n);
|
if (d != n)
|
||||||
if (ct.IsCancellationRequested)
|
{
|
||||||
{
|
return d;
|
||||||
return 0;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
y = y.MulMod(y, n).AddMod(c, n);
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
diff = x > y ? x - y : y - x;
|
private static UInt128 Gcd128(UInt128 a, UInt128 b, CancellationToken ct)
|
||||||
if (ct.IsCancellationRequested)
|
{
|
||||||
{
|
UInt128 t;
|
||||||
return 0;
|
while (b != 0)
|
||||||
}
|
{
|
||||||
|
if (ct.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
d = Gcd128(diff, n, ct); // TODO: Move this to the binary GCD implementation once the
|
t = a % b; // TODO: Replace with the binary GCD helper once the UInt128 benchmarks land so
|
||||||
// UInt128 benchmarks validate the optimized routine.
|
// we remove the slow `%` from the wide gcd loop.
|
||||||
}
|
a = b;
|
||||||
|
b = t;
|
||||||
|
}
|
||||||
|
|
||||||
if (d != n)
|
return a;
|
||||||
{
|
}
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static UInt128 Gcd128(UInt128 a, UInt128 b, CancellationToken ct)
|
|
||||||
{
|
|
||||||
UInt128 t;
|
|
||||||
while (b != 0)
|
|
||||||
{
|
|
||||||
if (ct.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
t = a % b; // TODO: Replace with the binary GCD helper once the UInt128 benchmarks land so
|
|
||||||
// we remove the slow `%` from the wide gcd loop.
|
|
||||||
a = b;
|
|
||||||
b = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,29 +5,34 @@ 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)
|
||||||
{
|
{
|
||||||
ulong inv = modulus;
|
ulong inv = modulus;
|
||||||
inv *= unchecked(2UL - modulus * inv);
|
inv *= unchecked(2UL - modulus * inv);
|
||||||
inv *= unchecked(2UL - modulus * inv);
|
inv *= unchecked(2UL - modulus * inv);
|
||||||
inv *= unchecked(2UL - modulus * inv);
|
inv *= unchecked(2UL - modulus * inv);
|
||||||
inv *= unchecked(2UL - modulus * inv);
|
inv *= unchecked(2UL - modulus * inv);
|
||||||
inv *= unchecked(2UL - modulus * inv);
|
inv *= unchecked(2UL - modulus * inv);
|
||||||
inv *= unchecked(2UL - modulus * inv);
|
inv *= unchecked(2UL - modulus * inv);
|
||||||
inv *= unchecked(2UL - modulus * inv);
|
inv *= unchecked(2UL - modulus * inv);
|
||||||
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,30 +45,30 @@ 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 montgomeryOne = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64, modulus);
|
|
||||||
ulong montgomeryTwo = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64x2, modulus);
|
|
||||||
ulong montgomeryTwoSquared = ULongExtensions.MontgomeryMultiplyCpu(montgomeryTwo, montgomeryTwo, modulus, nPrime);
|
|
||||||
|
|
||||||
return queue.Rent(
|
|
||||||
modulus,
|
|
||||||
nPrime,
|
|
||||||
montgomeryOne,
|
|
||||||
montgomeryTwo,
|
|
||||||
montgomeryTwoSquared);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static MontgomeryDivisorData Rent(this Queue<MontgomeryDivisorData> queue, ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
|
|
||||||
{
|
{
|
||||||
if (queue.TryDequeue(out var pooled))
|
ulong nPrime = ComputeMontgomeryNPrime(modulus);
|
||||||
|
ulong montgomeryOne = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64, modulus);
|
||||||
|
ulong montgomeryTwo = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64x2, modulus);
|
||||||
|
ulong montgomeryTwoSquared = ULongExtensions.MontgomeryMultiplyCpu(montgomeryTwo, montgomeryTwo, modulus, nPrime);
|
||||||
|
|
||||||
|
return queue.Rent(
|
||||||
|
modulus,
|
||||||
|
nPrime,
|
||||||
|
montgomeryOne,
|
||||||
|
montgomeryTwo,
|
||||||
|
montgomeryTwoSquared);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static MontgomeryDivisorData Rent(this FixedCapacityStack<MontgomeryDivisorData> queue, ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
|
||||||
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@@ -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,13 +7,16 @@ 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 MaxOddPowersCount = 128;
|
public const int DefaultStringBuilderCapacity = 10_240;
|
||||||
|
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;
|
||||||
public static int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup;
|
public static int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup;
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Return(StringBuilder sb)
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
{
|
public static void Return(StringBuilder sb)
|
||||||
_ = sb.Clear();
|
{
|
||||||
// TODO: Preserve the builder's capacity when returning it so the pool hands the same buffer back without shrinkage.
|
_ = sb.Clear();
|
||||||
_stringBuilderPool.Enqueue(sb);
|
// TODO: Preserve the builder's capacity when returning it so the pool hands the same buffer back without shrinkage.
|
||||||
}
|
_stringBuilderPool.Push(sb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,241 +1,233 @@
|
|||||||
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 object _lock = new();
|
||||||
|
private readonly Action<Task> _taskExecutor;
|
||||||
|
private readonly int _maximumConcurrency;
|
||||||
|
private readonly SemaphoreSlim _workAvailable = new(0);
|
||||||
|
private readonly CancellationTokenSource _disposeCancellation = new();
|
||||||
|
private int _pendingTaskCount;
|
||||||
|
private int _currentThreads;
|
||||||
|
private int _threadId;
|
||||||
|
|
||||||
private readonly ConcurrentQueue<WorkItem> _pendingTaskQueue = new();
|
public TaskThreadPool(int minimumThreads, Action<Task> taskExecutor)
|
||||||
private readonly HashSet<Thread> _runningThreads = new();
|
{
|
||||||
private readonly object _lock = new();
|
if (minimumThreads <= 0)
|
||||||
private readonly Action<Task> _taskExecutor;
|
{
|
||||||
private readonly int _maximumConcurrency;
|
throw new ArgumentOutOfRangeException(nameof(minimumThreads));
|
||||||
private readonly SemaphoreSlim _workAvailable = new(0);
|
}
|
||||||
private readonly CancellationTokenSource _disposeCancellation = new();
|
|
||||||
private int _pendingTaskCount;
|
|
||||||
private int _currentThreads;
|
|
||||||
private int _threadId;
|
|
||||||
|
|
||||||
public TaskThreadPool(int minimumThreads, Action<Task> taskExecutor)
|
_taskExecutor = taskExecutor ?? throw new ArgumentNullException(nameof(taskExecutor));
|
||||||
{
|
_maximumConcurrency = minimumThreads;
|
||||||
if (minimumThreads <= 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(minimumThreads));
|
|
||||||
}
|
|
||||||
|
|
||||||
_taskExecutor = taskExecutor ?? throw new ArgumentNullException(nameof(taskExecutor));
|
List<Thread> threadsToStart = new(minimumThreads);
|
||||||
_maximumConcurrency = minimumThreads;
|
|
||||||
|
|
||||||
List<Thread> threadsToStart = new(minimumThreads);
|
lock (_lock)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < minimumThreads; i++)
|
||||||
|
{
|
||||||
|
Thread thread = CreateWorkerThread();
|
||||||
|
_runningThreads.Add(thread);
|
||||||
|
_currentThreads++;
|
||||||
|
threadsToStart.Add(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lock (_lock)
|
foreach (Thread thread in threadsToStart)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < minimumThreads; i++)
|
thread.Start();
|
||||||
{
|
}
|
||||||
Thread thread = CreateWorkerThread();
|
}
|
||||||
_runningThreads.Add(thread);
|
|
||||||
_currentThreads++;
|
|
||||||
threadsToStart.Add(thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Thread thread in threadsToStart)
|
public int ThreadCount => _maximumConcurrency;
|
||||||
{
|
|
||||||
thread.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ThreadCount => _maximumConcurrency;
|
public void Queue(Task task)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(task);
|
||||||
|
|
||||||
public void Queue(Task task)
|
List<Thread>? threadsToStart;
|
||||||
{
|
|
||||||
if (task is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(task));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Thread>? threadsToStart;
|
lock (_lock)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposeCancellation.IsCancellationRequested, nameof(TaskThreadPool));
|
||||||
|
|
||||||
lock (_lock)
|
_pendingTaskQueue.Push(new WorkItem(task));
|
||||||
{
|
_pendingTaskCount++;
|
||||||
if (_disposeCancellation.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(nameof(TaskThreadPool));
|
|
||||||
}
|
|
||||||
|
|
||||||
_pendingTaskQueue.Enqueue(new WorkItem(task));
|
threadsToStart = EnsureWorkerCapacityLocked(null);
|
||||||
_pendingTaskCount++;
|
}
|
||||||
|
|
||||||
threadsToStart = EnsureWorkerCapacityLocked(null);
|
_workAvailable.Release();
|
||||||
}
|
|
||||||
|
|
||||||
_workAvailable.Release();
|
if (threadsToStart is not null)
|
||||||
|
{
|
||||||
|
foreach (Thread thread in threadsToStart)
|
||||||
|
{
|
||||||
|
thread.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (threadsToStart is not null)
|
public IEnumerable<Task> GetScheduledTasks()
|
||||||
{
|
{
|
||||||
foreach (Thread thread in threadsToStart)
|
WorkItem[] snapshot = _pendingTaskQueue.ToArray();
|
||||||
{
|
|
||||||
thread.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Task> GetScheduledTasks()
|
if (snapshot.Length == 0)
|
||||||
{
|
{
|
||||||
WorkItem[] snapshot = _pendingTaskQueue.ToArray();
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
if (snapshot.Length == 0)
|
Task[] tasks = new Task[snapshot.Length];
|
||||||
{
|
|
||||||
return Array.Empty<Task>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Task[] tasks = new Task[snapshot.Length];
|
for (int i = 0; i < snapshot.Length; i++)
|
||||||
|
{
|
||||||
|
tasks[i] = snapshot[i].Task;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < snapshot.Length; i++)
|
ConcurrentFixedCapacityStack<WorkItem>.Return(snapshot);
|
||||||
{
|
return tasks;
|
||||||
tasks[i] = snapshot[i].Task;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return tasks;
|
public void Dispose()
|
||||||
}
|
{
|
||||||
|
Thread[] threadsToJoin;
|
||||||
|
|
||||||
public void Dispose()
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Thread[] threadsToJoin;
|
_disposeCancellation.Cancel();
|
||||||
|
|
||||||
lock (_lock)
|
threadsToJoin = new Thread[_runningThreads.Count];
|
||||||
{
|
_runningThreads.CopyTo(threadsToJoin);
|
||||||
_disposeCancellation.Cancel();
|
}
|
||||||
|
|
||||||
threadsToJoin = new Thread[_runningThreads.Count];
|
if (threadsToJoin.Length > 0)
|
||||||
_runningThreads.CopyTo(threadsToJoin);
|
{
|
||||||
}
|
_workAvailable.Release(threadsToJoin.Length);
|
||||||
|
}
|
||||||
|
|
||||||
if (threadsToJoin.Length > 0)
|
foreach (Thread thread in threadsToJoin)
|
||||||
{
|
{
|
||||||
_workAvailable.Release(threadsToJoin.Length);
|
thread.Join();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Thread thread in threadsToJoin)
|
_workAvailable.Dispose();
|
||||||
{
|
_disposeCancellation.Dispose();
|
||||||
thread.Join();
|
|
||||||
}
|
|
||||||
|
|
||||||
_workAvailable.Dispose();
|
lock (_lock)
|
||||||
_disposeCancellation.Dispose();
|
{
|
||||||
|
_runningThreads.Clear();
|
||||||
|
|
||||||
lock (_lock)
|
while (_pendingTaskQueue.Pop() is { })
|
||||||
{
|
{
|
||||||
_runningThreads.Clear();
|
}
|
||||||
|
|
||||||
while (_pendingTaskQueue.TryDequeue(out _))
|
_pendingTaskCount = 0;
|
||||||
{
|
_currentThreads = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_pendingTaskCount = 0;
|
private bool TryDequeueWork(out WorkItem workItem)
|
||||||
_currentThreads = 0;
|
{
|
||||||
}
|
CancellationToken cancellationToken = _disposeCancellation.Token;
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryDequeueWork(out WorkItem workItem)
|
while (true)
|
||||||
{
|
{
|
||||||
CancellationToken cancellationToken = _disposeCancellation.Token;
|
try
|
||||||
|
{
|
||||||
|
_workAvailable.Wait(cancellationToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
workItem = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
while (true)
|
workItem = _pendingTaskQueue.Pop();
|
||||||
{
|
if (workItem is { })
|
||||||
try
|
{
|
||||||
{
|
Interlocked.Decrement(ref _pendingTaskCount);
|
||||||
_workAvailable.Wait(cancellationToken);
|
return true;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
}
|
||||||
{
|
}
|
||||||
workItem = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_pendingTaskQueue.TryDequeue(out workItem))
|
private void WorkerLoop()
|
||||||
{
|
{
|
||||||
Interlocked.Decrement(ref _pendingTaskCount);
|
try
|
||||||
return true;
|
{
|
||||||
}
|
if (!TryDequeueWork(out WorkItem workItem))
|
||||||
}
|
{
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
private void WorkerLoop()
|
_taskExecutor(workItem.Task);
|
||||||
{
|
}
|
||||||
try
|
finally
|
||||||
{
|
{
|
||||||
if (!TryDequeueWork(out WorkItem workItem))
|
CompleteWorker();
|
||||||
{
|
}
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_taskExecutor(workItem.Task);
|
private void CompleteWorker()
|
||||||
}
|
{
|
||||||
finally
|
List<Thread>? threadsToStart = null;
|
||||||
{
|
|
||||||
CompleteWorker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CompleteWorker()
|
lock (_lock)
|
||||||
{
|
{
|
||||||
List<Thread>? threadsToStart = null;
|
_runningThreads.Remove(Thread.CurrentThread);
|
||||||
|
_currentThreads--;
|
||||||
|
|
||||||
lock (_lock)
|
if (!_disposeCancellation.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
_runningThreads.Remove(Thread.CurrentThread);
|
threadsToStart = EnsureWorkerCapacityLocked(null);
|
||||||
_currentThreads--;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!_disposeCancellation.IsCancellationRequested)
|
if (threadsToStart is not null)
|
||||||
{
|
{
|
||||||
threadsToStart = EnsureWorkerCapacityLocked(null);
|
foreach (Thread thread in threadsToStart)
|
||||||
}
|
{
|
||||||
}
|
thread.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (threadsToStart is not null)
|
private List<Thread>? EnsureWorkerCapacityLocked(List<Thread>? threadsToStart)
|
||||||
{
|
{
|
||||||
foreach (Thread thread in threadsToStart)
|
int pendingTasks = Math.Min(_maximumConcurrency, Volatile.Read(ref _pendingTaskCount));
|
||||||
{
|
|
||||||
thread.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Thread>? EnsureWorkerCapacityLocked(List<Thread>? threadsToStart)
|
while (!_disposeCancellation.IsCancellationRequested && _currentThreads < pendingTasks)
|
||||||
{
|
{
|
||||||
int pendingTasks = Math.Min(_maximumConcurrency, Volatile.Read(ref _pendingTaskCount));
|
Thread thread = CreateWorkerThread();
|
||||||
|
_runningThreads.Add(thread);
|
||||||
|
_currentThreads++;
|
||||||
|
|
||||||
while (!_disposeCancellation.IsCancellationRequested && _currentThreads < pendingTasks)
|
threadsToStart ??= [];
|
||||||
{
|
threadsToStart.Add(thread);
|
||||||
Thread thread = CreateWorkerThread();
|
}
|
||||||
_runningThreads.Add(thread);
|
|
||||||
_currentThreads++;
|
|
||||||
|
|
||||||
threadsToStart ??= new List<Thread>();
|
return threadsToStart;
|
||||||
threadsToStart.Add(thread);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return threadsToStart;
|
private Thread CreateWorkerThread()
|
||||||
}
|
{
|
||||||
|
int threadIndex = Interlocked.Increment(ref _threadId);
|
||||||
|
|
||||||
private Thread CreateWorkerThread()
|
return new Thread(WorkerLoop)
|
||||||
{
|
{
|
||||||
int threadIndex = Interlocked.Increment(ref _threadId);
|
IsBackground = true,
|
||||||
|
Name = $"{nameof(TaskThreadPool)}-{threadIndex}"
|
||||||
return new Thread(WorkerLoop)
|
};
|
||||||
{
|
}
|
||||||
IsBackground = true,
|
|
||||||
Name = $"{nameof(TaskThreadPool)}-{threadIndex}"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -6,36 +6,30 @@ using PeterO.Numbers;
|
|||||||
|
|
||||||
namespace PerfectNumbers.Core
|
namespace PerfectNumbers.Core
|
||||||
{
|
{
|
||||||
public static class ThreadStaticPools
|
public static class ThreadStaticPools
|
||||||
{
|
{
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static ArrayPool<EInteger>? _eintegerPool;
|
private static ArrayPool<EInteger>? _eintegerPool;
|
||||||
|
|
||||||
public static ArrayPool<EInteger> EIntegerPool
|
public static ArrayPool<EInteger> EIntegerPool
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _eintegerPool ??= ArrayPool<EInteger>.Create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static Queue<List<KeyValuePair<UInt128, int>>>? _keyValuePairUInt128IntegerPool;
|
|
||||||
|
|
||||||
public static Queue<List<KeyValuePair<UInt128, int>>> KeyValuePairUInt128IntegerPool
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _keyValuePairUInt128IntegerPool ??= [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static List<KeyValuePair<UInt128, int>> Rent(this Queue<List<KeyValuePair<UInt128, int>>> pool, int capacity)
|
|
||||||
{
|
{
|
||||||
if (pool.TryDequeue(out var pooled))
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
get => _eintegerPool ??= ArrayPool<EInteger>.Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static FixedCapacityStack<List<KeyValuePair<UInt128, int>>>? _keyValuePairUInt128IntegerPool;
|
||||||
|
|
||||||
|
public static FixedCapacityStack<List<KeyValuePair<UInt128, int>>> KeyValuePairUInt128IntegerPool
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
get => _keyValuePairUInt128IntegerPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static List<KeyValuePair<UInt128, int>> Rent(this FixedCapacityStack<List<KeyValuePair<UInt128, int>>> pool, int capacity)
|
||||||
|
{
|
||||||
|
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)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _uint128ListPool ??= [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static List<UInt128> Rent(this Queue<List<UInt128>> pool, int capacity)
|
|
||||||
{
|
{
|
||||||
if (pool.TryDequeue(out var pooled))
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
get => _uint128ListPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static List<UInt128> Rent(this FixedCapacityStack<List<UInt128>> pool, int capacity)
|
||||||
|
{
|
||||||
|
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)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _uint128IntDictionaryPool ??= [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Dictionary<UInt128, int> Rent(this Queue<Dictionary<UInt128, int>> pool, int capacity)
|
|
||||||
{
|
{
|
||||||
if (pool.TryDequeue(out var pooled))
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
get => _uint128IntDictionaryPool ??= new(PerfectNumberConstants.DefaultPoolCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public static Dictionary<UInt128, int> Rent(this FixedCapacityStack<Dictionary<UInt128, int>> pool, int capacity)
|
||||||
|
{
|
||||||
|
if (pool.Pop() is { } pooled)
|
||||||
{
|
{
|
||||||
pooled.EnsureCapacity(capacity);
|
pooled.EnsureCapacity(capacity);
|
||||||
pooled.Clear();
|
pooled.Clear();
|
||||||
@@ -109,368 +97,289 @@ 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]
|
||||||
private static ArrayPool<FactorEntry128>? _factorEntry128Pool;
|
private static ArrayPool<FactorEntry128>? _factorEntry128Pool;
|
||||||
|
|
||||||
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]
|
||||||
private static ArrayPool<GpuUInt128>? _gpuUInt128Pool;
|
private static ArrayPool<GpuUInt128>? _gpuUInt128Pool;
|
||||||
|
|
||||||
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)
|
{
|
||||||
{
|
session.Configure(gpu, orderDevice);
|
||||||
return null;
|
_mersenneCpuDivisorSession = null;
|
||||||
}
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
_mersenneCpuDivisorSession = null;
|
return new MersenneCpuDivisorScanSession(gpu, orderDevice);
|
||||||
return session;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
internal static void ReturnMersenneCpuDivisorSession(MersenneCpuDivisorScanSession session)
|
internal static void ReturnMersenneCpuDivisorSession(MersenneCpuDivisorScanSession session) => _mersenneCpuDivisorSession = 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]
|
||||||
private static ArrayPool<ulong>? _ulongPool;
|
private static ArrayPool<ulong>? _ulongPool;
|
||||||
|
|
||||||
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]
|
||||||
private static ArrayPool<bool>? _boolPool;
|
private static ArrayPool<bool>? _boolPool;
|
||||||
|
|
||||||
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]
|
||||||
private static ArrayPool<byte>? _bytePool;
|
private static ArrayPool<byte>? _bytePool;
|
||||||
|
|
||||||
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]
|
||||||
private static ArrayPool<MontgomeryDivisorData>? _montgomeryDivisorDataPool;
|
private static ArrayPool<MontgomeryDivisorData>? _montgomeryDivisorDataPool;
|
||||||
|
|
||||||
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]
|
||||||
private static ArrayPool<GpuDivisorPartialData>? _gpuDivisorPartialDataPool;
|
private static ArrayPool<GpuDivisorPartialData>? _gpuDivisorPartialDataPool;
|
||||||
|
|
||||||
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]
|
||||||
private static ArrayPool<int>? _intPool;
|
private static ArrayPool<int>? _intPool;
|
||||||
|
|
||||||
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]
|
||||||
private static ArrayPool<char>? _charPool;
|
private static ArrayPool<char>? _charPool;
|
||||||
|
|
||||||
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)
|
{
|
||||||
{
|
if (list.Capacity < capacityHint)
|
||||||
int lastIndex = poolCount - 1;
|
{
|
||||||
List<ulong> list = pool[lastIndex];
|
list.Capacity = capacityHint;
|
||||||
pool.RemoveAt(lastIndex);
|
}
|
||||||
if (list.Capacity < capacityHint)
|
|
||||||
{
|
|
||||||
list.Capacity = capacityHint;
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
||||||
}
|
private static ArrayPool<(ulong, ulong)>? _ulongUlongTupleArrayPool;
|
||||||
|
|
||||||
[ThreadStatic]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private static ArrayPool<(ulong, ulong)>? _ulongUlongTupleArrayPool;
|
public static (ulong, ulong)[] RentUlongUlongArray(int capacityHint)
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static (ulong, ulong)[] RentUlongUlongArray(int capacityHint)
|
|
||||||
{
|
{
|
||||||
_ulongUlongTupleArrayPool ??= ArrayPool<(ulong, ulong)>.Create();
|
_ulongUlongTupleArrayPool ??= ArrayPool<(ulong, ulong)>.Create();
|
||||||
ArrayPool<(ulong, ulong)>? pool = _ulongUlongTupleArrayPool;
|
ArrayPool<(ulong, ulong)>? pool = _ulongUlongTupleArrayPool;
|
||||||
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)
|
{
|
||||||
{
|
if (list.Capacity < capacityHint)
|
||||||
int lastIndex = poolCount - 1;
|
{
|
||||||
List<PrimeOrderCalculator.PendingEntry> list = pool[lastIndex];
|
list.Capacity = capacityHint;
|
||||||
pool.RemoveAt(lastIndex);
|
}
|
||||||
if (list.Capacity < capacityHint)
|
|
||||||
{
|
|
||||||
list.Capacity = capacityHint;
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
||||||
}
|
private static FixedCapacityStack<FixedCapacityStack<ulong>>? _ulongStackPool;
|
||||||
|
|
||||||
[ThreadStatic]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private static List<Stack<ulong>>? _ulongStackPool;
|
public static FixedCapacityStack<ulong> RentUlongStack(int capacityHint)
|
||||||
|
{
|
||||||
|
FixedCapacityStack<FixedCapacityStack<ulong>>? pool = _ulongStackPool;
|
||||||
|
if (pool is not null)
|
||||||
|
{
|
||||||
|
if (pool.Pop() is { } stack)
|
||||||
|
{
|
||||||
|
stack.Capacity = capacityHint;
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ulongStackPool = new(PrimeOrderConstants.GpuSmallPrimeFactorSlots);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
return new FixedCapacityStack<ulong>(capacityHint);
|
||||||
public static Stack<ulong> RentUlongStack(int capacityHint)
|
}
|
||||||
{
|
|
||||||
List<Stack<ulong>>? pool = _ulongStackPool;
|
|
||||||
if (pool is not null)
|
|
||||||
{
|
|
||||||
int poolCount = pool.Count;
|
|
||||||
if (poolCount > 0)
|
|
||||||
{
|
|
||||||
int lastIndex = poolCount - 1;
|
|
||||||
Stack<ulong> stack = pool[lastIndex];
|
|
||||||
pool.RemoveAt(lastIndex);
|
|
||||||
|
|
||||||
if (stack.Count < capacityHint)
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
{
|
public static void ReturnUlongStack(FixedCapacityStack<ulong> stack) => _ulongStackPool!.Push(stack);
|
||||||
stack.EnsureCapacity(capacityHint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack;
|
[ThreadStatic]
|
||||||
}
|
private static Dictionary<ulong, int>? _factorCountDictionary;
|
||||||
}
|
|
||||||
|
|
||||||
return new Stack<ulong>(capacityHint);
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
}
|
public static Dictionary<ulong, int> RentFactorCountDictionary()
|
||||||
|
{
|
||||||
|
Dictionary<ulong, int>? dictionary = _factorCountDictionary;
|
||||||
|
if (dictionary is null)
|
||||||
|
{
|
||||||
|
return new Dictionary<ulong, int>(8);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
_factorCountDictionary = null;
|
||||||
public static void ReturnUlongStack(Stack<ulong> stack)
|
return dictionary;
|
||||||
{
|
}
|
||||||
List<Stack<ulong>>? pool = _ulongStackPool;
|
|
||||||
if (pool is null)
|
|
||||||
{
|
|
||||||
pool = new List<Stack<ulong>>(4);
|
|
||||||
_ulongStackPool = pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.Add(stack);
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
}
|
public static void ReturnFactorCountDictionary(Dictionary<ulong, int> dictionary) => _factorCountDictionary = dictionary;
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static Dictionary<ulong, int>? _factorCountDictionary;
|
private static Dictionary<ulong, int>? _factorScratchDictionary;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static Dictionary<ulong, int> RentFactorCountDictionary()
|
public static Dictionary<ulong, int> RentFactorScratchDictionary()
|
||||||
{
|
{
|
||||||
Dictionary<ulong, int>? dictionary = _factorCountDictionary;
|
if (_factorScratchDictionary is { } dictionary)
|
||||||
if (dictionary is null)
|
{
|
||||||
{
|
_factorScratchDictionary = null;
|
||||||
return new Dictionary<ulong, int>(8);
|
return dictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
_factorCountDictionary = null;
|
return new Dictionary<ulong, int>(8);
|
||||||
return dictionary;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static void ReturnFactorCountDictionary(Dictionary<ulong, int> dictionary)
|
public static void ReturnFactorScratchDictionary(Dictionary<ulong, int> dictionary) => _factorScratchDictionary = dictionary;
|
||||||
{
|
|
||||||
_factorCountDictionary = dictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static Dictionary<ulong, int>? _factorScratchDictionary;
|
private static ExponentRemainderStepperCpu _exponentRemainderStepperCpu;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[ThreadStatic]
|
||||||
public static Dictionary<ulong, int> RentFactorScratchDictionary()
|
private static bool _hasExponentRemainderStepperCpu;
|
||||||
{
|
|
||||||
Dictionary<ulong, int>? dictionary = _factorScratchDictionary;
|
|
||||||
if (dictionary is null)
|
|
||||||
{
|
|
||||||
return new Dictionary<ulong, int>(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
_factorScratchDictionary = null;
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
return dictionary;
|
internal static ExponentRemainderStepperCpu RentExponentStepperCpu(in MontgomeryDivisorData divisorData, ulong cycleLength = 0UL)
|
||||||
}
|
{
|
||||||
|
if (_hasExponentRemainderStepperCpu && _exponentRemainderStepperCpu.MatchesDivisor(divisorData, cycleLength))
|
||||||
|
{
|
||||||
|
ExponentRemainderStepperCpu stepper = _exponentRemainderStepperCpu;
|
||||||
|
_hasExponentRemainderStepperCpu = false;
|
||||||
|
stepper.Reset();
|
||||||
|
return stepper;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
return new ExponentRemainderStepperCpu(divisorData, cycleLength);
|
||||||
public static void ReturnFactorScratchDictionary(Dictionary<ulong, int> dictionary)
|
}
|
||||||
{
|
|
||||||
_factorScratchDictionary = dictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ThreadStatic]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private static ExponentRemainderStepperCpu _exponentRemainderStepperCpu;
|
internal static void ReturnExponentStepperCpu(ExponentRemainderStepperCpu stepper)
|
||||||
|
{
|
||||||
[ThreadStatic]
|
_exponentRemainderStepperCpu = stepper;
|
||||||
private static bool _hasExponentRemainderStepperCpu;
|
_hasExponentRemainderStepperCpu = true;
|
||||||
|
}
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
}
|
||||||
internal static ExponentRemainderStepperCpu RentExponentStepperCpu(in MontgomeryDivisorData divisorData, ulong cycleLength = 0UL)
|
|
||||||
{
|
|
||||||
if (_hasExponentRemainderStepperCpu && _exponentRemainderStepperCpu.MatchesDivisor(divisorData, cycleLength))
|
|
||||||
{
|
|
||||||
ExponentRemainderStepperCpu stepper = _exponentRemainderStepperCpu;
|
|
||||||
_hasExponentRemainderStepperCpu = false;
|
|
||||||
stepper.Reset();
|
|
||||||
return stepper;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ExponentRemainderStepperCpu(divisorData, cycleLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static void ReturnExponentStepperCpu(ExponentRemainderStepperCpu stepper)
|
|
||||||
{
|
|
||||||
stepper.Reset();
|
|
||||||
_exponentRemainderStepperCpu = stepper;
|
|
||||||
_hasExponentRemainderStepperCpu = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,67 +1,72 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core;
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
public sealed class UnboundedTaskScheduler : TaskScheduler
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ConfigureThreadCount(int threadCount)
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
{
|
public static void ConfigureThreadCount(int threadCount)
|
||||||
_configuredThreadCount = threadCount;
|
{
|
||||||
PrimeOrderCalculator.PrimeOrderCalculatorConfig.ConfigureHeuristicDefault(threadCount);
|
_configuredThreadCount = threadCount;
|
||||||
|
PrimeOrderCalculator.PrimeOrderCalculatorConfig.ConfigureHeuristicDefault(threadCount);
|
||||||
|
|
||||||
// On EvenPerfectBitScanner's execution path the scheduler instance is created after configuration,
|
// On EvenPerfectBitScanner's execution path the scheduler instance is created after configuration,
|
||||||
// so updating an existing pool would never run. Leave the update path disabled for now.
|
// so updating an existing pool would never run. Leave the update path disabled for now.
|
||||||
// if (_instance is not null)
|
// if (_instance is not null)
|
||||||
// {
|
// {
|
||||||
// _instance.UpdateThreadPool(threadCount);
|
// _instance.UpdateThreadPool(threadCount);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int MaximumConcurrencyLevel => int.MaxValue;
|
public override int MaximumConcurrencyLevel
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
get => int.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void QueueTask(Task task)
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
{
|
protected override void QueueTask(Task task) => _threadPool.Queue(task);
|
||||||
_threadPool.Queue(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateThreadPool(int threadCount)
|
private void UpdateThreadPool(int threadCount)
|
||||||
{
|
{
|
||||||
if (_threadPool.ThreadCount == threadCount)
|
if (_threadPool.ThreadCount == threadCount)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskThreadPool newPool = new(threadCount, ExecuteTask);
|
TaskThreadPool newPool = new(threadCount, ExecuteTask);
|
||||||
TaskThreadPool oldPool = _threadPool;
|
TaskThreadPool oldPool = _threadPool;
|
||||||
_threadPool = newPool;
|
_threadPool = newPool;
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user