MontgomeryDivisorDataPool.Rent / .Return removed as we no longer pool Montgomery divisor data. UInt128Extensions.MontgomeryDivisorDataPool variables optimized.
This commit is contained in:
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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in new issue
Block a user