PrimeOrderCalculator.ExponentLoweringCpu optimized to reuse factors & exponents span instead of its own buffers

This commit is contained in:
mlemanczyk committed 2025-12-15 20:22:03 +01:00
1 parent 6d427c6906
commit b7773adad8
3 files changed
+50 -59

No files matched your search

@@ -11,8 +11,6 @@ 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 ulong[] StackCandidates = new ulong[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3];
public readonly bool[] StackEvaluations = new bool[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3]; public readonly bool[] StackEvaluations = new bool[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3];
private PartialFactorResult? _next; private PartialFactorResult? _next;
+49 -56
View File
@@ -123,7 +123,7 @@ internal static partial class PrimeOrderCalculator
} }
ulong candidateOrder = InitializeStartingOrderCpu(prime, phi, divisorData); ulong candidateOrder = InitializeStartingOrderCpu(prime, phi, divisorData);
candidateOrder = ExponentLoweringCpu(candidateOrder, prime, phiFactors, divisorData); candidateOrder = ExponentLoweringCpu(candidateOrder, phiFactors, divisorData);
if (TryConfirmOrderCpu(prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors)) if (TryConfirmOrderCpu(prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors))
{ {
@@ -542,39 +542,34 @@ internal static partial class PrimeOrderCalculator
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) private static ulong ExponentLoweringCpu(ulong order, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
{ {
int length = factors.Count; int factorCount = factors.Count;
ReadOnlySpan<ulong> factorSpan = new(factors.Factors, 0, length); int newFactorCount = factorCount + 1;
ReadOnlySpan<int> exponentsSpan = new(factors.Exponents, 0, length); Span<ulong> factorsSpan = new(factors.Factors, 0, newFactorCount);
Span<int> exponentsSpan = new(factors.Exponents, 0, newFactorCount);
int newLength = length + 1;
ulong[] tempFactorsArray = factors.TempFactorsArray;
int[] tempExponentsArray = factors.TempExponentsArray;
Span<ulong> ulongBuffer = tempFactorsArray;
factorSpan.CopyTo(ulongBuffer);
Span<int> intBuffer = tempExponentsArray;
exponentsSpan.CopyTo(intBuffer);
if (!factors.FullyFactored && factors.Cofactor > 1UL && factors.CofactorIsPrime) if (!factors.FullyFactored && factors.Cofactor > 1UL && factors.CofactorIsPrime)
{ {
ulongBuffer[length] = factors.Cofactor; factorsSpan[factorCount] = factors.Cofactor;
intBuffer[length] = 1; exponentsSpan[factorCount] = 1;
length = newLength; factors.Count = factorCount = newFactorCount;
factors.Cofactor = 1UL;
factors.FullyFactored = true;
factors.CofactorIsPrime = false;
} }
Array.Sort(tempFactorsArray, tempExponentsArray, 0, length); SortFactorExponentSpans(factorsSpan, exponentsSpan, factorCount);
ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData);
Span<ulong> stackCandidates = new(factors.StackCandidates); Span<ulong> stackCandidates = new(factors.StackCandidates);
Span<bool> stackEvaluations = new(factors.StackEvaluations); Span<bool> stackEvaluations = new(factors.StackEvaluations);
for (int i = 0; i < length; i++) for (int i = 0; i < factorCount; i++)
{ {
ulong primeFactor = ulongBuffer[i]; ulong primeFactor = factorsSpan[i];
int exponent = intBuffer[i]; int exponent = exponentsSpan[i];
// Factor exponents produced by Pollard-Rho and trial division are always positive in the scanner flow. // Factor exponents produced by Pollard-Rho and trial division are always positive in the scanner flow.
// if (exponent <= 0) // if (exponent <= 0)
// { // {
@@ -588,6 +583,29 @@ internal static partial class PrimeOrderCalculator
return order; return order;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void SortFactorExponentSpans(Span<ulong> factorsSpan, Span<int> exponentsSpan, int length)
{
if (length <= 1)
{
return;
}
for (int i = 1; i < length; i++)
{
int current = i;
int previous = current - 1;
while (current > 0 && factorsSpan[current] < factorsSpan[previous])
{
(factorsSpan[current], factorsSpan[previous]) = (factorsSpan[previous], factorsSpan[current]);
(exponentsSpan[current], exponentsSpan[previous]) = (exponentsSpan[previous], exponentsSpan[current]);
current--;
previous--;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveOptimization)]
private static void ProcessExponentLoweringPrime(Span<ulong> candidateBuffer, Span<bool> evaluationBuffer, ref ulong order, ulong primeFactor, int exponent, ref ExponentRemainderStepperCpu stepper) private static void ProcessExponentLoweringPrime(Span<ulong> candidateBuffer, Span<bool> evaluationBuffer, ref ulong order, ulong primeFactor, int exponent, ref ExponentRemainderStepperCpu stepper)
{ {
@@ -696,11 +714,9 @@ internal static partial class PrimeOrderCalculator
int length = factorization.Count; int length = factorization.Count;
const int StackExponentCapacity = 32; const int StackExponentCapacity = 32;
const int ExponentHardLimit = 256;
Span<ulong> stackBuffer = stackalloc ulong[StackExponentCapacity]; Span<ulong> stackBuffer = stackalloc ulong[StackExponentCapacity];
ulong[]? heapCandidateArray = null;
ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData);
bool violates = false; bool violates = false;
@@ -715,24 +731,7 @@ internal static partial class PrimeOrderCalculator
// continue; // continue;
// } // }
if (exponent > ExponentHardLimit) if (ValidateOrderForFactor(stackBuffer[..exponent], primeFactor, exponent, order, ref stepper))
{
throw new InvalidOperationException($"Factor exponent {exponent} exceeds the supported limit of {ExponentHardLimit}.");
}
if (exponent <= StackExponentCapacity)
{
if (ValidateOrderForFactor(stackBuffer[..exponent], primeFactor, exponent, order, ref stepper))
{
violates = true;
break;
}
continue;
}
heapCandidateArray ??= FixedCapacityPools.ExclusiveUlongArray.Rent(ExponentHardLimit);
if (ValidateOrderForFactor(heapCandidateArray.AsSpan(0, exponent), primeFactor, exponent, order, ref stepper))
{ {
violates = true; violates = true;
break; break;
@@ -741,11 +740,6 @@ internal static partial class PrimeOrderCalculator
ThreadStaticPools.ReturnExponentStepperCpu(stepper); ThreadStaticPools.ReturnExponentStepperCpu(stepper);
if (heapCandidateArray is not null)
{
FixedCapacityPools.ExclusiveUlongArray.Return(heapCandidateArray);
}
return !violates; return !violates;
} }
@@ -946,10 +940,10 @@ internal static partial class PrimeOrderCalculator
bool leftIsGe = !hasPrevious || left >= previous; bool leftIsGe = !hasPrevious || left >= previous;
bool rightIsGe = !hasPrevious || right >= previous; bool rightIsGe = !hasPrevious || right >= previous;
int leftPrimary = ComputePrimary(leftGroup, leftIsGe, previousGroup);
int rightPrimary = ComputePrimary(rightGroup, rightIsGe, previousGroup); int rightPrimary = ComputePrimary(rightGroup, rightIsGe, previousGroup);
int compare = leftPrimary.CompareTo(rightPrimary); int compare = ComputePrimary(leftGroup, leftIsGe, previousGroup);
compare = compare.CompareTo(rightPrimary);
if (compare != 0) if (compare != 0)
{ {
return compare; return compare;
@@ -957,15 +951,15 @@ internal static partial class PrimeOrderCalculator
long leftSecondary; long leftSecondary;
long leftTertiary; long leftTertiary;
ulong tempReference;
if (leftGroup == 3) if (leftGroup == 3)
{ {
leftSecondary = -(long)left; leftSecondary = leftTertiary = -(long)left;
leftTertiary = -(long)left;
} }
else else
{ {
ulong leftReference = hasPrevious ? (left > previous ? left - previous : previous - left) : left; tempReference = hasPrevious ? (left > previous ? left - previous : previous - left) : left;
leftSecondary = (long)leftReference; leftSecondary = (long)tempReference;
leftTertiary = (long)left; leftTertiary = (long)left;
} }
@@ -973,13 +967,12 @@ internal static partial class PrimeOrderCalculator
long rightTertiary; long rightTertiary;
if (rightGroup == 3) if (rightGroup == 3)
{ {
rightSecondary = -(long)right; rightSecondary = rightTertiary = -(long)right;
rightTertiary = -(long)right;
} }
else else
{ {
ulong rightReference = hasPrevious ? (right > previous ? right - previous : previous - right) : right; tempReference = hasPrevious ? (right > previous ? right - previous : previous - right) : right;
rightSecondary = (long)rightReference; rightSecondary = (long)tempReference;
rightTertiary = (long)right; rightTertiary = (long)right;
} }
@@ -304,7 +304,7 @@ internal static partial class PrimeOrderCalculator
} }
ulong candidateOrder = InitializeStartingOrderHybrid(gpu, prime, phi, divisorData); ulong candidateOrder = InitializeStartingOrderHybrid(gpu, prime, phi, divisorData);
candidateOrder = ExponentLoweringCpu(candidateOrder, prime, phiFactors, divisorData); candidateOrder = ExponentLoweringCpu(candidateOrder, phiFactors, divisorData);
if (TryConfirmOrderHybrid(gpu, prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors)) if (TryConfirmOrderHybrid(gpu, prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors))
{ {