Stop using IDisposable on prime order scopes

This commit is contained in:
Marek Lemańczyk committed 2025-10-10 02:57:54 +02:00
1 parent e7ac18ea0b
commit 7aaccb5feb
7 files changed
+161 -174

No files matched your search

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