MontgomeryDivisorDataPool.Rent / .Return removed as we no longer pool Montgomery divisor data. UInt128Extensions.MontgomeryDivisorDataPool variables optimized.

This commit is contained in:
mlemanczyk committed 2025-12-16 02:27:29 +01:00
1 parent b7773adad8
commit 3c80221105
27 files changed
+260 -476

No files matched your search

@@ -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);
@@ -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();
@@ -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(
@@ -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);
@@ -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);
@@ -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));
}
@@ -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);
}
@@ -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();
@@ -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
{
@@ -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,
@@ -5,20 +5,20 @@ namespace PerfectNumbers.Core.Cpu;
internal sealed class MersenneCpuDivisorScanSession(PrimeOrderCalculatorAccelerator gpu, ComputationDevice orderDevice) : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
{
private Func<ulong, ulong> _getCycleLength = orderDevice switch
private Func<ulong, MontgomeryDivisorData, ulong> _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)
// {
@@ -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<MontgomeryDivisorData> 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)
{
+62 -149
View File
@@ -19,22 +19,18 @@ public sealed class DivisorCycleCache
private static int _sharedDivisorCyclesBatchSize = GpuConstants.GpuCycleStepsPerInvocation;
private const int CycleCacheTrackingLimit = 500_000;
private static readonly HashSet<ulong> CycleCacheTrackedDivisors = new();
private static readonly HashSet<ulong> 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<ulong> result = stackalloc ulong[1];
ReadOnlySpan<ulong> 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<MontgomeryDivisorData> 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<ulong> result = stackalloc ulong[1];
ReadOnlySpan<ulong> 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<MontgomeryDivisorData> 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<MontgomeryDivisorData> 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<ulong> divisors, Span<ulong> 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<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
{
FixedCapacityStack<MontgomeryDivisorData> 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<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
{
FixedCapacityStack<MontgomeryDivisorData> 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<ulong, Stride1D.Dense>? divisorBuffer;
private static MemoryBuffer1D<ulong, Stride1D.Dense>? _divisorBuffer;
[ThreadStatic]
private static MemoryBuffer1D<ulong, Stride1D.Dense>? powBuffer;
private static MemoryBuffer1D<ulong, Stride1D.Dense>? _powBuffer;
[ThreadStatic]
private static MemoryBuffer1D<ulong, Stride1D.Dense>? orderBuffer;
private static MemoryBuffer1D<ulong, Stride1D.Dense>? _orderBuffer;
[ThreadStatic]
private static MemoryBuffer1D<ulong, Stride1D.Dense>? resultBuffer;
private static MemoryBuffer1D<ulong, Stride1D.Dense>? _resultBuffer;
[ThreadStatic]
private static MemoryBuffer1D<byte, Stride1D.Dense>? statusBuffer;
private static MemoryBuffer1D<byte, Stride1D.Dense>? _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<ulong>(requiredCapacity);
powBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
orderBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
resultBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
statusBuffer = accelerator.Allocate1D<byte>(requiredCapacity);
_divisorBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
_powBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
_orderBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
_resultBuffer = accelerator.Allocate1D<ulong>(requiredCapacity);
_statusBuffer = accelerator.Allocate1D<byte>(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);
@@ -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.
/// </summary>
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;
@@ -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<MersenneNumberDivisorByDivisorAccelerator>[] _resourcePool = [.. AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentFixedCapacityStack<MersenneNumberDivisorByDivisorAccelerator>(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<ulong, Stride1D.Dense> exponentViewDevice = exponentBuffer.View;
ArrayView1D<byte, Stride1D.Dense> hitsView = hitsBuffer.View;
ArrayView1D<int, Stride1D.Dense> hitIndexView = hitIndexBuffer.View;
FixedCapacityStack<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
static bool ProcessRange(
PrimeOrderCalculatorAccelerator gpu,
ulong prime,
@@ -291,7 +269,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
Span<int> offsetSpan,
Span<int> countSpan,
Span<ulong> cycleSpan,
FixedCapacityStack<MontgomeryDivisorData> 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;
}
@@ -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<ulong> exponents = stackalloc ulong[1];
Span<ulong> remainders = stackalloc ulong[1];
@@ -175,7 +175,7 @@ internal static partial class PrimeOrderGpuHeuristics
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static GpuPow2ModStatus TryPow2ModBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> exponents, ulong prime, Span<ulong> remainders, in MontgomeryDivisorData? divisorData)
public static GpuPow2ModStatus TryPow2ModBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> exponents, ulong prime, Span<ulong> 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<ulong> exponents, ulong prime, MontgomeryDivisorData? divisorData, Span<ulong> results)
private static bool TryComputeOnGpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> exponents, ulong prime, MontgomeryDivisorData divisorData, Span<ulong> 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);
@@ -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);
}
+1 -1
View File
@@ -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);
}
+4 -12
View File
@@ -394,10 +394,8 @@ public class MersenneDivisorCycles
if (divisor <= ulong.MaxValue)
{
ulong divisor64 = (ulong)divisor;
FixedCapacityStack<MontgomeryDivisorData> 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<MontgomeryDivisorData> 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<MontgomeryDivisorData> 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<MontgomeryDivisorData> 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);
}
+37 -15
View File
@@ -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;
}
@@ -1,86 +0,0 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core;
public static class MontgomeryDivisorDataPool
{
[ThreadStatic]
private static FixedCapacityStack<MontgomeryDivisorData>? _pool;
public static FixedCapacityStack<MontgomeryDivisorData> 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<MontgomeryDivisorData> 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<MontgomeryDivisorData> 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<MontgomeryDivisorData> queue, MontgomeryDivisorData divisorData) => queue.Push(divisorData);
}
+9 -8
View File
@@ -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<PartialFactorPendingEntry> 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
+10 -15
View File
@@ -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<MontgomeryDivisorData> 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<ulong> stackCandidates = new(factors.StackCandidates);
Span<bool> 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<ulong> primeSlots = new(factors);
int[] exponents = result.Exponents;
+15 -17
View File
@@ -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<MontgomeryDivisorData> 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<FactorEntry128> pool = ThreadStaticPools.FactorEntry128Pool;
ReadOnlySpan<FactorEntry128> 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<UInt128, int> 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<ulong> 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<FactorEntry128> factorSpan = factors.Factors;
int length = factors.Count;
@@ -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<MontgomeryDivisorData> 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<ulong> 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)
-9
View File
@@ -182,15 +182,6 @@ namespace PerfectNumbers.Core
get => _bytePool ??= ArrayPool<byte>.Create();
}
[ThreadStatic]
private static ArrayPool<MontgomeryDivisorData>? _montgomeryDivisorDataPool;
public static ArrayPool<MontgomeryDivisorData> MontgomeryDivisorDataPool
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => _montgomeryDivisorDataPool ??= ArrayPool<MontgomeryDivisorData>.Create();
}
[ThreadStatic]
private static ArrayPool<GpuDivisorPartialData>? _gpuDivisorPartialDataPool;
+20 -20
View File
@@ -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)]