diff --git a/PerfectNumbers.Core/PartialFactorPendingEntry.cs b/PerfectNumbers.Core/PartialFactorPendingEntry.cs new file mode 100644 index 0000000..71d71b5 --- /dev/null +++ b/PerfectNumbers.Core/PartialFactorPendingEntry.cs @@ -0,0 +1,50 @@ +using System.Runtime.CompilerServices; + +namespace PerfectNumbers.Core; + +internal sealed class PartialFactorPendingEntry(ulong value, bool knownComposite) +{ + [ThreadStatic] + private static PartialFactorPendingEntry? _poolHead; + private PartialFactorPendingEntry? _next; + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static PartialFactorPendingEntry Rent(ulong value, bool knownComposite) + { + var poolHead = _poolHead; + if (poolHead == null) + { + return new(value, knownComposite); + } + + _poolHead = poolHead._next; + poolHead.Value = value; + poolHead.KnownComposite = knownComposite; + poolHead.HasKnownPrimality = knownComposite; + poolHead.IsPrime = false; + return poolHead; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static void Return(PartialFactorPendingEntry entry) + { + entry._next = _poolHead; + _poolHead = entry; + } + + public ulong Value = value; + public bool KnownComposite = knownComposite; + public bool HasKnownPrimality = knownComposite; + public bool IsPrime = false; + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public void WithPrimality(bool isPrime) + { + KnownComposite = KnownComposite || !isPrime; + HasKnownPrimality = true; + IsPrime = isPrime; + } +} + + + diff --git a/PerfectNumbers.Core/PartialFactorResult.cs b/PerfectNumbers.Core/PartialFactorResult.cs index c19aefd..f95eace 100644 --- a/PerfectNumbers.Core/PartialFactorResult.cs +++ b/PerfectNumbers.Core/PartialFactorResult.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core { @@ -7,8 +7,8 @@ namespace PerfectNumbers.Core [ThreadStatic] private static PartialFactorResult? s_poolHead; - public ulong[]? Factors; - public int[]? Exponents; + public readonly ulong[] Factors = new ulong[PrimeOrderConstants.GpuSmallPrimeFactorSlots]; + public readonly int[] Exponents = new int[PrimeOrderConstants.GpuSmallPrimeFactorSlots]; private PartialFactorResult? _next; public ulong Cofactor; public bool FullyFactored; @@ -19,7 +19,7 @@ namespace PerfectNumbers.Core { } - public static PartialFactorResult Rent(in ulong[]? factors, in int[]? exponents, ulong cofactor, bool fullyFactored, int count, bool cofactorIsPrime) + public static PartialFactorResult Rent() { PartialFactorResult? instance = s_poolHead; if (instance is null) @@ -32,34 +32,10 @@ namespace PerfectNumbers.Core } instance._next = null; - instance.Factors = factors; - instance.Exponents = exponents; - instance.Cofactor = cofactor; - instance.FullyFactored = fullyFactored; - instance.Count = count; - instance.CofactorIsPrime = cofactorIsPrime; - return instance; - } - - public static PartialFactorResult Rent(ulong cofactor, bool fullyFactored, int count, bool cofactorIsPrime) - { - PartialFactorResult? instance = s_poolHead; - if (instance is null) - { - instance = new PartialFactorResult(); - } - else - { - s_poolHead = instance._next; - } - - instance._next = null; - instance.Factors = null; - instance.Exponents = null; - instance.Cofactor = cofactor; - instance.FullyFactored = fullyFactored; - instance.Count = count; - instance.CofactorIsPrime = cofactorIsPrime; + instance.Cofactor = 0UL; + instance.FullyFactored = false; + instance.Count = 0; + instance.CofactorIsPrime = false; return instance; } @@ -73,59 +49,29 @@ namespace PerfectNumbers.Core { ulong[]? sourceFactors = Factors; int sourceCount = Count; - if (sourceFactors is null || sourceCount == 0) + if (sourceCount == 0) { InitializeWithPrime(prime); return; } - int newSize = (int)BitOperations.RoundUpToPowerOf2((uint)sourceCount + 1U); - ulong[] extendedFactors = sourceFactors.Length >= newSize - ? sourceFactors - : ResizeFactors(sourceFactors, sourceCount, newSize); - int[] sourceExponents = Exponents!; - int[] extendedExponents = sourceExponents.Length >= newSize - ? sourceExponents - : ResizeExponents(sourceCount, newSize, sourceExponents); - newSize = sourceCount + 1; - extendedFactors[sourceCount] = prime; - extendedExponents[sourceCount] = 1; - Array.Sort(extendedFactors, extendedExponents, 0, newSize); + int newSize = sourceCount + 1; + sourceFactors[sourceCount] = prime; + sourceExponents[sourceCount] = 1; + Array.Sort(sourceFactors, sourceExponents, 0, newSize); - Factors = extendedFactors; - Exponents = extendedExponents; Cofactor = 1UL; FullyFactored = true; Count = newSize; CofactorIsPrime = false; } - private static int[] ResizeExponents(int sourceCount, int newSize, in int[] sourceExponents) - { - int[] extendedExponents = FixedCapacityPools.ExclusiveIntArray.Rent(newSize); - Array.Copy(sourceExponents, 0, extendedExponents, 0, sourceCount); - FixedCapacityPools.ExclusiveIntArray.Return(sourceExponents); - return extendedExponents; - } - - private static ulong[] ResizeFactors(in ulong[] sourceFactors, int sourceCount, int newSize) - { - ulong[] extendedFactors = FixedCapacityPools.ExclusiveUlongArray.Rent(newSize); - Array.Copy(sourceFactors, 0, extendedFactors, 0, sourceCount); - FixedCapacityPools.ExclusiveUlongArray.Return(sourceFactors); - return extendedFactors; - } - private void InitializeWithPrime(ulong prime) { - ulong[] factors = FixedCapacityPools.ExclusiveUlongArray.Rent(1); - int[] exponents = FixedCapacityPools.ExclusiveIntArray.Rent(1); - factors[0] = prime; - exponents[0] = 1; - Factors = factors; - Exponents = exponents; + Factors[0] = prime; + Exponents[0] = 1; Cofactor = 1UL; FullyFactored = true; Count = 1; @@ -136,19 +82,6 @@ namespace PerfectNumbers.Core { if (this != Empty) { - - ulong[]? factors = Factors; - - if (factors is not null) - { - int[] exponents = Exponents!; - Factors = null; - Exponents = null; - - FixedCapacityPools.ExclusiveUlongArray.Return(factors); - FixedCapacityPools.ExclusiveIntArray.Return(exponents); - } - _next = s_poolHead; s_poolHead = this; } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 95be940..b9d820e 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -10,50 +10,6 @@ namespace PerfectNumbers.Core; internal static partial class PrimeOrderCalculator { - internal sealed class PendingEntry(ulong value, bool knownComposite) - { - [ThreadStatic] - private static PendingEntry? _poolHead; - private PendingEntry? _next; - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static PendingEntry Rent(ulong value, bool knownComposite) - { - var poolHead = _poolHead; - if (poolHead == null) - { - return new(value, knownComposite); - } - - _poolHead = poolHead._next; - poolHead.Value = value; - poolHead.KnownComposite = knownComposite; - poolHead.HasKnownPrimality = knownComposite; - poolHead.IsPrime = false; - return poolHead; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static void Return(PendingEntry entry) - { - entry._next = _poolHead; - _poolHead = entry; - } - - public ulong Value = value; - public bool KnownComposite = knownComposite; - public bool HasKnownPrimality = knownComposite; - public bool IsPrime = false; - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public void WithPrimality(bool isPrime) - { - KnownComposite = KnownComposite || !isPrime; - HasKnownPrimality = true; - IsPrime = isPrime; - } - } - public static ulong CalculateCpu( ulong prime, ulong? previousOrder, @@ -575,10 +531,10 @@ internal static partial class PrimeOrderCalculator [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) { - ReadOnlySpan factorSpan = factors.Factors; - ReadOnlySpan exponentsSpan = factors.Exponents; - int length = factors.Count; + ReadOnlySpan factorSpan = factors.Factors.AsSpan(0, length); + ReadOnlySpan exponentsSpan = factors.Exponents.AsSpan(0, length); + int newLength = length + 1; ulong[] tempFactorsArray = FixedCapacityPools.ExclusiveUlongArray.Rent(newLength); int[] tempExponentsArray = FixedCapacityPools.ExclusiveIntArray.Rent(newLength); @@ -1343,14 +1299,18 @@ internal static partial class PrimeOrderCalculator } // stackalloc is faster than pooling - Span primeSlots = stackalloc ulong[PrimeOrderConstants.GpuSmallPrimeFactorSlots]; - Span exponentSlots = stackalloc int[PrimeOrderConstants.GpuSmallPrimeFactorSlots]; + + var result = PartialFactorResult.Rent(); + ulong[] factors = result.Factors; + Span primeSlots = new(factors); + int[] exponents = result.Exponents; + Span exponentSlots = new(exponents); // We don't need to worry about leftovers, because we always use indexes within the calculated counts // primeSlots.Clear(); // exponentSlots.Clear(); - List pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(PrimeOrderConstants.GpuSmallPrimeFactorSlots); + List pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(PrimeOrderConstants.GpuSmallPrimeFactorSlots); PopulateSmallPrimeFactorsCpu( value, @@ -1360,7 +1320,6 @@ internal static partial class PrimeOrderCalculator out int factorCount, out ulong remaining); - bool limitReached; if (remaining > 1UL) { if (config.PollardRhoMilliseconds > 0) @@ -1369,13 +1328,13 @@ internal static partial class PrimeOrderCalculator FixedCapacityStack compositeStack = ThreadStaticPools.RentUlongStack(PrimeOrderConstants.GpuSmallPrimeFactorSlots); compositeStack.Push(remaining); - CollectFactorsCpu(primeSlots, exponentSlots, ref factorCount, pending, compositeStack, deadlineTimestamp, out limitReached); + CollectFactorsCpu(primeSlots, exponentSlots, ref factorCount, pending, compositeStack, deadlineTimestamp, out bool limitReached); if (limitReached) { while (compositeStack.Count > 0) { - pending.Add(PendingEntry.Rent(compositeStack.Pop(), knownComposite: false)); + pending.Add(PartialFactorPendingEntry.Rent(compositeStack.Pop(), knownComposite: false)); } } @@ -1384,7 +1343,7 @@ internal static partial class PrimeOrderCalculator } else { - pending.Add(PendingEntry.Rent(remaining, knownComposite: false)); + pending.Add(PartialFactorPendingEntry.Rent(remaining, knownComposite: false)); } } @@ -1399,14 +1358,14 @@ internal static partial class PrimeOrderCalculator for (; index < pendingCount; index++) { - PendingEntry entry = pending[index]; + PartialFactorPendingEntry entry = pending[index]; ulong composite = entry.Value; if (entry.KnownComposite) { cofactor = checked(cofactor * composite); cofactorContainsComposite = true; - PendingEntry.Return(entry); + PartialFactorPendingEntry.Return(entry); continue; } @@ -1429,7 +1388,7 @@ internal static partial class PrimeOrderCalculator cofactorContainsComposite = true; } - PendingEntry.Return(entry); + PartialFactorPendingEntry.Return(entry); } bool cofactorIsPrime; @@ -1446,19 +1405,6 @@ internal static partial class PrimeOrderCalculator cofactorIsPrime = PrimeTesterByLastDigit.IsPrimeCpu(cofactor); } - PartialFactorResult result; - if (factorCount == 0) - { - if (cofactor == value) - { - result = PartialFactorResult.Rent(cofactor, false, 0, cofactorIsPrime); - goto ReturnResult; - } - - result = PartialFactorResult.Rent(cofactor, cofactor == 1UL, 0, cofactorIsPrime); - goto ReturnResult; - } - // This will never happen in production code. We'll always get at least 1 factor // if (factorCount == 0) // { @@ -1472,37 +1418,22 @@ internal static partial class PrimeOrderCalculator // goto ReturnResult; // } - ulong[] factorsArray = FixedCapacityPools.ExclusiveUlongArray.Rent(factorCount); - int[] exponentsArray = FixedCapacityPools.ExclusiveIntArray.Rent(factorCount); - - for (int i = 0; i < factorCount; i++) - { - factorsArray[i] = primeSlots[i]; - exponentsArray[i] = exponentSlots[i]; - - // This will never happen on the execution path from production code - // if (primeValue == 0UL || exponentValue == 0) - // { - // throw new Exception("Prime value or exponent equals zero"); - // continue; - // } - } - - Array.Sort(factorsArray, exponentsArray, 0, factorCount); + Array.Sort(factors, exponents, 0, factorCount); // Check if it was it factored completely - limitReached = cofactor == 1UL; - result = PartialFactorResult.Rent(factorsArray, exponentsArray, cofactor, limitReached, factorCount, cofactorIsPrime); + result.Cofactor = cofactor; + result.FullyFactored = cofactor == 1UL; + result.Count = factorCount; + result.CofactorIsPrime = cofactorIsPrime; - ReturnResult: pending.Clear(); ThreadStaticPools.ReturnPrimeOrderPendingEntryList(pending); return result; } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static void CollectFactorsCpu(Span primeSlots, Span exponentSlots, ref int factorCount, List pending, FixedCapacityStack compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached) + private static void CollectFactorsCpu(Span primeSlots, Span exponentSlots, ref int factorCount, List pending, FixedCapacityStack compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached) { while (compositeStack.Count > 0) { @@ -1518,13 +1449,13 @@ internal static partial class PrimeOrderCalculator pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp; if (pollardRhoDeadlineReached) { - pending.Add(PendingEntry.Rent(composite, knownComposite: true)); + pending.Add(PartialFactorPendingEntry.Rent(composite, knownComposite: true)); continue; } if (!TryPollardRhoCpu(composite, deadlineTimestamp, out ulong factor)) { - pending.Add(PendingEntry.Rent(composite, knownComposite: true)); + pending.Add(PartialFactorPendingEntry.Rent(composite, knownComposite: true)); continue; } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index c2349b6..8aa6a04 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -161,7 +161,7 @@ internal static partial class PrimeOrderCalculator } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static void CollectFactorsGpu(PrimeOrderCalculatorAccelerator gpu, ref Span primeSlots, ref Span exponentSlots, ref ulong[] primeSlotArray, ref int[] exponentSlotArray, ref int factorCount, List pending, Stack compositeStack, long deadlineTimestamp, ref bool pollardRhoDeadlineReached) + private static void CollectFactorsGpu(PrimeOrderCalculatorAccelerator gpu, ref Span primeSlots, ref Span exponentSlots, ref ulong[] primeSlotArray, ref int[] exponentSlotArray, ref int factorCount, List pending, Stack compositeStack, long deadlineTimestamp, ref bool pollardRhoDeadlineReached) { while (compositeStack.Count > 0) { @@ -178,14 +178,14 @@ internal static partial class PrimeOrderCalculator if (currentTimestamp > deadlineTimestamp) { pollardRhoDeadlineReached = true; - pending.Add(new PendingEntry(composite, knownComposite: true)); + pending.Add(new PartialFactorPendingEntry(composite, knownComposite: true)); continue; } // if (!TryPollardRhoGpu(gpu, composite, out ulong factor)) if (!TryPollardRhoCpu(composite, deadlineTimestamp, out ulong factor)) { - pending.Add(new PendingEntry(composite, knownComposite: true)); + pending.Add(new PartialFactorPendingEntry(composite, knownComposite: true)); continue; } @@ -345,10 +345,11 @@ internal static partial class PrimeOrderCalculator } // stackalloc is faster than pooling - Span primeSlots = stackalloc ulong[PrimeOrderConstants.GpuSmallPrimeFactorSlots]; - Span exponentSlots = stackalloc int[PrimeOrderConstants.GpuSmallPrimeFactorSlots]; - ulong[] primeSlotArray = []; - int[] exponentSlotArray = []; + var result = PartialFactorResult.Rent(); + ulong[] factors = result.Factors; + Span primeSlots = new (factors); + int[] exponents = result.Exponents; + Span exponentSlots = new (exponents); // We don't need to worry about leftovers, because we always use indexes within the calculated counts // primeSlots.Clear(); // exponentSlots.Clear(); @@ -358,7 +359,7 @@ internal static partial class PrimeOrderCalculator bool gpuFactored = PrimeOrderGpuHeuristics.TryPartialFactor(gpu, value, limit, primeSlots, exponentSlots, out int factorCount, out remaining); - List pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2); + List pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2); if (!gpuFactored) { @@ -386,7 +387,7 @@ internal static partial class PrimeOrderCalculator { while (compositeStack.Count > 0) { - pending.Add(new PendingEntry(compositeStack.Pop(), knownComposite: false)); + pending.Add(new PartialFactorPendingEntry(compositeStack.Pop(), knownComposite: false)); } } @@ -395,7 +396,7 @@ internal static partial class PrimeOrderCalculator } else { - pending.Add(new PendingEntry(remaining, knownComposite: false)); + pending.Add(new PartialFactorPendingEntry(remaining, knownComposite: false)); } } @@ -405,7 +406,7 @@ internal static partial class PrimeOrderCalculator int index = 0; for (; index < pendingCount; index++) { - PendingEntry entry = pending[index]; + PartialFactorPendingEntry entry = pending[index]; ulong composite = entry.Value; if (entry.KnownComposite) @@ -450,19 +451,6 @@ internal static partial class PrimeOrderCalculator cofactorIsPrime = PrimeTesterByLastDigit.IsPrimeCpu(cofactor); } - PartialFactorResult result; - if (factorCount == 0) - { - if (cofactor == value) - { - result = PartialFactorResult.Rent(cofactor, false, 0, cofactorIsPrime); - goto ReturnResult; - } - - result = PartialFactorResult.Rent(cofactor, cofactor == 1UL, 0, cofactorIsPrime); - goto ReturnResult; - } - // This will never happen in production code. We'll always get at least 1 factor // if (factorCount == 0) // { @@ -476,38 +464,13 @@ internal static partial class PrimeOrderCalculator // goto ReturnResult; // } - ArrayPool ulongPool = ThreadStaticPools.UlongPool; - ArrayPool intPool = ThreadStaticPools.IntPool; - - ulong[] factorsArray = ulongPool.Rent(factorCount); - int[] exponentsArray = intPool.Rent(factorCount); - - for (int i = 0; i < factorCount; i++) - { - factorsArray[i] = primeSlots[i]; - exponentsArray[i] = exponentSlots[i]; - - // This will never happen on the execution path from production code - // if (primeValue == 0UL || exponentValue == 0) - // { - // throw new Exception("Prime value or exponent equals zero"); - // continue; - // } - } - - Array.Sort(factorsArray, exponentsArray, 0, factorCount); + Array.Sort(factors, exponents, 0, factorCount); // Check if it was factored completely - limitReached = cofactor == 1UL; - - result = PartialFactorResult.Rent(factorsArray, exponentsArray, cofactor, limitReached, factorCount, cofactorIsPrime); - - ReturnResult: - if (primeSlotArray.Length > 0) - { - ThreadStaticPools.UlongPool.Return(primeSlotArray, clearArray: false); - ThreadStaticPools.IntPool.Return(exponentSlotArray!, clearArray: false); - } + result.Cofactor = cofactor; + result.FullyFactored = cofactor == 1UL; + result.Count = factorCount; + result.CofactorIsPrime = cofactorIsPrime; pending.Clear(); ThreadStaticPools.ReturnPrimeOrderPendingEntryList(pending); diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Hybrid.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Hybrid.cs index 87f2dae..c401deb 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Hybrid.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Hybrid.cs @@ -83,7 +83,7 @@ internal static partial class PrimeOrderCalculator } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static void CollectFactorsHybrid(PrimeOrderCalculatorAccelerator gpu, Span primeSlots, Span exponentSlots, ref int factorCount, List pending, FixedCapacityStack compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached) + private static void CollectFactorsHybrid(PrimeOrderCalculatorAccelerator gpu, Span primeSlots, Span exponentSlots, ref int factorCount, List pending, FixedCapacityStack compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached) { while (compositeStack.Count > 0) { @@ -99,13 +99,13 @@ internal static partial class PrimeOrderCalculator pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp; if (pollardRhoDeadlineReached) { - pending.Add(new PendingEntry(composite, knownComposite: true)); + pending.Add(new PartialFactorPendingEntry(composite, knownComposite: true)); continue; } if (!TryPollardRhoCpu(composite, deadlineTimestamp, out ulong factor)) { - pending.Add(new PendingEntry(composite, knownComposite: true)); + pending.Add(new PartialFactorPendingEntry(composite, knownComposite: true)); continue; } @@ -142,18 +142,19 @@ internal static partial class PrimeOrderCalculator } // stackalloc is faster than pooling - Span primeSlots = stackalloc ulong[PrimeOrderConstants.GpuSmallPrimeFactorSlots]; - Span exponentSlots = stackalloc int[PrimeOrderConstants.GpuSmallPrimeFactorSlots]; - ulong[] primeSlotArray = []; - int[] exponentSlotArray = []; + + var result = PartialFactorResult.Rent(); + ulong[] factors = result.Factors; + Span primeSlots = new(factors); + int[] exponents = result.Exponents; + Span exponentSlots = new(exponents); // We don't need to worry about leftovers, because we always use indexes within the calculated counts // primeSlots.Clear(); // exponentSlots.Clear(); uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit; - ulong remaining = value; - List pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2); + List pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(2); PopulateSmallPrimeFactorsCpu( value, @@ -161,7 +162,7 @@ internal static partial class PrimeOrderCalculator primeSlots, exponentSlots, out int factorCount, - out remaining); + out ulong remaining); bool limitReached; if (remaining > 1UL) @@ -178,7 +179,7 @@ internal static partial class PrimeOrderCalculator { while (compositeStack.Count > 0) { - pending.Add(new PendingEntry(compositeStack.Pop(), knownComposite: false)); + pending.Add(new PartialFactorPendingEntry(compositeStack.Pop(), knownComposite: false)); } } @@ -187,7 +188,7 @@ internal static partial class PrimeOrderCalculator } else { - pending.Add(new PendingEntry(remaining, knownComposite: false)); + pending.Add(new PartialFactorPendingEntry(remaining, knownComposite: false)); } } @@ -197,7 +198,7 @@ internal static partial class PrimeOrderCalculator int index = 0; for (; index < pendingCount; index++) { - PendingEntry entry = pending[index]; + PartialFactorPendingEntry entry = pending[index]; ulong composite = entry.Value; if (entry.KnownComposite) @@ -242,19 +243,6 @@ internal static partial class PrimeOrderCalculator cofactorIsPrime = RunOnCpu() ? PrimeTesterByLastDigit.IsPrimeCpu(cofactor) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor); } - PartialFactorResult result; - if (factorCount == 0) - { - if (cofactor == value) - { - result = PartialFactorResult.Rent(cofactor, false, 0, cofactorIsPrime); - goto ReturnResult; - } - - result = PartialFactorResult.Rent(cofactor, cofactor == 1UL, 0, cofactorIsPrime); - goto ReturnResult; - } - // This will never happen in production code. We'll always get at least 1 factor // if (factorCount == 0) // { @@ -268,38 +256,12 @@ internal static partial class PrimeOrderCalculator // goto ReturnResult; // } - ArrayPool ulongPool = ThreadStaticPools.UlongPool; - ArrayPool intPool = ThreadStaticPools.IntPool; + Array.Sort(factors, exponents, 0, factorCount); - ulong[] factorsArray = ulongPool.Rent(factorCount); - int[] exponentsArray = intPool.Rent(factorCount); - - for (int i = 0; i < factorCount; i++) - { - factorsArray[i] = primeSlots[i]; - exponentsArray[i] = exponentSlots[i]; - - // This will never happen on the execution path from production code - // if (primeValue == 0UL || exponentValue == 0) - // { - // throw new Exception("Prime value or exponent equals zero"); - // continue; - // } - } - - Array.Sort(factorsArray, exponentsArray, 0, factorCount); - - // Check if it was factored completely - limitReached = cofactor == 1UL; - - result = PartialFactorResult.Rent(factorsArray, exponentsArray, cofactor, limitReached, factorCount, cofactorIsPrime); - - ReturnResult: - if (primeSlotArray.Length > 0) - { - ThreadStaticPools.UlongPool.Return(primeSlotArray, clearArray: false); - ThreadStaticPools.IntPool.Return(exponentSlotArray!, clearArray: false); - } + result.Cofactor = cofactor; + result.FullyFactored = cofactor == 1UL; + result.Count = factorCount; + result.CofactorIsPrime = cofactorIsPrime; pending.Clear(); ThreadStaticPools.ReturnPrimeOrderPendingEntryList(pending); diff --git a/PerfectNumbers.Core/ThreadStaticPools.cs b/PerfectNumbers.Core/ThreadStaticPools.cs index 598eab4..3efe013 100644 --- a/PerfectNumbers.Core/ThreadStaticPools.cs +++ b/PerfectNumbers.Core/ThreadStaticPools.cs @@ -263,12 +263,12 @@ namespace PerfectNumbers.Core public static void Return((ulong, ulong)[] array) => _ulongUlongTupleArrayPool!.Return(array, clearArray: false); [ThreadStatic] - private static FixedCapacityStack>? _primeOrderPendingEntryListPool; + private static FixedCapacityStack>? _primeOrderPendingEntryListPool; [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - internal static List RentPrimeOrderPendingEntryList(int capacityHint) + internal static List RentPrimeOrderPendingEntryList(int capacityHint) { - FixedCapacityStack>? pool = _primeOrderPendingEntryListPool; + FixedCapacityStack>? pool = _primeOrderPendingEntryListPool; if (pool is not null) { if (pool.Pop() is { } list) @@ -286,11 +286,11 @@ namespace PerfectNumbers.Core _primeOrderPendingEntryListPool = new(PerfectNumberConstants.DefaultPoolCapacity); } - return new List(capacityHint); + return new List(capacityHint); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - internal static void ReturnPrimeOrderPendingEntryList(List list) => _primeOrderPendingEntryListPool!.Push(list); + internal static void ReturnPrimeOrderPendingEntryList(List list) => _primeOrderPendingEntryListPool!.Push(list); [ThreadStatic] private static FixedCapacityStack>? _ulongStackPool;