PrimeOrderCalculator.Cpu optimizations.
This commit is contained in:
1 parent
5523469e8a
commit
566cbc3599
4 files changed
+169
-68
No files matched your search
@@ -1,40 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core
|
namespace PerfectNumbers.Core
|
||||||
{
|
{
|
||||||
public struct IntArrayPool(int poolCapacity)
|
public struct IntArrayPool(int poolCapacity)
|
||||||
{
|
{
|
||||||
private readonly int[][] _arrays = new int[poolCapacity][];
|
private const int MinBucketPower = 1;
|
||||||
private int _count = 0;
|
private static readonly int MaxBucketPower = CalculateMaxBucketPower();
|
||||||
|
private static readonly int MaxBucketLength = 1 << MaxBucketPower;
|
||||||
|
private static readonly int BucketCount = MaxBucketPower - MinBucketPower + 1;
|
||||||
|
|
||||||
|
private readonly int[][] _arrays = new int[poolCapacity * BucketCount][];
|
||||||
|
private readonly int[] _counts = new int[BucketCount];
|
||||||
|
private readonly int _bucketCapacity = poolCapacity;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public int[] Rent(int length)
|
public readonly int[] Rent(int length)
|
||||||
{
|
{
|
||||||
int available = _count;
|
int bucketIndex = GetBucketIndex(length);
|
||||||
if (available == 0)
|
if (bucketIndex < 0)
|
||||||
{
|
{
|
||||||
return new int[length];
|
return new int[length];
|
||||||
}
|
}
|
||||||
|
|
||||||
int[][] arrays = _arrays;
|
int[] counts = _counts;
|
||||||
available--;
|
int available = counts[bucketIndex];
|
||||||
_count = available;
|
int bucketLength = GetBucketLength(bucketIndex);
|
||||||
|
|
||||||
int[] candidate = arrays[available];
|
if (available == 0)
|
||||||
return candidate.Length >= length ? candidate : new int[length];
|
{
|
||||||
|
return new int[bucketLength];
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = (bucketIndex * _bucketCapacity) + (available - 1);
|
||||||
|
counts[bucketIndex] = available - 1;
|
||||||
|
return _arrays[offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Return(int[] array)
|
public readonly void Return(int[] array)
|
||||||
{
|
{
|
||||||
int[][] arrays = _arrays;
|
int capacity = array.Length;
|
||||||
int available = _count;
|
int bucketIndex = GetBucketIndex(capacity);
|
||||||
|
if (bucketIndex < 0)
|
||||||
if (available < arrays.Length)
|
|
||||||
{
|
{
|
||||||
arrays[available] = array;
|
return;
|
||||||
_count = available + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bucketLength = GetBucketLength(bucketIndex);
|
||||||
|
if (capacity < bucketLength)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] counts = _counts;
|
||||||
|
int available = counts[bucketIndex];
|
||||||
|
int bucketCapacity = _bucketCapacity;
|
||||||
|
if (available >= bucketCapacity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = (bucketIndex * bucketCapacity) + available;
|
||||||
|
_arrays[offset] = array;
|
||||||
|
counts[bucketIndex] = available + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static int GetBucketIndex(int length)
|
||||||
|
{
|
||||||
|
if (length <= 0 || length > MaxBucketLength)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length <= (1 << MinBucketPower))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int power = 32 - BitOperations.LeadingZeroCount((uint)(length - 1));
|
||||||
|
if (power > MaxBucketPower)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return power - MinBucketPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static int GetBucketLength(int bucketIndex) => 1 << (bucketIndex + MinBucketPower);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static int CalculateMaxBucketPower()
|
||||||
|
{
|
||||||
|
int maxLength = Math.Min(int.MaxValue, Array.MaxLength);
|
||||||
|
int leadingZeros = BitOperations.LeadingZeroCount((uint)maxLength);
|
||||||
|
return 31 - leadingZeros;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,10 @@ namespace PerfectNumbers.Core
|
|||||||
public readonly int[] Exponents = new int[PrimeOrderConstants.GpuSmallPrimeFactorSlots];
|
public readonly int[] Exponents = new int[PrimeOrderConstants.GpuSmallPrimeFactorSlots];
|
||||||
public readonly FixedCapacityStack<ulong> CompositeStack = new (PrimeOrderConstants.GpuSmallPrimeFactorSlots);
|
public readonly FixedCapacityStack<ulong> CompositeStack = new (PrimeOrderConstants.GpuSmallPrimeFactorSlots);
|
||||||
public readonly List<PartialFactorPendingEntry> PendingFactors = new (PrimeOrderConstants.GpuSmallPrimeFactorSlots);
|
public readonly List<PartialFactorPendingEntry> PendingFactors = new (PrimeOrderConstants.GpuSmallPrimeFactorSlots);
|
||||||
|
public readonly ulong[] TempFactorsArray = new ulong[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3];
|
||||||
|
public readonly int[] TempExponentsArray = new int[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3];
|
||||||
|
public readonly ulong[] StackCandidates = new ulong[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3];
|
||||||
|
public readonly bool[] StackEvaluations = new bool[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3];
|
||||||
private PartialFactorResult? _next;
|
private PartialFactorResult? _next;
|
||||||
public ulong Cofactor;
|
public ulong Cofactor;
|
||||||
public bool FullyFactored;
|
public bool FullyFactored;
|
||||||
|
|||||||
@@ -545,12 +545,12 @@ internal static partial class PrimeOrderCalculator
|
|||||||
private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
|
private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
|
||||||
{
|
{
|
||||||
int length = factors.Count;
|
int length = factors.Count;
|
||||||
ReadOnlySpan<ulong> factorSpan = factors.Factors.AsSpan(0, length);
|
ReadOnlySpan<ulong> factorSpan = new(factors.Factors, 0, length);
|
||||||
ReadOnlySpan<int> exponentsSpan = factors.Exponents.AsSpan(0, length);
|
ReadOnlySpan<int> exponentsSpan = new(factors.Exponents, 0, length);
|
||||||
|
|
||||||
int newLength = length + 1;
|
int newLength = length + 1;
|
||||||
ulong[] tempFactorsArray = FixedCapacityPools.ExclusiveUlongArray.Rent(newLength);
|
ulong[] tempFactorsArray = factors.TempFactorsArray;
|
||||||
int[] tempExponentsArray = FixedCapacityPools.ExclusiveIntArray.Rent(newLength);
|
int[] tempExponentsArray = factors.TempExponentsArray;
|
||||||
|
|
||||||
Span<ulong> ulongBuffer = tempFactorsArray;
|
Span<ulong> ulongBuffer = tempFactorsArray;
|
||||||
factorSpan.CopyTo(ulongBuffer);
|
factorSpan.CopyTo(ulongBuffer);
|
||||||
@@ -568,14 +568,8 @@ internal static partial class PrimeOrderCalculator
|
|||||||
|
|
||||||
ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData);
|
ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData);
|
||||||
|
|
||||||
const int StackExponentCapacity = 16;
|
Span<ulong> stackCandidates = new(factors.StackCandidates);
|
||||||
const int ExponentHardLimit = 256;
|
Span<bool> stackEvaluations = new(factors.StackEvaluations);
|
||||||
|
|
||||||
Span<ulong> stackCandidates = stackalloc ulong[StackExponentCapacity];
|
|
||||||
Span<bool> stackEvaluations = stackalloc bool[StackExponentCapacity];
|
|
||||||
|
|
||||||
ulong[]? heapCandidateArray = null;
|
|
||||||
bool[]? heapEvaluationArray = null;
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
@@ -587,31 +581,10 @@ internal static partial class PrimeOrderCalculator
|
|||||||
// continue;
|
// continue;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (exponent > ExponentHardLimit)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Prime factor exponent {exponent} exceeds the supported limit of {ExponentHardLimit}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exponent <= StackExponentCapacity)
|
|
||||||
{
|
|
||||||
ProcessExponentLoweringPrime(stackCandidates[..exponent], stackEvaluations[..exponent], ref order, primeFactor, exponent, ref stepper);
|
ProcessExponentLoweringPrime(stackCandidates[..exponent], stackEvaluations[..exponent], ref order, primeFactor, exponent, ref stepper);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
heapCandidateArray ??= FixedCapacityPools.ExclusiveUlongArray.Rent(ExponentHardLimit);
|
|
||||||
heapEvaluationArray ??= ThreadStaticPools.BoolPool.Rent(ExponentHardLimit);
|
|
||||||
ProcessExponentLoweringPrime(heapCandidateArray.AsSpan(0, exponent), heapEvaluationArray.AsSpan(0, exponent), ref order, primeFactor, exponent, ref stepper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadStaticPools.ReturnExponentStepperCpu(stepper);
|
ThreadStaticPools.ReturnExponentStepperCpu(stepper);
|
||||||
|
|
||||||
if (heapCandidateArray is not null)
|
|
||||||
{
|
|
||||||
FixedCapacityPools.ExclusiveUlongArray.Return(heapCandidateArray);
|
|
||||||
ThreadStaticPools.BoolPool.Return(heapEvaluationArray!);
|
|
||||||
}
|
|
||||||
|
|
||||||
FixedCapacityPools.ExclusiveUlongArray.Return(tempFactorsArray);
|
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1945,4 +1918,3 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,40 +1,102 @@
|
|||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace PerfectNumbers.Core
|
namespace PerfectNumbers.Core
|
||||||
{
|
{
|
||||||
public struct UlongArrayPool(int poolCapacity)
|
public struct UlongArrayPool(int poolCapacity)
|
||||||
{
|
{
|
||||||
private readonly ulong[][] _arrays = new ulong[poolCapacity][];
|
private const int MinBucketPower = 1;
|
||||||
private int _count = 0;
|
private static readonly int MaxBucketPower = CalculateMaxBucketPower();
|
||||||
|
private static readonly int MaxBucketLength = 1 << MaxBucketPower;
|
||||||
|
private static readonly int BucketCount = MaxBucketPower - MinBucketPower + 1;
|
||||||
|
|
||||||
|
private readonly ulong[][] _arrays = new ulong[poolCapacity * BucketCount][];
|
||||||
|
private readonly int[] _counts = new int[BucketCount];
|
||||||
|
private readonly int _bucketCapacity = poolCapacity;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public ulong[] Rent(int length)
|
public readonly ulong[] Rent(int length)
|
||||||
{
|
{
|
||||||
int available = _count;
|
int bucketIndex = GetBucketIndex(length);
|
||||||
if (available == 0)
|
if (bucketIndex < 0)
|
||||||
{
|
{
|
||||||
return new ulong[length];
|
return new ulong[length];
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong[][] arrays = _arrays;
|
int[] counts = _counts;
|
||||||
available--;
|
int available = counts[bucketIndex];
|
||||||
_count = available;
|
int bucketLength = GetBucketLength(bucketIndex);
|
||||||
|
|
||||||
ulong[] candidate = arrays[available];
|
if (available == 0)
|
||||||
return candidate.Length >= length ? candidate : new ulong[length];
|
{
|
||||||
|
return new ulong[bucketLength];
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = (bucketIndex * _bucketCapacity) + (available - 1);
|
||||||
|
counts[bucketIndex] = available - 1;
|
||||||
|
return _arrays[offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public void Return(ulong[] array)
|
public readonly void Return(ulong[] array)
|
||||||
{
|
{
|
||||||
ulong[][] arrays = _arrays;
|
int capacity = array.Length;
|
||||||
int available = _count;
|
int bucketIndex = GetBucketIndex(capacity);
|
||||||
|
if (bucketIndex < 0)
|
||||||
if (available < arrays.Length)
|
|
||||||
{
|
{
|
||||||
arrays[available] = array;
|
return;
|
||||||
_count = available + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bucketLength = GetBucketLength(bucketIndex);
|
||||||
|
if (capacity < bucketLength)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] counts = _counts;
|
||||||
|
int available = counts[bucketIndex];
|
||||||
|
int bucketCapacity = _bucketCapacity;
|
||||||
|
if (available >= bucketCapacity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = (bucketIndex * bucketCapacity) + available;
|
||||||
|
_arrays[offset] = array;
|
||||||
|
counts[bucketIndex] = available + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static int GetBucketIndex(int length)
|
||||||
|
{
|
||||||
|
if (length <= 0 || length > MaxBucketLength)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length <= (1 << MinBucketPower))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int power = 32 - BitOperations.LeadingZeroCount((uint)(length - 1));
|
||||||
|
if (power > MaxBucketPower)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return power - MinBucketPower;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static int GetBucketLength(int bucketIndex) => 1 << (bucketIndex + MinBucketPower);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static int CalculateMaxBucketPower()
|
||||||
|
{
|
||||||
|
int maxLength = Math.Min(int.MaxValue, Array.MaxLength);
|
||||||
|
int leadingZeros = BitOperations.LeadingZeroCount((uint)maxLength);
|
||||||
|
return 31 - leadingZeros;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user