From 3c80221105f06337b026506a467c34dac29709c1 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Mon, 15 Dec 2025 23:33:26 +0100 Subject: [PATCH] MontgomeryDivisorDataPool.Rent / .Return removed as we no longer pool Montgomery divisor data. UInt128Extensions.MontgomeryDivisorDataPool variables optimized. --- .../Pow2ModBenchmarks.cs | 8 +- .../Pow2MontgomeryModBenchmarks.cs | 2 +- .../Pow2MontgomeryModWindowedBenchmarkData.cs | 2 +- .../Gpu/PrimeOrderGpuHeuristicsTests.cs | 20 +- .../Gpu/ULongExtensionsGpuTests.cs | 4 +- .../MersenneDivisorCyclesGenerateGpuTests.cs | 4 +- .../MersenneDivisorCyclesTests.cs | 2 +- .../MersenneNumberDivisorCpuTesterTests.cs | 16 +- .../MersenneNumberDivisorGpuTesterTests.cs | 34 +-- .../PrimeOrderCalculatorTests.cs | 12 +- .../Cpu/MersenneCpuDivisorScanSession.cs | 16 +- ...MersenneNumberDivisorByDivisorCpuTester.cs | 53 ++--- PerfectNumbers.Core/DivisorCycleCache.cs | 211 +++++------------- .../ExponentRemainderStepperCpu.cs | 2 +- ...MersenneNumberDivisorByDivisorGpuTester.cs | 38 +--- .../Gpu/PrimeOrderGpuHeuristics.cs | 15 +- .../HeuristicCombinedPrimeTester.cs | 2 +- PerfectNumbers.Core/HeuristicPrimeTester.cs | 2 +- PerfectNumbers.Core/MersenneDivisorCycles.cs | 16 +- PerfectNumbers.Core/MontgomeryDivisorData.cs | 52 +++-- .../MontgomeryDivisorDataPool.cs | 86 ------- PerfectNumbers.Core/PartialFactorResult.cs | 17 +- .../PrimeOrderCalculator.Cpu.cs | 25 +-- .../PrimeOrderCalculator.Gpu.cs | 32 ++- .../PrimeOrderCalculator.Hybrid.cs | 16 +- PerfectNumbers.Core/ThreadStaticPools.cs | 9 - PerfectNumbers.Core/UInt128Extensions.cs | 40 ++-- 27 files changed, 260 insertions(+), 476 deletions(-) delete mode 100644 PerfectNumbers.Core/MontgomeryDivisorDataPool.cs diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2ModBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Pow2ModBenchmarks.cs index 772a025..5ecb650 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2ModBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2ModBenchmarks.cs @@ -500,7 +500,7 @@ public class Pow2ModBenchmarks Console.WriteLine($"Calculating small cycle {smallModulus}"); smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); smallExponents[i] = NextSmallExponent(random); - smallCycles[i] = MersenneDivisorCycles.CalculateCycleLengthCpu(smallModulus, MontgomeryDivisorDataPool.Shared.FromModulus(smallModulus)); + smallCycles[i] = MersenneDivisorCycles.CalculateCycleLengthCpu(smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); (ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(random); Console.WriteLine($"Calculating large cycle {largeModulus}"); @@ -613,7 +613,7 @@ public class Pow2ModBenchmarks private static ulong CalculateCycleLengthWithHeuristicsCpu(ulong modulus, ref ulong? previousPrimeOrder) { - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(modulus); if (modulus <= 1UL || (modulus & 1UL) == 0UL) { return MersenneDivisorCycles.CalculateCycleLengthCpu(modulus, divisorData); @@ -680,7 +680,7 @@ public class Pow2ModBenchmarks private ulong CalculateCycleLengthWithHeuristicsCpu(ulong modulus, ulong fallbackOrder = 0UL) { - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(modulus); if (modulus <= 1UL || (modulus & 1UL) == 0UL) { return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLengthCpu(modulus, divisorData); @@ -709,7 +709,7 @@ public class Pow2ModBenchmarks private ulong CalculateCycleLengthWithHeuristicsGpu(PrimeOrderCalculatorAccelerator gpu, ulong modulus, ulong fallbackOrder = 0UL) { - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(modulus); if (modulus <= 1UL || (modulus & 1UL) == 0UL) { return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, modulus, divisorData); diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs index 19e72d5..b1557c8 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs @@ -39,7 +39,7 @@ public class Pow2MontgomeryModBenchmarks ulong smallModulus = NextSmallOddModulus(); _smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); _smallExponents[i] = NextSmallExponent(); - _smallCycles[i] = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, smallModulus, MontgomeryDivisorDataPool.Shared.FromModulus(smallModulus)); + _smallCycles[i] = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); Console.WriteLine($"Calculating large cycle {i + 1} with heuristics"); (ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(); diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModWindowedBenchmarkData.cs b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModWindowedBenchmarkData.cs index fbd9045..8996a74 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModWindowedBenchmarkData.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModWindowedBenchmarkData.cs @@ -66,7 +66,7 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider { Pow2MontgomeryModWindowedBenchmarkSeed seed = seeds[i]; ulong modulus = 2UL * seed.Multiplier * seed.Exponent + 1UL; - MontgomeryDivisorData divisor = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); + MontgomeryDivisorData divisor = MontgomeryDivisorData.FromModulus(modulus); ulong cycleLength = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, modulus, divisor); ulong reducedExponent = seed.Exponent % cycleLength; cases[i] = new Pow2MontgomeryModWindowedBenchmarkCase( diff --git a/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs b/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs index 453ebf2..a9ebc5d 100644 --- a/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs +++ b/PerfectNumbers.Core.Tests/Gpu/PrimeOrderGpuHeuristicsTests.cs @@ -5,7 +5,7 @@ using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu.Accelerators; using Xunit; -namespace PerfectNumbers.Core.Tests; +namespace PerfectNumbers.Core.Tests.Gpu; [Trait("Category", "Fast")] public class PrimeOrderGpuHeuristicsTests @@ -25,7 +25,7 @@ public class PrimeOrderGpuHeuristicsTests try { PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 1UL, prime, out ulong remainder, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); @@ -43,7 +43,7 @@ public class PrimeOrderGpuHeuristicsTests { const ulong exponent = 138_000_001UL; const ulong prime = 138_000_001UL; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); var gpu = PrimeOrderCalculatorAccelerator.Rent(1); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out ulong remainder, divisorData); @@ -62,7 +62,7 @@ public class PrimeOrderGpuHeuristicsTests var gpu = PrimeOrderCalculatorAccelerator.Rent(1); try { - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 1UL, prime, out ulong remainder, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); @@ -85,7 +85,7 @@ public class PrimeOrderGpuHeuristicsTests try { const ulong prime = 193UL; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 16UL, prime, out ulong remainder, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); @@ -113,7 +113,7 @@ public class PrimeOrderGpuHeuristicsTests ulong[] remainders = new ulong[2]; ulong[] exponents = { 1UL, 2UL }; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); @@ -133,7 +133,7 @@ public class PrimeOrderGpuHeuristicsTests ulong[] remainders = { 123UL, 456UL }; ulong[] exponents = { 138_000_001UL, 138_000_011UL }; const ulong prime = 138_000_001UL; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); var gpu = PrimeOrderCalculatorAccelerator.Rent(1); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); @@ -162,7 +162,7 @@ public class PrimeOrderGpuHeuristicsTests ulong[] exponents = { 1UL, 2UL }; ulong[] remainders = { 5UL, 6UL }; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); @@ -189,7 +189,7 @@ public class PrimeOrderGpuHeuristicsTests ulong[] remainders = { 9UL, 11UL }; const ulong prime = 193UL; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData); status.Should().Be(GpuPow2ModStatus.Overflow); @@ -397,7 +397,7 @@ public class PrimeOrderGpuHeuristicsTests { PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime); ulong result = PrimeOrderCalculator.CalculateGpu(gpu, prime, null, divisorData, PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); result.Should().Be(3UL); diff --git a/PerfectNumbers.Core.Tests/Gpu/ULongExtensionsGpuTests.cs b/PerfectNumbers.Core.Tests/Gpu/ULongExtensionsGpuTests.cs index a52d4cb..02f269a 100644 --- a/PerfectNumbers.Core.Tests/Gpu/ULongExtensionsGpuTests.cs +++ b/PerfectNumbers.Core.Tests/Gpu/ULongExtensionsGpuTests.cs @@ -18,7 +18,7 @@ public class ULongExtensionsGpuTests [InlineData(65535UL, 2147483647UL)] public void Pow2ModWindowedGpu_matches_montgomery_cpu_for_known_values(ulong exponent, ulong modulus) { - MontgomeryDivisorData divisor = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); + MontgomeryDivisorData divisor = MontgomeryDivisorData.FromModulus(modulus); ulong expected = exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisor); exponent.Pow2ModWindowedGpu(modulus).Should().Be(expected); @@ -52,7 +52,7 @@ public class ULongExtensionsGpuTests foreach (ulong modulus in moduli) { - MontgomeryDivisorData divisor = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); + MontgomeryDivisorData divisor = MontgomeryDivisorData.FromModulus(modulus); foreach (ulong exponent in exponents) { ulong expected = exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisor); diff --git a/PerfectNumbers.Core.Tests/MersenneDivisorCyclesGenerateGpuTests.cs b/PerfectNumbers.Core.Tests/MersenneDivisorCyclesGenerateGpuTests.cs index 125a4b1..64a7f03 100644 --- a/PerfectNumbers.Core.Tests/MersenneDivisorCyclesGenerateGpuTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneDivisorCyclesGenerateGpuTests.cs @@ -40,7 +40,7 @@ public class MersenneDivisorCyclesGenerateGpuTests foreach (var pair in pairs) { expected.Should().Contain(pair.divisor); - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor); pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLengthCpu(pair.divisor, divisorData)); } } @@ -90,7 +90,7 @@ public class MersenneDivisorCyclesGenerateGpuTests foreach (var pair in pairs) { uniqueDivisors.Add(pair.divisor).Should().BeTrue(); - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor); pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLengthCpu(pair.divisor, divisorData)); } diff --git a/PerfectNumbers.Core.Tests/MersenneDivisorCyclesTests.cs b/PerfectNumbers.Core.Tests/MersenneDivisorCyclesTests.cs index d2e2d23..b288fea 100644 --- a/PerfectNumbers.Core.Tests/MersenneDivisorCyclesTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneDivisorCyclesTests.cs @@ -22,7 +22,7 @@ public class MersenneDivisorCyclesTests var gpu = PrimeOrderCalculatorAccelerator.Rent(1); try { - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData).Should().Be(expected); MersenneDivisorCycles.CalculateCycleLengthGpu(divisor).Should().Be(expected); } diff --git a/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs index b84444b..747fa1b 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs @@ -49,17 +49,17 @@ public class MersenneNumberDivisorCpuTesterTests { ulong[] primes = [5UL, 7UL, 11UL, 13UL]; - ulong cycle23 = MersenneDivisorCycles.CalculateCycleLengthCpu(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL)); - session.CheckDivisor(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL), cycle23, primes).Should().BeTrue(); + ulong cycle23 = MersenneDivisorCycles.CalculateCycleLengthCpu(23UL, MontgomeryDivisorData.FromModulus(23UL)); + session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primes).Should().BeTrue(); ulong[] primesWithoutHit = [5UL, 7UL, 13UL]; - session.CheckDivisor(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL), cycle23, primesWithoutHit).Should().BeFalse(); + session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primesWithoutHit).Should().BeFalse(); - ulong cycle31 = MersenneDivisorCycles.CalculateCycleLengthCpu(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL)); - session.CheckDivisor(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL), cycle31, primes).Should().BeTrue(); + ulong cycle31 = MersenneDivisorCycles.CalculateCycleLengthCpu(31UL, MontgomeryDivisorData.FromModulus(31UL)); + session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes).Should().BeTrue(); ulong[] primesBeforeHit = [7UL, 11UL, 13UL]; - session.CheckDivisor(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL), cycle31, primesBeforeHit).Should().BeFalse(); + session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primesBeforeHit).Should().BeFalse(); } finally { @@ -81,7 +81,7 @@ public class MersenneNumberDivisorCpuTesterTests { ulong[] exponents = [6UL, 7UL, 9UL, 10UL, 12UL]; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL); ulong cycle = MersenneDivisorCycles.CalculateCycleLengthCpu(7UL, divisorData); session.CheckDivisor(7UL, divisorData, cycle, exponents).Should().BeTrue(); @@ -108,7 +108,7 @@ public class MersenneNumberDivisorCpuTesterTests { ulong[] exponents = [10UL, 11UL, 20UL, 21UL, 30UL]; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL); ulong cycle = MersenneDivisorCycles.CalculateCycleLengthCpu(11UL, divisorData); session.CheckDivisor(11UL, divisorData, cycle, exponents).Should().BeTrue(); diff --git a/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs index fa887ab..3cc6a94 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs @@ -231,10 +231,10 @@ public class MersenneNumberDivisorGpuTesterTests { ulong[] primes = [31UL, 37UL, 41UL]; - session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), 0UL, primes).Should().BeTrue(); + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), 0UL, primes).Should().BeTrue(); ulong[] primesWithoutHit = [31UL, 41UL]; - session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), 0UL, primesWithoutHit).Should().BeFalse(); + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), 0UL, primesWithoutHit).Should().BeFalse(); } finally { @@ -256,17 +256,17 @@ public class MersenneNumberDivisorGpuTesterTests { ulong[] primes = [31UL, 37UL, 41UL, 43UL]; - ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL)); - session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes).Should().BeTrue(); + ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL)); + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue(); ulong[] primesBeforeHit = [31UL]; - session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse(); + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse(); - ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL)); - session.CheckDivisor(13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL), cycle13367, primes).Should().BeTrue(); + ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 13367UL, MontgomeryDivisorData.FromModulus(13367UL)); + session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primes).Should().BeTrue(); ulong[] primesWithoutMatchingExponent = [31UL, 37UL, 43UL]; - session.CheckDivisor(13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL), cycle13367, primesWithoutMatchingExponent).Should().BeFalse(); + session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primesWithoutMatchingExponent).Should().BeFalse(); } finally { @@ -292,11 +292,11 @@ public class MersenneNumberDivisorGpuTesterTests { ulong[] primes = [31UL, 37UL, 41UL, 43UL, 47UL]; - ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL)); - session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes).Should().BeTrue(); + ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL)); + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue(); ulong[] primesBeforeHit = [31UL]; - session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse(); + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse(); } finally { @@ -321,7 +321,7 @@ public class MersenneNumberDivisorGpuTesterTests { ulong[] exponents = [6UL, 7UL, 9UL, 10UL, 12UL]; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL); ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 7UL, divisorData); session.CheckDivisor(7UL, divisorData, cycle, exponents).Should().BeTrue(); @@ -352,7 +352,7 @@ public class MersenneNumberDivisorGpuTesterTests { ulong[] exponents = [10UL, 11UL, 20UL, 21UL, 30UL]; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL); ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 11UL, divisorData); session.CheckDivisor(11UL, divisorData, cycle, exponents).Should().BeTrue(); @@ -384,7 +384,7 @@ public class MersenneNumberDivisorGpuTesterTests var patchedTable = new List<(ulong Divisor, ulong Cycle)>(); ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1]; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(191UL); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(191UL); patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 191UL, divisorData); tableField.SetValue(cycles, patchedTable); @@ -428,11 +428,11 @@ public class MersenneNumberDivisorGpuTesterTests { ulong[] primes = [31UL, 37UL, 41UL, 43UL, 47UL]; - ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL)); - session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes).Should().BeTrue(); + ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL)); + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue(); ulong[] primesBeforeHit = [31UL]; - session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse(); + session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse(); } finally { diff --git a/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs b/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs index 37e5601..1f6ef99 100644 --- a/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs +++ b/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs @@ -49,7 +49,7 @@ public class PrimeOrderCalculatorTests gpu, prime, previousOrder: null, - MontgomeryDivisorDataPool.Shared.FromModulus(prime), + MontgomeryDivisorData.FromModulus(prime), PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); PrimeOrderCalculatorAccelerator.Return(gpu); @@ -69,14 +69,14 @@ public class PrimeOrderCalculatorTests gpu, prime, previousOrder, - MontgomeryDivisorDataPool.Shared.FromModulus(prime), + MontgomeryDivisorData.FromModulus(prime), PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); ulong strict = PrimeOrderCalculator.CalculateGpu( gpu, prime, previousOrder: null, - MontgomeryDivisorDataPool.Shared.FromModulus(prime), + MontgomeryDivisorData.FromModulus(prime), PrimeOrderCalculator.PrimeOrderCalculatorConfig.StrictDefault); PrimeOrderCalculatorAccelerator.Return(gpu); @@ -95,7 +95,7 @@ public class PrimeOrderCalculatorTests gpu, prime, previousOrder: null, - MontgomeryDivisorDataPool.Shared.FromModulus(prime), + MontgomeryDivisorData.FromModulus(prime), PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); PrimeOrderCalculatorAccelerator.Return(gpu); @@ -117,7 +117,7 @@ public class PrimeOrderCalculatorTests gpu, prime: 13UL, previousOrder: null, - MontgomeryDivisorDataPool.Shared.FromModulus(13UL), + MontgomeryDivisorData.FromModulus(13UL), config); PrimeOrderCalculatorAccelerator.Return(gpu); @@ -137,7 +137,7 @@ public class PrimeOrderCalculatorTests var gpu = PrimeOrderCalculatorAccelerator.Rent(1); try { - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(239UL); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(239UL); ulong heuristic = PrimeOrderCalculator.CalculateGpu( gpu, prime: 239UL, diff --git a/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs b/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs index c1cc3e7..4dfed17 100644 --- a/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs +++ b/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs @@ -5,20 +5,20 @@ namespace PerfectNumbers.Core.Cpu; internal sealed class MersenneCpuDivisorScanSession(PrimeOrderCalculatorAccelerator gpu, ComputationDevice orderDevice) : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession { - private Func _getCycleLength = orderDevice switch + private Func _getCycleLength = orderDevice switch { - ComputationDevice.Gpu => divisor => DivisorCycleCache.Shared.GetCycleLengthGpu(gpu, divisor), - ComputationDevice.Hybrid => divisor => DivisorCycleCache.Shared.GetCycleLengthHybrid(gpu, divisor), - _ => static (divisor) => DivisorCycleCache.Shared.GetCycleLengthCpu(divisor), + ComputationDevice.Gpu => (divisor, divisorData) => DivisorCycleCache.Shared.GetCycleLengthGpu(gpu, divisor, divisorData), + ComputationDevice.Hybrid => (divisor, divisorData) => DivisorCycleCache.Shared.GetCycleLengthHybrid(gpu, divisor, divisorData), + _ => static (divisor, divisorData) => DivisorCycleCache.Shared.GetCycleLengthCpu(divisor, divisorData), }; public void Configure(PrimeOrderCalculatorAccelerator gpu, ComputationDevice orderDevice) { _getCycleLength = orderDevice switch { - ComputationDevice.Gpu => divisor => DivisorCycleCache.Shared.GetCycleLengthGpu(gpu, divisor), - ComputationDevice.Hybrid => divisor => DivisorCycleCache.Shared.GetCycleLengthHybrid(gpu, divisor), - _ => static (divisor) => DivisorCycleCache.Shared.GetCycleLengthCpu(divisor), + ComputationDevice.Gpu => (divisor, divisorData) => DivisorCycleCache.Shared.GetCycleLengthGpu(gpu, divisor, divisorData), + ComputationDevice.Hybrid => (divisor, divisorData) => DivisorCycleCache.Shared.GetCycleLengthHybrid(gpu, divisor, divisorData), + _ => static (divisor, divisorData) => DivisorCycleCache.Shared.GetCycleLengthCpu(divisor, divisorData), }; } @@ -45,7 +45,7 @@ internal sealed class MersenneCpuDivisorScanSession(PrimeOrderCalculatorAccelera if (divisorCycle == 0UL) { - divisorCycle = _getCycleLength(divisor); + divisorCycle = _getCycleLength(divisor, divisorData); // This never kicks off in production code // if (divisorCycle == 0UL) // { diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs index 81fe6e2..286c3ef 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs @@ -6,18 +6,29 @@ using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core.Cpu; -public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester +public sealed class MersenneNumberDivisorByDivisorCpuTester(ComputationDevice orderDevice = ComputationDevice.Hybrid) : IMersenneNumberDivisorByDivisorTester { private BigInteger _divisorLimit; private int _batchSize = 1_024; private BigInteger _minK = BigInteger.One; - private readonly ComputationDevice _orderDevice; - private readonly TryCycleLengthDelegate _tryCalculateCycleLengthForExponent; - private readonly CalculateCycleLengthDelegate _calculateCycleLength; + private readonly ComputationDevice OrderDevice = orderDevice; + private readonly TryCycleLengthDelegate _tryCalculateCycleLengthForExponent = orderDevice switch + { + ComputationDevice.Gpu => TryCalculateCycleLengthForExponentGpu, + ComputationDevice.Hybrid => TryCalculateCycleLengthForExponentHybrid, + _ => TryCalculateCycleLengthForExponentCpu, + }; + + private readonly CalculateCycleLengthDelegate _calculateCycleLength = orderDevice switch + { + ComputationDevice.Gpu => CalculateCycleLengthGpu, + ComputationDevice.Hybrid => CalculateCycleLengthHybrid, + _ => CalculateCycleLengthCpu, + }; + private string? _stateFilePath; private int _stateCounter; private BigInteger _lastSavedK; - public ComputationDevice OrderDevice => _orderDevice; private delegate bool TryCycleLengthDelegate( ulong divisor, @@ -43,24 +54,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv public GpuUInt128 Limit; } - public MersenneNumberDivisorByDivisorCpuTester(ComputationDevice orderDevice = ComputationDevice.Hybrid) - { - _orderDevice = orderDevice; - _tryCalculateCycleLengthForExponent = orderDevice switch - { - ComputationDevice.Gpu => TryCalculateCycleLengthForExponentGpu, - ComputationDevice.Hybrid => TryCalculateCycleLengthForExponentHybrid, - _ => TryCalculateCycleLengthForExponentCpu, - }; - - _calculateCycleLength = orderDevice switch - { - ComputationDevice.Gpu => CalculateCycleLengthGpu, - ComputationDevice.Hybrid => CalculateCycleLengthHybrid, - _ => CalculateCycleLengthCpu, - }; - } - public int BatchSize { get => _batchSize; @@ -157,7 +150,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu) => ThreadStaticPools.RentMersenneCpuDivisorSession(gpu, _orderDevice); + public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu) => ThreadStaticPools.RentMersenneCpuDivisorSession(gpu, OrderDevice); private bool CheckDivisors( PrimeOrderCalculatorAccelerator gpu, @@ -340,13 +333,12 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv ulong divisorHigh = divisor.High; var residueStepper = new MersenneDivisorResidueStepper(prime, step, divisor); - var divisorPool = MontgomeryDivisorDataPool.Shared; while (divisor.CompareTo(limit) <= 0) { if (residueStepper.IsAdmissible()) { ulong candidate = divisor.Low; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(candidate); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(candidate); ulong divisorCycle; // Divisors generated from 2 * k * p + 1 exceed the small-cycle snapshot when p >= 138,000,000, so the short path below never runs. if (!_tryCalculateCycleLengthForExponent( @@ -370,7 +362,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv divisorCycle = computedCycle; } - divisorPool.Return(divisorData); RecordState(currentK); if (divisorCycle == prime) { @@ -553,12 +544,11 @@ private bool CheckDivisors64( out ulong foundDivisor) { bool canAdvance = step <= limit; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; if (!canAdvance) { if (divisor <= limit && residueStepper.IsAdmissible()) { - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); ulong divisorCycle; if (!_tryCalculateCycleLengthForExponent( divisor, @@ -579,8 +569,6 @@ private bool CheckDivisors64( divisorCycle = computedCycle; } - divisorPool.Return(divisorData); - if (divisorCycle == prime) { foundDivisor = divisor; @@ -611,7 +599,7 @@ private bool CheckDivisors64( { if (residueStepper.IsAdmissible()) { - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); ulong divisorCycle; if (!_tryCalculateCycleLengthForExponent( divisor, @@ -632,7 +620,6 @@ private bool CheckDivisors64( divisorCycle = computedCycle; } - divisorPool.Return(divisorData); RecordState(currentK); if (divisorCycle == prime) { diff --git a/PerfectNumbers.Core/DivisorCycleCache.cs b/PerfectNumbers.Core/DivisorCycleCache.cs index 0ea54b2..f7a0c98 100644 --- a/PerfectNumbers.Core/DivisorCycleCache.cs +++ b/PerfectNumbers.Core/DivisorCycleCache.cs @@ -19,22 +19,18 @@ public sealed class DivisorCycleCache private static int _sharedDivisorCyclesBatchSize = GpuConstants.GpuCycleStepsPerInvocation; private const int CycleCacheTrackingLimit = 500_000; - private static readonly HashSet CycleCacheTrackedDivisors = new(); + private static readonly HashSet CycleCacheTrackedDivisors = []; private static readonly object CycleCacheTrackingLock = new(); - private static ulong CycleCacheHitCount; - private static bool CycleCacheTrackingDisabled; public static void SetDivisorCyclesBatchSize(int divisorCyclesBatchSize) { _sharedDivisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize); } - public static DivisorCycleCache Shared { get; } = new DivisorCycleCache(_sharedDivisorCyclesBatchSize); + public static readonly DivisorCycleCache Shared = new(_sharedDivisorCyclesBatchSize); public int PreferredBatchSize => _divisorCyclesBatchSize; - // private static readonly Accelerator[] _accelerators; - private DivisorCycleCache(int divisorCyclesBatchSize) { _divisorCyclesBatchSize = Math.Max(1, divisorCyclesBatchSize); @@ -51,54 +47,28 @@ public sealed class DivisorCycleCache _snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); } - public ulong GetCycleLengthCpu(ulong divisor, bool skipPrimeOrderHeuristic = false) + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public ulong GetCycleLengthCpu(ulong divisor, in MontgomeryDivisorData divisorData) { - if (!skipPrimeOrderHeuristic) - { - // TODO: Implement array-free version of GetCycleLengthsCpu so that we don't pay the array costs for single checks. - Span result = stackalloc ulong[1]; - ReadOnlySpan singleDivisor = stackalloc ulong[1] { divisor }; - GetCycleLengthsCpu(singleDivisor, result); - return result[0]; - } - - // The skipPrimeOrderHeuristic path is only used for divisors produced by the CPU by-divisor scanner, - // which always generates values greater than one. Keep this guard disabled here to avoid repeating the - // validation already performed by the general GetCycleLengths path. - // if (divisor <= 1UL) - // { - // throw new InvalidDataException("Divisor must be > 1"); - // } - ulong[] snapshot = _snapshot; - ulong cycleLength; + if (divisor < (ulong)snapshot.Length) { - cycleLength = snapshot[divisor]; - if (cycleLength == 0UL) - { - throw new InvalidDataException($"Divisor cycle is missing for {divisor}"); - } + ulong cached = snapshot[divisor]; + ArgumentOutOfRangeException.ThrowIfZero(cached); - return cycleLength; + return cached; } - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); - cycleLength = MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData, skipPrimeOrderHeuristic: true); - divisorPool.Return(divisorData); - return cycleLength; + return MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData); } - public ulong GetCycleLengthHybrid(PrimeOrderCalculatorAccelerator gpu, ulong divisor, bool skipPrimeOrderHeuristic = false) + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public ulong GetCycleLengthHybrid(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false) { if (!skipPrimeOrderHeuristic) { - // TODO: Implement array-free version of GetCycleLengthsHybrid so that we don't pay the array costs for single checks. - Span result = stackalloc ulong[1]; - ReadOnlySpan singleDivisor = stackalloc ulong[1] { divisor }; - GetCycleLengthsHybrid(gpu, singleDivisor, result); - return result[0]; + return GetCycleLengthHybrid(gpu, divisor, divisorData); } // The skipPrimeOrderHeuristic path is only used for divisors produced by the CPU by-divisor scanner, @@ -114,22 +84,17 @@ public sealed class DivisorCycleCache if (divisor < (ulong)snapshot.Length) { cycleLength = snapshot[divisor]; - if (cycleLength == 0UL) - { - throw new InvalidDataException($"Divisor cycle is missing for {divisor}"); - } + ArgumentOutOfRangeException.ThrowIfZero(cycleLength); return cycleLength; } - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); cycleLength = MersenneDivisorCycles.CalculateCycleLengthHybrid(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true); - divisorPool.Return(divisorData); return cycleLength; } - public ulong GetCycleLengthGpu(PrimeOrderCalculatorAccelerator gpu, ulong divisor, bool skipPrimeOrderHeuristic = false) + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public ulong GetCycleLengthGpu(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false) { if (!skipPrimeOrderHeuristic) { @@ -152,18 +117,12 @@ public sealed class DivisorCycleCache if (divisor < (ulong)snapshot.Length) { cycleLength = snapshot[divisor]; - if (cycleLength == 0UL) - { - throw new InvalidDataException($"Divisor cycle is missing for {divisor}"); - } + ArgumentOutOfRangeException.ThrowIfZero(cycleLength); return cycleLength; } - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); cycleLength = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true); - divisorPool.Return(divisorData); return cycleLength; } @@ -181,18 +140,11 @@ public sealed class DivisorCycleCache for (int i = 0; i < length; i++) { ulong divisor = divisors[i]; - if (divisor <= 1UL) - { - throw new InvalidDataException("Divisor must be > 1"); - } if (divisor < (ulong)snapshot.Length) { ulong cached = snapshot[divisor]; - if (cached == 0UL) - { - throw new InvalidDataException($"Divisor cycle is missing for {divisor}"); - } + ArgumentOutOfRangeException.ThrowIfZero(cached); cycles[i] = cached; } @@ -214,6 +166,22 @@ public sealed class DivisorCycleCache } } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public ulong GetCycleLengthHybrid(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData) + { + ulong[] snapshot = _snapshot; + + if (divisor < (ulong)snapshot.Length) + { + ulong cached = snapshot[divisor]; + ArgumentOutOfRangeException.ThrowIfZero(cached); + + return cached; + } + + return MersenneDivisorCycles.CalculateCycleLengthHybrid(gpu, divisor, divisorData); + } + public void GetCycleLengthsHybrid(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan divisors, Span cycles) { ulong[] snapshot = _snapshot; @@ -228,18 +196,11 @@ public sealed class DivisorCycleCache for (int i = 0; i < length; i++) { ulong divisor = divisors[i]; - if (divisor <= 1UL) - { - throw new InvalidDataException("Divisor must be > 1"); - } if (divisor < (ulong)snapshot.Length) { ulong cached = snapshot[divisor]; - if (cached == 0UL) - { - throw new InvalidDataException($"Divisor cycle is missing for {divisor}"); - } + ArgumentOutOfRangeException.ThrowIfZero(cached); cycles[i] = cached; } @@ -275,18 +236,11 @@ public sealed class DivisorCycleCache for (int i = 0; i < length; i++) { ulong divisor = divisors[i]; - if (divisor <= 1UL) - { - throw new InvalidDataException("Divisor must be > 1"); - } if (divisor < (ulong)snapshot.Length) { ulong cached = snapshot[divisor]; - if (cached == 0UL) - { - throw new InvalidDataException($"Divisor cycle is missing for {divisor}"); - } + ArgumentOutOfRangeException.ThrowIfZero(cached); cycles[i] = cached; } @@ -308,68 +262,27 @@ public sealed class DivisorCycleCache } } - private static void ObserveCycleCacheDivisor(ulong divisor) - { - bool logHit = false; - ulong hitNumber = 0UL; - bool trackingDisabledNow = false; - - lock (CycleCacheTrackingLock) - { - if (CycleCacheTrackingDisabled) - { - return; - } - - if (!CycleCacheTrackedDivisors.Add(divisor)) - { - CycleCacheHitCount++; - hitNumber = CycleCacheHitCount; - logHit = true; - } - else if (CycleCacheTrackedDivisors.Count >= CycleCacheTrackingLimit) - { - CycleCacheTrackingDisabled = true; - CycleCacheTrackedDivisors.Clear(); - trackingDisabledNow = true; - } - } - - if (logHit) - { - Console.WriteLine($"Cycle cache hit for divisor {divisor} ({hitNumber})"); - } - else if (trackingDisabledNow) - { - Console.WriteLine("Cycle cache hit tracking disabled after exceeding the tracking limit."); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ComputeCyclesCpu(ReadOnlySpan divisors, Span cycles, ReadOnlySpan indices) { - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; for (int i = 0; i < indices.Length; i++) { int targetIndex = indices[i]; ulong divisor = divisors[targetIndex]; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData); - divisorPool.Return(divisorData); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ComputeCyclesHybrid(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan divisors, Span cycles, ReadOnlySpan indices) { - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; for (int i = 0; i < indices.Length; i++) { int targetIndex = indices[i]; ulong divisor = divisors[targetIndex]; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLengthHybrid(gpu, divisor, divisorData); - divisorPool.Return(divisorData); } } @@ -408,44 +321,44 @@ public sealed class DivisorCycleCache private const int DefaultCapacity = 128; [ThreadStatic] - private static MemoryBuffer1D? divisorBuffer; + private static MemoryBuffer1D? _divisorBuffer; [ThreadStatic] - private static MemoryBuffer1D? powBuffer; + private static MemoryBuffer1D? _powBuffer; [ThreadStatic] - private static MemoryBuffer1D? orderBuffer; + private static MemoryBuffer1D? _orderBuffer; [ThreadStatic] - private static MemoryBuffer1D? resultBuffer; + private static MemoryBuffer1D? _resultBuffer; [ThreadStatic] - private static MemoryBuffer1D? statusBuffer; + private static MemoryBuffer1D? _statusBuffer; private void EnsureCapacity(int requiredCapacity) { - if (requiredCapacity <= (divisorBuffer?.Length ?? 0)) + if (_divisorBuffer is not null && requiredCapacity <= _divisorBuffer.Length) { return; } - if (divisorBuffer != null) + if (_divisorBuffer != null) { - divisorBuffer.Dispose(); - powBuffer!.Dispose(); - orderBuffer!.Dispose(); - resultBuffer!.Dispose(); - statusBuffer!.Dispose(); + _divisorBuffer.Dispose(); + _powBuffer!.Dispose(); + _orderBuffer!.Dispose(); + _resultBuffer!.Dispose(); + _statusBuffer!.Dispose(); } Accelerator accelerator = _accelerator; // lock (accelerator) { - divisorBuffer = accelerator.Allocate1D(requiredCapacity); - powBuffer = accelerator.Allocate1D(requiredCapacity); - orderBuffer = accelerator.Allocate1D(requiredCapacity); - resultBuffer = accelerator.Allocate1D(requiredCapacity); - statusBuffer = accelerator.Allocate1D(requiredCapacity); + _divisorBuffer = accelerator.Allocate1D(requiredCapacity); + _powBuffer = accelerator.Allocate1D(requiredCapacity); + _orderBuffer = accelerator.Allocate1D(requiredCapacity); + _resultBuffer = accelerator.Allocate1D(requiredCapacity); + _statusBuffer = accelerator.Allocate1D(requiredCapacity); } } @@ -496,17 +409,17 @@ public sealed class DivisorCycleCache var acceleratorIndex = _acceleratorIndex; var stream = AcceleratorStreamPool.Rent(acceleratorIndex); - divisorBuffer!.View.CopyFromCPU(stream, divisors); - powBuffer!.View.CopyFromCPU(stream, powSpan); - orderBuffer!.View.CopyFromCPU(stream, orderSpan); - resultBuffer!.View.CopyFromCPU(stream, resultSpan); - statusBuffer!.View.CopyFromCPU(stream, statusSpan); + _divisorBuffer!.View.CopyFromCPU(stream, divisors); + _powBuffer!.View.CopyFromCPU(stream, powSpan); + _orderBuffer!.View.CopyFromCPU(stream, orderSpan); + _resultBuffer!.View.CopyFromCPU(stream, resultSpan); + _statusBuffer!.View.CopyFromCPU(stream, statusSpan); do { - kernel(stream, length, _divisorCyclesBatchSize, divisorBuffer.View, powBuffer.View, orderBuffer.View, resultBuffer.View, statusBuffer.View); + kernel(stream, length, _divisorCyclesBatchSize, _divisorBuffer.View, _powBuffer.View, _orderBuffer.View, _resultBuffer.View, _statusBuffer.View); - statusBuffer.View.CopyToCPU(stream, statusSpan); + _statusBuffer.View.CopyToCPU(stream, statusSpan); stream.Synchronize(); pending = 0; @@ -520,7 +433,7 @@ public sealed class DivisorCycleCache } while (pending > 0); - resultBuffer.View.CopyToCPU(stream, resultSpan); + _resultBuffer.View.CopyToCPU(stream, resultSpan); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); diff --git a/PerfectNumbers.Core/ExponentRemainderStepperCpu.cs b/PerfectNumbers.Core/ExponentRemainderStepperCpu.cs index 3e875b6..c8c5bf6 100644 --- a/PerfectNumbers.Core/ExponentRemainderStepperCpu.cs +++ b/PerfectNumbers.Core/ExponentRemainderStepperCpu.cs @@ -7,7 +7,7 @@ namespace PerfectNumbers.Core; /// Tracks successive Montgomery residues for a single divisor while scanning exponent candidates on the CPU path. /// Callers are responsible for seeding the stepper once per divisor and then advancing exponents in ascending order. /// -internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ulong cycleLength = 0UL) +public struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ulong cycleLength = 0UL) { private readonly MontgomeryDivisorData _divisor = divisor; private readonly ulong _modulus = divisor.Modulus; diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index a2abc94..aaa127e 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -17,10 +17,8 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN private int _gpuBatchSize = GpuConstants.ScanBatchSize; // EvenPerfectBitScanner configures the GPU tester once before scanning and never mutates the configuration afterwards, // so the synchronization fields from the previous implementation remain commented out here. - // private readonly object _sync = new(); private ulong _divisorLimit; private BigInteger _minK = BigInteger.One; - // private bool _isConfigured; private readonly ConcurrentFixedCapacityStack[] _resourcePool = [.. AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentFixedCapacityStack(PerfectNumberConstants.DefaultPoolCapacity))]; @@ -64,14 +62,8 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN { // EvenPerfectBitScanner configures the GPU tester once before scanning and never mutates the configuration afterwards, // so synchronization and runtime configuration guards are unnecessary here. - // lock (_sync) - // { - // _divisorLimit = ComputeDivisorLimitFromMaxPrimeGpu(maxPrime); - // _isConfigured = true; - // } _divisorLimit = ComputeDivisorLimitFromMaxPrimeGpu(maxPrime); - // _isConfigured = true; } public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out BigInteger divisor) @@ -80,16 +72,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN int batchCapacity; // EvenPerfectBitScanner only calls into this tester after configuring it once, so we can read the cached values without locking. - // lock (_sync) - // { - // if (!_isConfigured) - // { - // throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester."); - // } - - // allowedMax = ComputeAllowedMaxDivisorGpu(prime, _divisorLimit); - // batchCapacity = _gpuBatchSize; - // } allowedMax = ComputeAllowedMaxDivisorGpu(prime, _divisorLimit); batchCapacity = _gpuBatchSize; @@ -103,7 +85,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN bool composite; bool coveredRange; - ulong processedCount; ulong lastProcessed; // GpuPrimeWorkLimiter(); @@ -135,8 +116,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN resources.Counts, resources.Cycles, out lastProcessed, - out coveredRange, - out processedCount); + out coveredRange); ReturnBatchResources(acceleratorIndex, resources); // Monitor.Exit(gpuLease.ExecutionLock); @@ -213,8 +193,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN int[] counts, ulong[] cycles, out ulong lastProcessed, - out bool coveredRange, - out ulong processedCount) + out bool coveredRange) { int batchCapacity = (int)divisorDataBuffer.Length; bool composite = false; @@ -244,7 +223,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN if (!hasCandidates) { coveredRange = true; - processedCount = processedCountLocal; lastProcessed = lastProcessedLocal; return false; } @@ -272,7 +250,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN ArrayView1D exponentViewDevice = exponentBuffer.View; ArrayView1D hitsView = hitsBuffer.View; ArrayView1D hitIndexView = hitIndexBuffer.View; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; + static bool ProcessRange( PrimeOrderCalculatorAccelerator gpu, ulong prime, @@ -291,7 +269,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN Span offsetSpan, Span countSpan, Span cycleSpan, - FixedCapacityStack divisorPool, int chunkCountBaseline, int acceleratorIndex, ref bool composite, @@ -341,8 +318,8 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN if (localStepper.IsAdmissible()) { ulong divisorValue = (ulong)nextDivisor128; - MontgomeryDivisorData montgomeryData = divisorPool.FromModulus(divisorValue); - ulong divisorCycle = ResolveDivisorCycle(gpu, divisorValue, prime, in montgomeryData); + MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue); + ulong divisorCycle = ResolveDivisorCycle(gpu, divisorValue, prime, montgomeryData); if (divisorCycle == prime) { processedCountLocal += (ulong)(i + 1); @@ -351,7 +328,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN return false; } - divisorPool.Return(montgomeryData); divisorSpan[admissibleCount] = divisorValue; divisorDataSpan[admissibleCount] = new GpuDivisorPartialData(divisorValue); offsetSpan[admissibleCount] = admissibleCount; @@ -434,7 +410,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN offsetSpan, countSpan, cycleSpan, - divisorPool, chunkCountBaseline, acceleratorIndex, ref composite, @@ -446,7 +421,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN if (composite) { coveredRange = true; - processedCount = processedCountLocal; lastProcessed = lastProcessedLocal; return true; } @@ -474,7 +448,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN offsetSpan, countSpan, cycleSpan, - divisorPool, chunkCountBaseline, acceleratorIndex, ref composite, @@ -487,7 +460,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN processedAll = processedTop && processedBottom; coveredRange = composite || processedAll; - processedCount = processedCountLocal; lastProcessed = lastProcessedLocal; return composite; } diff --git a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs index d64e9db..e580cbc 100644 --- a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs +++ b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs @@ -163,7 +163,7 @@ internal static partial class PrimeOrderGpuHeuristics } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - public static GpuPow2ModStatus TryPow2Mod(PrimeOrderCalculatorAccelerator gpu, ulong exponent, ulong prime, out ulong remainder, in MontgomeryDivisorData? divisorData) + public static GpuPow2ModStatus TryPow2Mod(PrimeOrderCalculatorAccelerator gpu, ulong exponent, ulong prime, out ulong remainder, in MontgomeryDivisorData divisorData) { Span exponents = stackalloc ulong[1]; Span remainders = stackalloc ulong[1]; @@ -175,7 +175,7 @@ internal static partial class PrimeOrderGpuHeuristics } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - public static GpuPow2ModStatus TryPow2ModBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, ulong prime, Span remainders, in MontgomeryDivisorData? divisorData) + public static GpuPow2ModStatus TryPow2ModBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, ulong prime, Span remainders, in MontgomeryDivisorData divisorData) { // This will never occur in production code // if (exponents.Length == 0) @@ -364,7 +364,7 @@ internal static partial class PrimeOrderGpuHeuristics return GpuPow2ModStatus.Success; } - private static bool TryComputeOnGpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, ulong prime, MontgomeryDivisorData? divisorData, Span results) + private static bool TryComputeOnGpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, ulong prime, MontgomeryDivisorData divisorData, Span results) { // GpuPrimeWorkLimiter.Acquire(); int acceleratorIndex = gpu.AcceleratorIndex; @@ -379,10 +379,11 @@ internal static partial class PrimeOrderGpuHeuristics var exponentBufferView = gpu.InputView; var remainderBufferView = gpu.OutputUlongView; - if (divisorData is null) - { - divisorData = MontgomeryDivisorData.Empty; - } + // if (divisorData.Equals(MontgomeryDivisorData.Empty) is null) + // if (divisorData is null) + // { + // divisorData = MontgomeryDivisorData.Empty; + // } AcceleratorStream? stream = AcceleratorStreamPool.Rent(acceleratorIndex); exponentBufferView.CopyFromCPU(stream, exponents); diff --git a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs index acf95c1..6f37522 100644 --- a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs @@ -483,7 +483,7 @@ public sealed class HeuristicCombinedPrimeTester internal static HeuristicDivisorPreparation PrepareHeuristicDivisor(in HeuristicDivisorCandidate candidate) { ulong divisor = candidate.Value; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); bool hasCycleHint = TryGetCycleLengthHint(divisor, out ulong cycleLength); return new(candidate, divisorData, cycleLength, hasCycleHint); } diff --git a/PerfectNumbers.Core/HeuristicPrimeTester.cs b/PerfectNumbers.Core/HeuristicPrimeTester.cs index 82977b1..7098b20 100644 --- a/PerfectNumbers.Core/HeuristicPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicPrimeTester.cs @@ -413,7 +413,7 @@ public sealed class HeuristicPrimeTester internal static HeuristicDivisorPreparation PrepareHeuristicDivisor(in HeuristicDivisorCandidate candidate) { ulong divisor = candidate.Value; - MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); bool hasCycleHint = TryGetCycleLengthHint(divisor, out ulong cycleLength); return new HeuristicDivisorPreparation(candidate, divisorData, cycleLength, hasCycleHint); } diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index d9a00e8..d8dbb47 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -394,10 +394,8 @@ public class MersenneDivisorCycles if (divisor <= ulong.MaxValue) { ulong divisor64 = (ulong)divisor; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64); ulong cycle = CalculateCycleLengthCpu(divisor64, divisorData); - divisorPool.Return(divisorData); return (UInt128)cycle; } @@ -441,10 +439,8 @@ public class MersenneDivisorCycles if (divisor <= ulong.MaxValue) { ulong divisor64 = (ulong)divisor; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64); ulong cycle = CalculateCycleLengthHybrid(gpu, divisor64, divisorData); - divisorPool.Return(divisorData); return (UInt128)cycle; } @@ -489,10 +485,8 @@ public class MersenneDivisorCycles if (divisor <= ulong.MaxValue) { ulong divisor64 = (ulong)divisor; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64); ulong cycle = CalculateCycleLengthGpu(gpu, divisor64, divisorData); - divisorPool.Return(divisorData); return (UInt128)cycle; } @@ -1168,7 +1162,6 @@ public class MersenneDivisorCycles // (ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength)); int localCycleIndex = 0; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; var gpu = PrimeOrderCalculatorAccelerator.Rent(1); for (ulong divisor = localStart; divisor <= localEnd; divisor++) { @@ -1188,9 +1181,8 @@ public class MersenneDivisorCycles // TODO: Migrate this generation path to the shared // unrolled-hex calculator so the CPU generator matches // the GPU benchmark leader for large divisors. - MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); ulong cycle = CalculateCycleLengthGpu(gpu, divisor, divisorData); - divisorPool.Return(divisorData); localCycles[localCycleIndex++] = (divisor, cycle); } diff --git a/PerfectNumbers.Core/MontgomeryDivisorData.cs b/PerfectNumbers.Core/MontgomeryDivisorData.cs index 8b9e5a9..51bc61b 100644 --- a/PerfectNumbers.Core/MontgomeryDivisorData.cs +++ b/PerfectNumbers.Core/MontgomeryDivisorData.cs @@ -1,24 +1,46 @@ +using System.Runtime.CompilerServices; + namespace PerfectNumbers.Core; -public sealed class MontgomeryDivisorData +public readonly struct MontgomeryDivisorData(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared) { public static readonly MontgomeryDivisorData Empty = new(); - public ulong Modulus; - public ulong NPrime; - public ulong MontgomeryOne; - public ulong MontgomeryTwo; - public ulong MontgomeryTwoSquared; - public MontgomeryDivisorData() - { - } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static ulong ComputeMontgomeryResidue(UInt128 value, ulong modulus) => (ulong)(value % modulus); - public MontgomeryDivisorData(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared) + private static ulong ComputeMontgomeryNPrime(ulong modulus) { - Modulus = modulus; - NPrime = nPrime; - MontgomeryOne = montgomeryOne; - MontgomeryTwo = montgomeryTwo; - MontgomeryTwoSquared = montgomeryTwoSquared; + ulong inv = modulus; + inv *= unchecked(2UL - modulus * inv); + inv *= unchecked(2UL - modulus * inv); + inv *= unchecked(2UL - modulus * inv); + inv *= unchecked(2UL - modulus * inv); + inv *= unchecked(2UL - modulus * inv); + inv *= unchecked(2UL - modulus * inv); + inv *= unchecked(2UL - modulus * inv); + return unchecked(0UL - inv); } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static MontgomeryDivisorData FromModulus(ulong modulus) + { + ulong nPrime = ComputeMontgomeryNPrime(modulus); + ulong montgomeryOne = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64, modulus); + ulong montgomeryTwo = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64x2, modulus); + ulong montgomeryTwoSquared = ULongExtensions.MontgomeryMultiplyCpu(montgomeryTwo, montgomeryTwo, modulus, nPrime); + + return new( + modulus, + nPrime, + montgomeryOne, + montgomeryTwo, + montgomeryTwoSquared); + } + + public readonly ulong Modulus = modulus; + public readonly ulong NPrime = nPrime; + public readonly ulong MontgomeryOne = montgomeryOne; + public readonly ulong MontgomeryTwo = montgomeryTwo; + public readonly ulong MontgomeryTwoSquared = montgomeryTwoSquared; } diff --git a/PerfectNumbers.Core/MontgomeryDivisorDataPool.cs b/PerfectNumbers.Core/MontgomeryDivisorDataPool.cs deleted file mode 100644 index e66a6a4..0000000 --- a/PerfectNumbers.Core/MontgomeryDivisorDataPool.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace PerfectNumbers.Core; - -public static class MontgomeryDivisorDataPool -{ - [ThreadStatic] - private static FixedCapacityStack? _pool; - public static FixedCapacityStack Shared - { - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - get => _pool ??= new(PerfectNumberConstants.DefaultPoolCapacity); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private static ulong ComputeMontgomeryResidue(UInt128 value, ulong modulus) => (ulong)(value % modulus); - - private static ulong ComputeMontgomeryNPrime(ulong modulus) - { - ulong inv = modulus; - inv *= unchecked(2UL - modulus * inv); - inv *= unchecked(2UL - modulus * inv); - inv *= unchecked(2UL - modulus * inv); - inv *= unchecked(2UL - modulus * inv); - inv *= unchecked(2UL - modulus * inv); - inv *= unchecked(2UL - modulus * inv); - inv *= unchecked(2UL - modulus * inv); - return unchecked(0UL - inv); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static MontgomeryDivisorData Rent(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared) - { - var pool = _pool ??= new(PerfectNumberConstants.DefaultPoolCapacity); - if (pool.Pop() is { } pooled) - { - pooled.Modulus = modulus; - pooled.NPrime = nPrime; - pooled.MontgomeryOne = montgomeryOne; - pooled.MontgomeryTwo = montgomeryTwo; - pooled.MontgomeryTwoSquared = montgomeryTwoSquared; - return pooled; - } - - return new(modulus, nPrime, montgomeryOne, montgomeryTwo, montgomeryTwoSquared); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static void Return(MontgomeryDivisorData divisorData) => _pool!.Push(divisorData); - - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static MontgomeryDivisorData FromModulus(this FixedCapacityStack queue, ulong modulus) - { - ulong nPrime = ComputeMontgomeryNPrime(modulus); - ulong montgomeryOne = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64, modulus); - ulong montgomeryTwo = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64x2, modulus); - ulong montgomeryTwoSquared = ULongExtensions.MontgomeryMultiplyCpu(montgomeryTwo, montgomeryTwo, modulus, nPrime); - - return queue.Rent( - modulus, - nPrime, - montgomeryOne, - montgomeryTwo, - montgomeryTwoSquared); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static MontgomeryDivisorData Rent(this FixedCapacityStack queue, ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared) - { - if (queue.Pop() is { } pooled) - { - pooled.Modulus = modulus; - pooled.NPrime = nPrime; - pooled.MontgomeryOne = montgomeryOne; - pooled.MontgomeryTwo = montgomeryTwo; - pooled.MontgomeryTwoSquared = montgomeryTwoSquared; - return pooled; - } - - return new(modulus, nPrime, montgomeryOne, montgomeryTwo, montgomeryTwoSquared); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static void Return(this FixedCapacityStack queue, MontgomeryDivisorData divisorData) => queue.Push(divisorData); -} diff --git a/PerfectNumbers.Core/PartialFactorResult.cs b/PerfectNumbers.Core/PartialFactorResult.cs index 2179377..e2a4142 100644 --- a/PerfectNumbers.Core/PartialFactorResult.cs +++ b/PerfectNumbers.Core/PartialFactorResult.cs @@ -2,7 +2,7 @@ using PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core { - public sealed class PartialFactorResult + public sealed class PartialFactorResult(in MontgomeryDivisorData divisorData) { [ThreadStatic] private static PartialFactorResult? s_poolHead; @@ -13,26 +13,27 @@ namespace PerfectNumbers.Core public readonly List PendingFactors = new (PrimeOrderConstants.GpuSmallPrimeFactorSlots); public readonly ulong[] StackCandidates = new ulong[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3]; public readonly bool[] StackEvaluations = new bool[PrimeOrderConstants.GpuSmallPrimeFactorSlots << 3]; + public ExponentRemainderStepperCpu ExponentRemainderStepper = new(divisorData); private PartialFactorResult? _next; public ulong Cofactor; public bool FullyFactored; public int Count; public bool CofactorIsPrime; - private PartialFactorResult() - { - } - - public static PartialFactorResult Rent() + public static PartialFactorResult Rent(in MontgomeryDivisorData divisorData) { PartialFactorResult? instance = s_poolHead; if (instance is null) { - instance = new PartialFactorResult(); + instance = new PartialFactorResult(divisorData); } else { s_poolHead = instance._next; + if (!instance.ExponentRemainderStepper.MatchesDivisor(divisorData, 0UL)) + { + instance.ExponentRemainderStepper = new(divisorData, 0UL); + } } instance._next = null; @@ -43,7 +44,7 @@ namespace PerfectNumbers.Core return instance; } - public static readonly PartialFactorResult Empty = new() + public static readonly PartialFactorResult Empty = new(MontgomeryDivisorData.Empty) { Cofactor = 1UL, FullyFactored = true diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 41488f0..7dd9aa7 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -25,7 +25,7 @@ internal static partial class PrimeOrderCalculator ulong phi = prime - 1UL; - PartialFactorResult phiFactors = PartialFactorCpu(phi, config); + PartialFactorResult phiFactors = PartialFactorCpu(phi, divisorData, config); ulong result; if (phiFactors.Factors is null) @@ -45,8 +45,7 @@ internal static partial class PrimeOrderCalculator in UInt128 prime, in UInt128? previousOrder, in PrimeOrderCalculatorConfig config) - { - MontgomeryDivisorData divisorData; + { UInt128 result; if (prime <= ulong.MaxValue) { @@ -65,16 +64,13 @@ internal static partial class PrimeOrderCalculator } ulong prime64 = (ulong)prime; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - divisorData = divisorPool.FromModulus(prime64); + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime64); ulong order64 = CalculateCpu(prime64, previous, divisorData, config); - divisorPool.Return(divisorData); result = order64 == 0UL ? UInt128.Zero : (UInt128)order64; } else { - divisorData = MontgomeryDivisorData.Empty; - result = CalculateWideInternalCpu(prime, previousOrder, divisorData, config); + result = CalculateWideInternalCpu(prime, previousOrder, MontgomeryDivisorData.Empty, config); } return result; @@ -561,7 +557,7 @@ internal static partial class PrimeOrderCalculator SortFactorExponentSpans(factorsSpan, exponentsSpan, factorCount); - ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); + ExponentRemainderStepperCpu stepper = factors.ExponentRemainderStepper; Span stackCandidates = new(factors.StackCandidates); Span stackEvaluations = new(factors.StackEvaluations); @@ -579,7 +575,6 @@ internal static partial class PrimeOrderCalculator ProcessExponentLoweringPrime(stackCandidates[..exponent], stackEvaluations[..exponent], ref order, primeFactor, exponent, ref stepper); } - ThreadStaticPools.ReturnExponentStepperCpu(stepper); return order; } @@ -673,7 +668,7 @@ internal static partial class PrimeOrderCalculator return false; } - PartialFactorResult factorization = PartialFactorCpu(order, config); + PartialFactorResult factorization = PartialFactorCpu(order, divisorData, config); if (factorization.Factors is null) { factorization.Dispose(); @@ -810,7 +805,7 @@ internal static partial class PrimeOrderCalculator } // Reuse the partial factorization from TryConfirmOrderCpu when available. - PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactorCpu(order, config); + PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactorCpu(order, divisorData, config); try { if (orderFactors.Factors is null) @@ -1106,7 +1101,7 @@ internal static partial class PrimeOrderCalculator [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryConfirmCandidateCpu(ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config, ref int powUsed, int powBudget) { - PartialFactorResult factorization = PartialFactorCpu(candidate, config); + PartialFactorResult factorization = PartialFactorCpu(candidate, divisorData, config); if (factorization.Factors is null) { @@ -1270,7 +1265,7 @@ internal static partial class PrimeOrderCalculator => exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static PartialFactorResult PartialFactorCpu(ulong value, in PrimeOrderCalculatorConfig config) + private static PartialFactorResult PartialFactorCpu(ulong value, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config) { if (value <= 1UL) { @@ -1279,7 +1274,7 @@ internal static partial class PrimeOrderCalculator // stackalloc is faster than pooling - var result = PartialFactorResult.Rent(); + var result = PartialFactorResult.Rent(divisorData); ulong[] factors = result.Factors; Span primeSlots = new(factors); int[] exponents = result.Exponents; diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index 9c580b4..8380216 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -76,7 +76,7 @@ internal static partial class PrimeOrderCalculator return gpuOrder; } - PartialFactorResult phiFactors = PartialFactorGpu(gpu, phi, config); + PartialFactorResult phiFactors = PartialFactorGpu(gpu, phi, divisorData, config); ulong result; if (phiFactors.Factors is null) @@ -117,10 +117,8 @@ internal static partial class PrimeOrderCalculator } ulong prime64 = (ulong)prime; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - divisorData = divisorPool.FromModulus(prime64); + divisorData = MontgomeryDivisorData.FromModulus(prime64); ulong order64 = CalculateGpu(gpu, prime64, previous, divisorData, config); - divisorPool.Return(divisorData); result = order64 == 0UL ? UInt128.Zero : (UInt128)order64; } else @@ -133,7 +131,7 @@ internal static partial class PrimeOrderCalculator } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static UInt128 CalculateWideInternalGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData? divisorData, in PrimeOrderCalculatorConfig config) + private static UInt128 CalculateWideInternalGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config) { if (prime <= UInt128.One) { @@ -233,7 +231,7 @@ internal static partial class PrimeOrderCalculator return result != 0; } - private static UInt128 ExponentLoweringWideGpu(PrimeOrderCalculatorAccelerator gpu, UInt128 order, in UInt128 prime, in MontgomeryDivisorData? divisorData, in PartialFactorResult128 factors) + private static UInt128 ExponentLoweringWideGpu(PrimeOrderCalculatorAccelerator gpu, UInt128 order, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors) { ArrayPool pool = ThreadStaticPools.FactorEntry128Pool; ReadOnlySpan factorSpan = factors.Factors; @@ -281,7 +279,7 @@ internal static partial class PrimeOrderCalculator return order; } - private static UInt128 FinishStrictlyWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in MontgomeryDivisorData? divisorData) + private static UInt128 FinishStrictlyWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in MontgomeryDivisorData divisorData) { UInt128 phi = prime - UInt128.One; Dictionary counts = new(capacity: 8); @@ -320,7 +318,7 @@ internal static partial class PrimeOrderCalculator return order; } - private static UInt128 InitializeStartingOrderWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 phi, in MontgomeryDivisorData? divisorData) + private static UInt128 InitializeStartingOrderWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 phi, in MontgomeryDivisorData divisorData) { UInt128 order = phi; UInt128 mod8 = prime & (UInt128)7UL; @@ -337,7 +335,7 @@ internal static partial class PrimeOrderCalculator } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static PartialFactorResult PartialFactorGpu(PrimeOrderCalculatorAccelerator gpu, ulong value, in PrimeOrderCalculatorConfig config) + private static PartialFactorResult PartialFactorGpu(PrimeOrderCalculatorAccelerator gpu, ulong value, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config) { if (value <= 1UL) { @@ -345,7 +343,7 @@ internal static partial class PrimeOrderCalculator } // stackalloc is faster than pooling - var result = PartialFactorResult.Rent(); + var result = PartialFactorResult.Rent(divisorData); ulong[] factors = result.Factors; Span primeSlots = new (factors); int[] exponents = result.Exponents; @@ -478,7 +476,7 @@ internal static partial class PrimeOrderCalculator } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static UInt128 Pow2ModWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData? divisorData) + private static UInt128 Pow2ModWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData divisorData) { if (modulus == UInt128.One) { @@ -509,7 +507,7 @@ internal static partial class PrimeOrderCalculator PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128? previousOrder, - in MontgomeryDivisorData? divisorData, + in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config, in UInt128 phi, in PartialFactorResult128 phiFactors) @@ -540,7 +538,7 @@ internal static partial class PrimeOrderCalculator return candidateOrder; } - private static bool TryConfirmCandidateWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 candidate, in PrimeOrderCalculatorConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData? divisorData) + private static bool TryConfirmCandidateWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 candidate, in PrimeOrderCalculatorConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData divisorData) { PartialFactorResult128 factorization = PartialFactorWide(candidate, config); if (factorization.Factors is null) @@ -593,7 +591,7 @@ internal static partial class PrimeOrderCalculator return true; } - private static bool TryConfirmOrderWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 order, in MontgomeryDivisorData? divisorData, in PrimeOrderCalculatorConfig config) + private static bool TryConfirmOrderWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 order, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config) { if (order == UInt128.Zero) { @@ -670,7 +668,7 @@ internal static partial class PrimeOrderCalculator } // Reuse the partial factorization from TryConfirmOrderCpu when available. - PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactorGpu(gpu, order, config); + PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactorGpu(gpu, order, divisorData, config); try { if (orderFactors.Factors is null) @@ -854,7 +852,7 @@ internal static partial class PrimeOrderCalculator in UInt128 prime, in UInt128 order, in UInt128? previousOrder, - in MontgomeryDivisorData? divisorData, + in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config, out UInt128 result) { @@ -1023,7 +1021,7 @@ internal static partial class PrimeOrderCalculator return true; } - private static bool TrySpecialMaxWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 phi, in UInt128 prime, in MontgomeryDivisorData? divisorData, in PartialFactorResult128 factors) + private static bool TrySpecialMaxWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 phi, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors) { ReadOnlySpan factorSpan = factors.Factors; int length = factors.Count; diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Hybrid.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Hybrid.cs index 966f0c3..3339b62 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Hybrid.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Hybrid.cs @@ -25,7 +25,7 @@ internal static partial class PrimeOrderCalculator ulong phi = prime - 1UL; - PartialFactorResult phiFactors = PartialFactorHybrid(gpu, phi, config); + PartialFactorResult phiFactors = PartialFactorHybrid(gpu, phi, divisorData, config); ulong result; if (phiFactors.Factors is null) @@ -67,10 +67,8 @@ internal static partial class PrimeOrderCalculator } ulong prime64 = (ulong)prime; - FixedCapacityStack divisorPool = MontgomeryDivisorDataPool.Shared; - divisorData = divisorPool.FromModulus(prime64); + divisorData = MontgomeryDivisorData.FromModulus(prime64); ulong order64 = CalculateHybrid(gpu, prime64, previous, divisorData, config); - divisorPool.Return(divisorData); result = order64 == 0UL ? UInt128.Zero : (UInt128)order64; } else @@ -134,7 +132,7 @@ internal static partial class PrimeOrderCalculator } [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private static PartialFactorResult PartialFactorHybrid(PrimeOrderCalculatorAccelerator gpu, ulong value, in PrimeOrderCalculatorConfig config) + private static PartialFactorResult PartialFactorHybrid(PrimeOrderCalculatorAccelerator gpu, ulong value, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config) { if (value <= 1UL) { @@ -143,7 +141,7 @@ internal static partial class PrimeOrderCalculator // stackalloc is faster than pooling - var result = PartialFactorResult.Rent(); + var result = PartialFactorResult.Rent(divisorData); ulong[] factors = result.Factors; Span primeSlots = new(factors); int[] exponents = result.Exponents; @@ -369,7 +367,7 @@ internal static partial class PrimeOrderCalculator [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryConfirmCandidateHybrid(PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config, ref int powUsed, int powBudget) { - PartialFactorResult factorization = PartialFactorHybrid(gpu, candidate, config); + PartialFactorResult factorization = PartialFactorHybrid(gpu, candidate, divisorData, config); if (factorization.Factors is null) { @@ -466,7 +464,7 @@ internal static partial class PrimeOrderCalculator return false; } - PartialFactorResult factorization = PartialFactorHybrid(gpu, order, config); + PartialFactorResult factorization = PartialFactorHybrid(gpu, order, divisorData, config); if (factorization.Factors is null) { factorization.Dispose(); @@ -514,7 +512,7 @@ internal static partial class PrimeOrderCalculator } // Reuse the partial factorization from TryConfirmOrderCpu when available. - PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactorHybrid(gpu, order, config); + PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactorHybrid(gpu, order, divisorData, config); try { if (orderFactors.Factors is null) diff --git a/PerfectNumbers.Core/ThreadStaticPools.cs b/PerfectNumbers.Core/ThreadStaticPools.cs index 3efe013..61a2dad 100644 --- a/PerfectNumbers.Core/ThreadStaticPools.cs +++ b/PerfectNumbers.Core/ThreadStaticPools.cs @@ -182,15 +182,6 @@ namespace PerfectNumbers.Core get => _bytePool ??= ArrayPool.Create(); } - [ThreadStatic] - private static ArrayPool? _montgomeryDivisorDataPool; - - public static ArrayPool MontgomeryDivisorDataPool - { - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - get => _montgomeryDivisorDataPool ??= ArrayPool.Create(); - } - [ThreadStatic] private static ArrayPool? _gpuDivisorPartialDataPool; diff --git a/PerfectNumbers.Core/UInt128Extensions.cs b/PerfectNumbers.Core/UInt128Extensions.cs index 2968436..a355a43 100644 --- a/PerfectNumbers.Core/UInt128Extensions.cs +++ b/PerfectNumbers.Core/UInt128Extensions.cs @@ -510,38 +510,38 @@ public static class UInt128Extensions ulong rightLow = (ulong)right; ulong rightHigh = (ulong)(right >> 64); - var partial0 = Multiply64(leftLow, rightLow); - var partial1 = Multiply64(leftLow, rightHigh); - var partial2 = Multiply64(leftHigh, rightLow); - var partial3 = Multiply64(leftHigh, rightHigh); + (ulong carry, p0) = Multiply64(leftLow, rightLow); + (ulong partial1High, ulong partial1Low) = Multiply64(leftLow, rightHigh); - p0 = partial0.Low; - ulong carry = partial0.High; - ulong sum = carry + partial1.Low; - ulong carryMid = sum < partial1.Low ? 1UL : 0UL; - sum += partial2.Low; - if (sum < partial2.Low) + ulong sum = carry + partial1Low; + carry = sum < partial1Low ? 1UL : 0UL; + + (ulong partial2High, ulong partial2Low) = Multiply64(leftHigh, rightLow); + sum += partial2Low; + if (sum < partial2Low) { - carryMid++; + carry++; } p1 = sum; - sum = partial1.High + partial2.High; - ulong carryHigh = sum < partial2.High ? 1UL : 0UL; - sum += partial3.Low; - if (sum < partial3.Low) + sum = partial1High + partial2High; + partial2High = sum < partial2High ? 1UL : 0UL; + + (partial1High, partial1Low) = Multiply64(leftHigh, rightHigh); + sum += partial1Low; + if (sum < partial1Low) { - carryHigh++; + partial2High++; } - sum += carryMid; - if (sum < carryMid) + sum += carry; + if (sum < carry) { - carryHigh++; + partial2High++; } p2 = sum; - p3 = partial3.High + carryHigh; + p3 = partial1High + partial2High; } [MethodImpl(MethodImplOptions.AggressiveInlining)]