Merge pull request #141 from mlemanczyk/codex/optimize-evenperfectbitscanner-cpu-path

Remove IDisposable from prime order scopes
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-10-10 03:03:58 +02:00
commit 0be9596bd5
7 files changed
+201 -222

No files matched your search

@@ -279,17 +279,17 @@ public class Pow2MontgomeryModCycleComputationBenchmarks
#if DEBUG
Console.WriteLine("Trying heuristic. Prime order calculation");
#endif
PrimeOrderCalculator.PrimeOrderResult orderResult = PrimeOrderCalculator.Calculate(
modulus,
_previousPrimeOrder,
divisorData,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong order = PrimeOrderCalculator.Calculate(
modulus,
_previousPrimeOrder,
divisorData,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
if (orderResult.Order != 0UL)
if (order != 0UL)
{
_previousPrimeOrder = orderResult.Order;
return orderResult.Order;
_previousPrimeOrder = order;
return order;
}
_previousPrimeOrder = null;
@@ -308,15 +308,15 @@ public class Pow2MontgomeryModCycleComputationBenchmarks
#if DEBUG
Console.WriteLine("Trying heuristic. Wide prime order calculation");
#endif
PrimeOrderCalculator.PrimeOrderResultWide orderResult = PrimeOrderCalculator.Calculate(
UInt128 order = PrimeOrderCalculator.Calculate(
modulus,
_previousWidePrimeOrder,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
if (orderResult.Order != UInt128.Zero)
if (order != UInt128.Zero)
{
_previousWidePrimeOrder = orderResult.Order;
return orderResult.Order;
_previousWidePrimeOrder = order;
return order;
}
_previousWidePrimeOrder = null;
@@ -359,10 +359,9 @@ public class PrimeOrderGpuHeuristicsTests
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
PrimeOrderCalculator.PrimeOrderResult result = PrimeOrderCalculator.Calculate(prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong result = PrimeOrderCalculator.Calculate(prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found);
result.Order.Should().Be(3UL);
result.Should().Be(3UL);
}
finally
{
@@ -45,15 +45,14 @@ public class PrimeOrderCalculatorTests
{
ulong expected = ComputeOrderByDoubling(prime);
PrimeOrderCalculator.PrimeOrderResult result = PrimeOrderCalculator.Calculate(
prime,
previousOrder: null,
MontgomeryDivisorData.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong result = PrimeOrderCalculator.Calculate(
prime,
previousOrder: null,
MontgomeryDivisorData.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found);
result.Order.Should().Be(expected);
result.Should().Be(expected);
}
[Theory]
@@ -63,23 +62,21 @@ public class PrimeOrderCalculatorTests
{
ulong previousOrder = ComputeOrderByDoubling(previousPrime);
PrimeOrderCalculator.PrimeOrderResult heuristic = PrimeOrderCalculator.Calculate(
prime,
previousOrder,
MontgomeryDivisorData.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong heuristic = PrimeOrderCalculator.Calculate(
prime,
previousOrder,
MontgomeryDivisorData.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculator.PrimeOrderResult strict = PrimeOrderCalculator.Calculate(
prime,
previousOrder: null,
MontgomeryDivisorData.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong strict = PrimeOrderCalculator.Calculate(
prime,
previousOrder: null,
MontgomeryDivisorData.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
heuristic.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found);
strict.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found);
heuristic.Order.Should().Be(strict.Order);
heuristic.Should().Be(strict);
}
[Theory]
@@ -88,15 +85,14 @@ public class PrimeOrderCalculatorTests
[InlineData(3UL, 2UL)]
public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder)
{
PrimeOrderCalculator.PrimeOrderResult result = PrimeOrderCalculator.Calculate(
prime,
previousOrder: null,
MontgomeryDivisorData.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong result = PrimeOrderCalculator.Calculate(
prime,
previousOrder: null,
MontgomeryDivisorData.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found);
result.Order.Should().Be(expectedOrder);
result.Should().Be(expectedOrder);
}
[Fact]
@@ -109,15 +105,14 @@ public class PrimeOrderCalculatorTests
maxPowChecks: 8,
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
PrimeOrderCalculator.PrimeOrderResult result = PrimeOrderCalculator.Calculate(
prime: 13UL,
previousOrder: null,
MontgomeryDivisorData.FromModulus(13UL),
config,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong result = PrimeOrderCalculator.Calculate(
prime: 13UL,
previousOrder: null,
MontgomeryDivisorData.FromModulus(13UL),
config,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.HeuristicUnresolved);
result.Order.Should().Be(ComputeOrderByDoubling(13UL));
result.Should().Be(ComputeOrderByDoubling(13UL));
}
[Fact]
@@ -131,15 +126,14 @@ public class PrimeOrderCalculatorTests
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(239UL);
PrimeOrderCalculator.PrimeOrderResult heuristic = PrimeOrderCalculator.Calculate(
prime: 239UL,
previousOrder: null,
divisorData,
heuristicConfig,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong heuristic = PrimeOrderCalculator.Calculate(
prime: 239UL,
previousOrder: null,
divisorData,
heuristicConfig,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
heuristic.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.HeuristicUnresolved);
heuristic.Order.Should().Be(ComputeOrderByDoubling(239UL));
heuristic.Should().Be(ComputeOrderByDoubling(239UL));
var strictConfig = new PrimeOrderCalculator.PrimeOrderSearchConfig(
smallFactorLimit: 2,
@@ -147,15 +141,14 @@ public class PrimeOrderCalculatorTests
maxPowChecks: 1,
mode: PrimeOrderCalculator.PrimeOrderMode.Strict);
PrimeOrderCalculator.PrimeOrderResult strict = PrimeOrderCalculator.Calculate(
prime: 239UL,
previousOrder: null,
divisorData,
strictConfig,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong strict = PrimeOrderCalculator.Calculate(
prime: 239UL,
previousOrder: null,
divisorData,
strictConfig,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
strict.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found);
strict.Order.Should().Be(heuristic.Order);
strict.Should().Be(heuristic);
}
[Fact]
@@ -164,13 +157,12 @@ public class PrimeOrderCalculatorTests
{
UInt128 prime = UInt128.Parse("18446744073709641691");
PrimeOrderCalculator.PrimeOrderResultWide result = PrimeOrderCalculator.Calculate(
UInt128 result = PrimeOrderCalculator.Calculate(
prime,
previousOrder: null,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
result.Status.Should().Be(PrimeOrderCalculator.PrimeOrderStatus.Found);
result.Order.Should().Be((UInt128)1229782938247309446UL);
result.Should().Be((UInt128)1229782938247309446UL);
}
[Fact]
@@ -415,9 +415,9 @@ internal static partial class PrimeOrderGpuHeuristics
ulong? previousOrder,
PrimeOrderCalculator.PrimeOrderSearchConfig config,
in MontgomeryDivisorData divisorData,
out PrimeOrderCalculator.PrimeOrderResult result)
out ulong order)
{
result = default;
order = 0UL;
var lease = GpuKernelPool.GetKernel(useGpuOrder: true);
var execution = lease.EnterExecutionScope();
@@ -497,18 +497,17 @@ internal static partial class PrimeOrderGpuHeuristics
throw new InvalidOperationException("GPU Pollard Rho stack overflow; increase HeuristicStackCapacity.");
}
ulong order = 0UL;
resultBuffer.View.CopyToCPU(ref order, 1);
PrimeOrderCalculator.PrimeOrderStatus finalStatus = kernelStatus == PrimeOrderKernelStatus.Found
? PrimeOrderCalculator.PrimeOrderStatus.Found
: PrimeOrderCalculator.PrimeOrderStatus.HeuristicUnresolved;
if (kernelStatus == PrimeOrderKernelStatus.FactoringFailure)
{
order = 0UL;
}
result = new PrimeOrderCalculator.PrimeOrderResult(finalStatus, order);
DisposeResources();
lease.Dispose();
return true;
return order != 0UL;
void DisposeResources()
{
+9 -9
View File
@@ -234,14 +234,14 @@ public class MersenneDivisorCycles
return (UInt128)cycle;
}
PrimeOrderCalculator.PrimeOrderResultWide wideResult = PrimeOrderCalculator.Calculate(
UInt128 wideOrder = PrimeOrderCalculator.Calculate(
divisor,
previousOrder: null,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Cpu);
if (wideResult.Order != UInt128.Zero)
if (wideOrder != UInt128.Zero)
{
return wideResult.Order;
return wideOrder;
}
// Otherwise, find order of 2 mod divisor
@@ -285,15 +285,15 @@ public class MersenneDivisorCycles
return true;
}
PrimeOrderCalculator.PrimeOrderResult orderResult = PrimeOrderCalculator.Calculate(
ulong order = PrimeOrderCalculator.Calculate(
divisor,
previousOrder: null,
divisorData,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Cpu);
if (orderResult.Order != 0UL)
if (order != 0UL)
{
cycleLength = orderResult.Order;
cycleLength = order;
return true;
}
@@ -940,15 +940,15 @@ public class MersenneDivisorCycles
if (PrimeTester.IsPrimeInternal(divisor, CancellationToken.None))
{
PrimeOrderCalculator.PrimeOrderResult orderResult = PrimeOrderCalculator.Calculate(
ulong order = PrimeOrderCalculator.Calculate(
divisor,
previousOrder: null,
divisorData,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Cpu);
if (orderResult.Order != 0UL)
if (order != 0UL)
{
cycleLength = orderResult.Order;
cycleLength = order;
return true;
}
}
@@ -9,23 +9,10 @@ namespace PerfectNumbers.Core;
internal static partial class PrimeOrderCalculator
{
internal readonly struct PrimeOrderResultWide
{
public PrimeOrderResultWide(PrimeOrderStatus status, UInt128 order)
{
Status = status;
Order = order;
}
public PrimeOrderStatus Status { get; }
public UInt128 Order { get; }
}
public static PrimeOrderResultWide Calculate(UInt128 prime, UInt128? previousOrder, PrimeOrderSearchConfig config)
public static UInt128 Calculate(UInt128 prime, UInt128? previousOrder, PrimeOrderSearchConfig config)
=> Calculate(prime, previousOrder, config, PrimeOrderHeuristicDevice.Gpu);
public static PrimeOrderResultWide Calculate(
public static UInt128 Calculate(
UInt128 prime,
UInt128? previousOrder,
PrimeOrderSearchConfig config,
@@ -33,6 +20,7 @@ internal static partial class PrimeOrderCalculator
{
var scope = UsePow2Mode(device);
MontgomeryDivisorData divisorData;
UInt128 result;
if (prime <= ulong.MaxValue)
{
ulong? previous = null;
@@ -51,50 +39,49 @@ internal static partial class PrimeOrderCalculator
ulong prime64 = (ulong)prime;
divisorData = MontgomeryDivisorData.FromModulus(prime64);
PrimeOrderResult result = Calculate(prime64, previous, divisorData, config, device);
scope.Dispose();
return new PrimeOrderResultWide(result.Status, result.Order);
ulong order64 = Calculate(prime64, previous, divisorData, config, device);
result = order64 == 0UL ? UInt128.Zero : (UInt128)order64;
}
else
{
divisorData = default;
result = CalculateWideInternal(prime, previousOrder, divisorData, config);
}
PrimeOrderResultWide resultWide = CalculateWideInternal(prime, previousOrder, divisorData, config);
scope.Dispose();
return resultWide;
scope.Dispose();
return result;
}
private static PrimeOrderResultWide CalculateWideInternal(UInt128 prime, UInt128? previousOrder, in MontgomeryDivisorData divisorData, PrimeOrderSearchConfig config)
private static UInt128 CalculateWideInternal(UInt128 prime, UInt128? previousOrder, in MontgomeryDivisorData divisorData, PrimeOrderSearchConfig config)
{
if (prime <= UInt128.One)
{
return new PrimeOrderResultWide(PrimeOrderStatus.Found, UInt128.One);
return UInt128.One;
}
if (prime == (UInt128)3UL)
{
return new PrimeOrderResultWide(PrimeOrderStatus.Found, (UInt128)2UL);
return (UInt128)2UL;
}
UInt128 phi = prime - UInt128.One;
var debugScope = UseDebugLogging(!IsGpuHeuristicDevice);
PartialFactorResult128 phiFactors = PartialFactorWide(phi, config);
PrimeOrderResultWide result;
PartialFactorResult128 phiFactors = PartialFactorWide(phi, config);
UInt128 result;
if (phiFactors.Factors is null)
{
// HeuristicFailureLog.Record(prime, null, HeuristicFailureReason.PhiPartialFactorizationFailed);
result = FinishStrictlyWide(prime, divisorData, config.Mode);
debugScope.Dispose();
return result;
result = FinishStrictlyWide(prime, divisorData);
}
else
{
result = RunHeuristicPipelineWide(prime, previousOrder, divisorData, config, phi, phiFactors);
}
result = RunHeuristicPipelineWide(prime, previousOrder, divisorData, config, phi, phiFactors);
debugScope.Dispose();
return result;
debugScope.Dispose();
return result;
}
private static PrimeOrderResultWide RunHeuristicPipelineWide(
private static UInt128 RunHeuristicPipelineWide(
UInt128 prime,
UInt128? previousOrder,
in MontgomeryDivisorData divisorData,
@@ -104,7 +91,7 @@ internal static partial class PrimeOrderCalculator
{
if (phiFactors.FullyFactored && TrySpecialMaxWide(phi, prime, divisorData, phiFactors))
{
return new PrimeOrderResultWide(PrimeOrderStatus.Found, phi);
return phi;
}
UInt128 candidateOrder = InitializeStartingOrderWide(prime, phi, divisorData);
@@ -112,30 +99,25 @@ internal static partial class PrimeOrderCalculator
if (TryConfirmOrderWide(prime, candidateOrder, divisorData, config))
{
return new PrimeOrderResultWide(PrimeOrderStatus.Found, candidateOrder);
return candidateOrder;
}
if (config.Mode == PrimeOrderMode.Strict)
{
// HeuristicFailureLog.Record(prime, candidateOrder, HeuristicFailureReason.StrictModeRequested);
return FinishStrictlyWide(prime, divisorData, PrimeOrderMode.Strict);
return FinishStrictlyWide(prime, divisorData);
}
if (TryHeuristicFinishWide(prime, candidateOrder, previousOrder, divisorData, config, out UInt128 order))
{
return new PrimeOrderResultWide(PrimeOrderStatus.Found, order);
return order;
}
// HeuristicFailureLog.Record(prime, candidateOrder, HeuristicFailureReason.HeuristicPipelineAssumedFullOrder);
// The heuristic pipeline could not narrow the order any further; assume the current order is final.
return new PrimeOrderResultWide(PrimeOrderStatus.Found, candidateOrder);
return candidateOrder;
}
private static PrimeOrderResultWide FinishStrictlyWide(UInt128 prime, in MontgomeryDivisorData divisorData, PrimeOrderMode mode)
private static UInt128 FinishStrictlyWide(UInt128 prime, in MontgomeryDivisorData divisorData)
{
UInt128 strictOrder = CalculateByFactorizationWide(prime, divisorData);
PrimeOrderStatus status = mode == PrimeOrderMode.Strict ? PrimeOrderStatus.Found : PrimeOrderStatus.HeuristicUnresolved;
return new PrimeOrderResultWide(status, strictOrder);
return CalculateByFactorizationWide(prime, divisorData);
}
private static bool TrySpecialMaxWide(UInt128 phi, UInt128 prime, in MontgomeryDivisorData divisorData, PartialFactorResult128 factors)
+92 -85
View File
@@ -18,24 +18,7 @@ namespace PerfectNumbers.Core;
internal static partial class PrimeOrderCalculator
{
internal enum PrimeOrderStatus
{
Found,
HeuristicUnresolved,
}
internal readonly struct PrimeOrderResult
{
public PrimeOrderResult(PrimeOrderStatus status, ulong order)
{
Status = status;
Order = order;
}
public PrimeOrderStatus Status { get; }
public ulong Order { get; }
}
internal enum PrimeOrderMode
{
@@ -167,7 +150,14 @@ internal static partial class PrimeOrderCalculator
}
}
public static PrimeOrderResult Calculate(
public static ulong Calculate(
ulong prime,
ulong? previousOrder,
in MontgomeryDivisorData divisorData,
in PrimeOrderSearchConfig config)
=> Calculate(prime, previousOrder, divisorData, config, PrimeOrderHeuristicDevice.Cpu);
public static ulong Calculate(
ulong prime,
ulong? previousOrder,
in MontgomeryDivisorData divisorData,
@@ -175,41 +165,36 @@ internal static partial class PrimeOrderCalculator
PrimeOrderHeuristicDevice device)
{
var scope = UsePow2Mode(device);
PrimeOrderResult result = CalculateInternal(prime, previousOrder, divisorData, config);
scope.Dispose();
return result;
ulong order = CalculateInternal(prime, previousOrder, divisorData, config);
scope.Dispose();
return order;
}
private static PrimeOrderResult CalculateInternal(ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
private static ulong CalculateInternal(ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
{
if (prime <= 3UL)
{
return new PrimeOrderResult(PrimeOrderStatus.Found, prime == 3UL ? 2UL : 1UL);
return prime == 3UL ? 2UL : 1UL;
}
ulong phi = prime - 1UL;
if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out PrimeOrderResult gpuResult))
if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out ulong gpuOrder))
{
return gpuResult;
return gpuOrder;
}
// using var debugScope = UseDebugLogging(!IsGpuHeuristicDevice);
// DebugLog("Partial factoring φ(p)");
using PartialFactorResult phiFactors = PartialFactor(phi, config);
if (phiFactors.Factors is null)
{
// DebugLog("No factors found");
// HeuristicFailureLog.Record(prime, null, HeuristicFailureReason.PhiPartialFactorizationFailed);
return FinishStrictly(prime, divisorData, config.Mode);
return FinishStrictly(prime, divisorData);
}
return RunHeuristicPipeline(prime, previousOrder, config, divisorData, phi, phiFactors);
}
private static PrimeOrderResult RunHeuristicPipeline(
private static ulong RunHeuristicPipeline(
ulong prime,
ulong? previousOrder,
in PrimeOrderSearchConfig config,
@@ -217,44 +202,35 @@ internal static partial class PrimeOrderCalculator
ulong phi,
PartialFactorResult phiFactors)
{
// DebugLog("Trying special max check");
if (phiFactors.FullyFactored && TrySpecialMax(phi, prime, phiFactors, divisorData))
{
return new PrimeOrderResult(PrimeOrderStatus.Found, phi);
return phi;
}
// DebugLog("Initializing starting order");
ulong candidateOrder = InitializeStartingOrder(prime, phi, divisorData);
candidateOrder = ExponentLowering(candidateOrder, prime, phiFactors, divisorData);
// DebugLog("Trying to confirm order");
if (TryConfirmOrder(prime, candidateOrder, divisorData, config))
{
return new PrimeOrderResult(PrimeOrderStatus.Found, candidateOrder);
return candidateOrder;
}
if (config.Mode == PrimeOrderMode.Strict)
{
// HeuristicFailureLog.Record(prime, candidateOrder, HeuristicFailureReason.StrictModeRequested);
return FinishStrictly(prime, divisorData, PrimeOrderMode.Strict);
return FinishStrictly(prime, divisorData);
}
if (TryHeuristicFinish(prime, candidateOrder, previousOrder, divisorData, config, phiFactors, out ulong order))
{
return new PrimeOrderResult(PrimeOrderStatus.Found, order);
return order;
}
// DebugLog("Heuristic unresolved, assuming current order");
// HeuristicFailureLog.Record(prime, candidateOrder, HeuristicFailureReason.HeuristicPipelineAssumedFullOrder);
// The heuristic pipeline could not rule out additional divisors; assume the current order is final.
return new PrimeOrderResult(PrimeOrderStatus.Found, candidateOrder);
return candidateOrder;
}
private static PrimeOrderResult FinishStrictly(ulong prime, in MontgomeryDivisorData divisorData, PrimeOrderMode mode)
private static ulong FinishStrictly(ulong prime, in MontgomeryDivisorData divisorData)
{
ulong strictOrder = CalculateByFactorization(prime, divisorData);
PrimeOrderStatus status = mode == PrimeOrderMode.Strict ? PrimeOrderStatus.Found : PrimeOrderStatus.HeuristicUnresolved;
return new PrimeOrderResult(status, strictOrder);
return CalculateByFactorization(prime, divisorData);
}
private static bool TrySpecialMax(ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData)
@@ -993,7 +969,7 @@ internal static partial class PrimeOrderCalculator
{
if (value <= 1UL)
{
return PartialFactorResult.Empty;
return PartialFactorResult.RentEmpty();
}
const int FactorSlotCount = GpuSmallPrimeFactorSlots;
@@ -1098,12 +1074,12 @@ internal static partial class PrimeOrderCalculator
{
if ((counts is null || counts.Count == 0) && cofactor == value)
{
return new PartialFactorResult(null, value, false, 0);
return PartialFactorResult.Rent(null, value, false, 0);
}
if (counts is null || counts.Count == 0)
{
return new PartialFactorResult(null, cofactor, cofactor == 1UL, 0);
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
}
FactorEntry[] factors = ArrayPool<FactorEntry>.Shared.Rent(counts.Count);
@@ -1118,7 +1094,7 @@ internal static partial class PrimeOrderCalculator
span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
bool fullyFactored = cofactor == 1UL;
return new PartialFactorResult(factors, cofactor, fullyFactored, index, pooled: true);
return PartialFactorResult.Rent(factors, cofactor, fullyFactored, index);
}
int actualCount = 0;
@@ -1134,10 +1110,10 @@ internal static partial class PrimeOrderCalculator
{
if (cofactor == value)
{
return new PartialFactorResult(null, value, false, 0);
return PartialFactorResult.Rent(null, value, false, 0);
}
return new PartialFactorResult(null, cofactor, cofactor == 1UL, 0);
return PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0);
}
FactorEntry[] array = ArrayPool<FactorEntry>.Shared.Rent(actualCount);
@@ -1158,7 +1134,7 @@ internal static partial class PrimeOrderCalculator
Span<FactorEntry> arraySpan = array.AsSpan(0, arrayIndex);
arraySpan.Sort(static (a, b) => a.Value.CompareTo(b.Value));
bool fullyFactoredArray = cofactor == 1UL;
return new PartialFactorResult(array, cofactor, fullyFactoredArray, arrayIndex, pooled: true);
return PartialFactorResult.Rent(array, cofactor, fullyFactoredArray, arrayIndex);
}
finally
{
@@ -1588,58 +1564,89 @@ internal static partial class PrimeOrderCalculator
public readonly int Exponent = exponent;
}
private struct PartialFactorResult : IDisposable
private sealed class PartialFactorResult : IDisposable
{
private FactorEntry[]? _factors;
private readonly bool _pooled;
[ThreadStatic]
private static PartialFactorResult? s_poolHead;
public PartialFactorResult(FactorEntry[]? factors, ulong cofactor, bool fullyFactored, int count, bool pooled = false)
private PartialFactorResult? _next;
private bool _returned;
public FactorEntry[]? Factors;
public ulong Cofactor;
public bool FullyFactored;
public int Count;
private PartialFactorResult()
{
_factors = factors;
Cofactor = cofactor;
FullyFactored = fullyFactored;
Count = count;
_pooled = pooled && factors is not null;
}
public FactorEntry[]? Factors => _factors;
public ulong Cofactor { get; }
public bool FullyFactored { get; }
public int Count { get; }
public static PartialFactorResult Empty => new(null, 1UL, true, 0);
public void Dispose()
public static PartialFactorResult Rent(FactorEntry[]? factors, ulong cofactor, bool fullyFactored, int count)
{
FactorEntry[]? factors = _factors;
if (factors is null || !_pooled)
PartialFactorResult? instance = s_poolHead;
if (instance is null)
{
return;
instance = new PartialFactorResult();
}
else
{
s_poolHead = instance._next;
}
_factors = null;
ArrayPool<FactorEntry>.Shared.Return(factors, clearArray: false);
instance._next = null;
instance._returned = false;
instance.Factors = factors;
instance.Cofactor = cofactor;
instance.FullyFactored = fullyFactored;
instance.Count = count;
return instance;
}
public static PartialFactorResult RentEmpty()
{
return Rent(null, 1UL, true, 0);
}
public PartialFactorResult WithAdditionalPrime(ulong prime)
{
if (_factors is null || Count == 0)
FactorEntry[]? source = Factors;
if (source is null || Count == 0)
{
FactorEntry[] local = ArrayPool<FactorEntry>.Shared.Rent(1);
local[0] = new FactorEntry(prime, 1);
return new PartialFactorResult(local, 1UL, true, 1, pooled: true);
return Rent(local, 1UL, true, 1);
}
FactorEntry[] extended = ArrayPool<FactorEntry>.Shared.Rent(Count + 1);
Array.Copy(_factors, 0, extended, 0, Count);
Array.Copy(source, 0, extended, 0, Count);
extended[Count] = new FactorEntry(prime, 1);
Span<FactorEntry> span = extended.AsSpan(0, Count + 1);
span.Sort(static (a, b) => a.Value.CompareTo(b.Value));
return new PartialFactorResult(extended, 1UL, true, Count + 1, pooled: true);
return Rent(extended, 1UL, true, Count + 1);
}
public void Dispose()
{
if (_returned)
{
return;
}
_returned = true;
FactorEntry[]? factors = Factors;
if (factors is not null)
{
Factors = null;
ArrayPool<FactorEntry>.Shared.Return(factors, clearArray: false);
}
Cofactor = 0UL;
FullyFactored = false;
Count = 0;
_next = s_poolHead;
s_poolHead = this;
}
}
}