PrimeOrderCalculatorAccelerator.Rent is now created outside of the loops and re-used for all calculations.

This commit is contained in:
mlemanczyk committed 2025-11-24 22:51:53 +01:00
1 parent 57a50e2a37
commit b9ca95bf8c
53 files changed
+835 -425

No files matched your search

@@ -4,6 +4,7 @@ using BenchmarkDotNet.Jobs;
using System.Numerics; using System.Numerics;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace EvenPerfectBitScanner.Benchmarks; namespace EvenPerfectBitScanner.Benchmarks;
@@ -98,11 +99,13 @@ public class Pow2ModBenchmarks
GetData(out var exponents, out var divisors, out _); GetData(out var exponents, out var divisors, out _);
ulong checksum = 0UL; ulong checksum = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
checksum ^= exponents[i].Pow2MontgomeryModWindowedConvertGpu(divisors[i]); checksum ^= exponents[i].Pow2MontgomeryModWindowedConvertGpu(gpu, divisors[i]);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -134,11 +137,13 @@ public class Pow2ModBenchmarks
GetData(out var exponents, out var divisors, out var cycles); GetData(out var exponents, out var divisors, out var cycles);
ulong checksum = 0UL; ulong checksum = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(cycles[i], divisors[i]); checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(gpu, cycles[i], divisors[i]);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -175,12 +180,14 @@ public class Pow2ModBenchmarks
GetData(out var exponents, out var divisors, out _); GetData(out var exponents, out var divisors, out _);
ulong checksum = 0UL; ulong checksum = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(divisors[i].Modulus); ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(divisors[i].Modulus);
checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(cycle, divisors[i]); checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(gpu, cycle, divisors[i]);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -190,12 +197,14 @@ public class Pow2ModBenchmarks
GetData(out var exponents, out var divisors, out _); GetData(out var exponents, out var divisors, out _);
ulong checksum = 0UL; ulong checksum = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
ulong cycle = CalculateCycleLengthWithHeuristics(divisors[i].Modulus); ulong cycle = CalculateCycleLengthWithHeuristics(gpu, divisors[i].Modulus);
checksum ^= exponents[i].Pow2MontgomeryModWithCycleCpu(cycle, divisors[i]); checksum ^= exponents[i].Pow2MontgomeryModWithCycleCpu(cycle, divisors[i]);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -209,12 +218,14 @@ public class Pow2ModBenchmarks
GetData(out var exponents, out var divisors, out _); GetData(out var exponents, out var divisors, out _);
ulong checksum = 0UL; ulong checksum = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
ulong cycle = CalculateCycleLengthWithHeuristics(divisors[i].Modulus); ulong cycle = CalculateCycleLengthWithHeuristics(gpu, divisors[i].Modulus);
checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(cycle, divisors[i]); checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(gpu, cycle, divisors[i]);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -223,12 +234,14 @@ public class Pow2ModBenchmarks
{ {
UInt128 checksum = UInt128.Zero; UInt128 checksum = UInt128.Zero;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
UInt128 cycle = CalculateCycleLengthWithHeuristics(_wideModuli[i]); UInt128 cycle = CalculateCycleLengthWithHeuristics(gpu, _wideModuli[i]);
checksum ^= cycle; checksum ^= cycle;
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -464,13 +477,14 @@ public class Pow2ModBenchmarks
ulong? previousPrimeOrder = null; ulong? previousPrimeOrder = null;
UInt128? previousWidePrimeOrder = null; UInt128? previousWidePrimeOrder = null;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
ulong smallModulus = NextSmallOddModulus(random); ulong smallModulus = NextSmallOddModulus(random);
Console.WriteLine($"Calculating small cycle {smallModulus}"); Console.WriteLine($"Calculating small cycle {smallModulus}");
smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus);
smallExponents[i] = NextSmallExponent(random); smallExponents[i] = NextSmallExponent(random);
smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(smallModulus, MontgomeryDivisorDataPool.Shared.FromModulus(smallModulus)); smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(gpu, smallModulus, MontgomeryDivisorDataPool.Shared.FromModulus(smallModulus));
(ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(random); (ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(random);
Console.WriteLine($"Calculating large cycle {largeModulus}"); Console.WriteLine($"Calculating large cycle {largeModulus}");
@@ -482,13 +496,13 @@ public class Pow2ModBenchmarks
Console.WriteLine($"Calculating very large cycle {veryLargeModulus} with heuristics"); Console.WriteLine($"Calculating very large cycle {veryLargeModulus} with heuristics");
veryLargeDivisors[i] = CreateMontgomeryDivisorData(veryLargeModulus); veryLargeDivisors[i] = CreateMontgomeryDivisorData(veryLargeModulus);
veryLargeExponents[i] = NextVeryLargeExponent(heuristicRandom); veryLargeExponents[i] = NextVeryLargeExponent(heuristicRandom);
veryLargeCycles[i] = CalculateCycleLengthWithHeuristics(veryLargeModulus, ref previousPrimeOrder); veryLargeCycles[i] = CalculateCycleLengthWithHeuristics(gpu, veryLargeModulus, ref previousPrimeOrder);
UInt128 wideModulus = NextWideOddModulus(wideRandom); UInt128 wideModulus = NextWideOddModulus(wideRandom);
Console.WriteLine($"Calculating wide cycle {wideModulus} with heuristics"); Console.WriteLine($"Calculating wide cycle {wideModulus} with heuristics");
wideModuli[i] = wideModulus; wideModuli[i] = wideModulus;
wideExponents[i] = NextWideExponent(wideRandom); wideExponents[i] = NextWideExponent(wideRandom);
wideCycles[i] = CalculateCycleLengthWithHeuristics(wideModulus, ref previousWidePrimeOrder); wideCycles[i] = CalculateCycleLengthWithHeuristics(gpu, wideModulus, ref previousWidePrimeOrder);
} }
SampleCollections collections = new SampleCollections( SampleCollections collections = new SampleCollections(
@@ -507,6 +521,7 @@ public class Pow2ModBenchmarks
Console.WriteLine("Finished preparing power-of-two modulus benchmark sample collections."); Console.WriteLine("Finished preparing power-of-two modulus benchmark sample collections.");
PrimeOrderCalculatorAccelerator.Return(gpu);
return collections; return collections;
} }
@@ -580,15 +595,16 @@ public class Pow2ModBenchmarks
return ((UInt128)high << 64) | low; return ((UInt128)high << 64) | low;
} }
private static ulong CalculateCycleLengthWithHeuristics(ulong modulus, ref ulong? previousPrimeOrder) private static ulong CalculateCycleLengthWithHeuristics(PrimeOrderCalculatorAccelerator gpu, ulong modulus, ref ulong? previousPrimeOrder)
{ {
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(modulus);
if (modulus <= 1UL || (modulus & 1UL) == 0UL) if (modulus <= 1UL || (modulus & 1UL) == 0UL)
{ {
return MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData); return MersenneDivisorCycles.CalculateCycleLength(gpu, modulus, divisorData);
} }
ulong order = PrimeOrderCalculator.Calculate( ulong order = PrimeOrderCalculator.Calculate(
gpu,
modulus, modulus,
previousPrimeOrder, previousPrimeOrder,
divisorData, divisorData,
@@ -602,17 +618,18 @@ public class Pow2ModBenchmarks
} }
previousPrimeOrder = null; previousPrimeOrder = null;
return MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData); return MersenneDivisorCycles.CalculateCycleLength(gpu, modulus, divisorData);
} }
private static UInt128 CalculateCycleLengthWithHeuristics(UInt128 modulus, ref UInt128? previousWidePrimeOrder) private static UInt128 CalculateCycleLengthWithHeuristics(PrimeOrderCalculatorAccelerator gpu, UInt128 modulus, ref UInt128? previousWidePrimeOrder)
{ {
if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero) if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero)
{ {
return MersenneDivisorCycles.GetCycle(modulus); return MersenneDivisorCycles.GetCycle(gpu, modulus);
} }
UInt128 order = PrimeOrderCalculator.Calculate( UInt128 order = PrimeOrderCalculator.Calculate(
gpu,
modulus, modulus,
previousWidePrimeOrder, previousWidePrimeOrder,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
@@ -625,7 +642,7 @@ public class Pow2ModBenchmarks
} }
previousWidePrimeOrder = null; previousWidePrimeOrder = null;
return MersenneDivisorCycles.GetCycle(modulus); return MersenneDivisorCycles.GetCycle(gpu, modulus);
} }
} }
@@ -649,18 +666,19 @@ public class Pow2ModBenchmarks
montgomeryTwoSquared); montgomeryTwoSquared);
} }
private ulong CalculateCycleLengthWithHeuristics(ulong modulus, ulong fallbackOrder = 0UL) private ulong CalculateCycleLengthWithHeuristics(PrimeOrderCalculatorAccelerator gpu, ulong modulus, ulong fallbackOrder = 0UL)
{ {
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(modulus);
if (modulus <= 1UL || (modulus & 1UL) == 0UL) if (modulus <= 1UL || (modulus & 1UL) == 0UL)
{ {
return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData); return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLength(gpu, modulus, divisorData);
} }
#if DEBUG #if DEBUG
Console.WriteLine("Trying heuristic. Prime order calculation"); Console.WriteLine("Trying heuristic. Prime order calculation");
#endif #endif
ulong order = PrimeOrderCalculator.Calculate( ulong order = PrimeOrderCalculator.Calculate(
gpu,
modulus, modulus,
_previousPrimeOrder, _previousPrimeOrder,
divisorData, divisorData,
@@ -676,20 +694,21 @@ public class Pow2ModBenchmarks
_previousPrimeOrder = null; _previousPrimeOrder = null;
Console.WriteLine($"Heuristic failed for {modulus}, falling back to full cycle calculation"); Console.WriteLine($"Heuristic failed for {modulus}, falling back to full cycle calculation");
return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData); return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLength(gpu, modulus, divisorData);
} }
private UInt128 CalculateCycleLengthWithHeuristics(UInt128 modulus) private UInt128 CalculateCycleLengthWithHeuristics(PrimeOrderCalculatorAccelerator gpu, UInt128 modulus)
{ {
if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero) if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero)
{ {
return MersenneDivisorCycles.GetCycle(modulus); return MersenneDivisorCycles.GetCycle(gpu, modulus);
} }
#if DEBUG #if DEBUG
Console.WriteLine("Trying heuristic. Wide prime order calculation"); Console.WriteLine("Trying heuristic. Wide prime order calculation");
#endif #endif
UInt128 order = PrimeOrderCalculator.Calculate( UInt128 order = PrimeOrderCalculator.Calculate(
gpu,
modulus, modulus,
_previousWidePrimeOrder, _previousWidePrimeOrder,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
@@ -704,7 +723,7 @@ public class Pow2ModBenchmarks
_previousWidePrimeOrder = null; _previousWidePrimeOrder = null;
Console.WriteLine($"Wide heuristic failed for {modulus}, falling back to full cycle calculation"); Console.WriteLine($"Wide heuristic failed for {modulus}, falling back to full cycle calculation");
return MersenneDivisorCycles.GetCycle(modulus); return MersenneDivisorCycles.GetCycle(gpu, modulus);
} }
private static ulong ComputeMontgomeryResidue(ulong value, ulong modulus) => (ulong)((UInt128)value * (UInt128.One << 64) % modulus); private static ulong ComputeMontgomeryResidue(ulong value, ulong modulus) => (ulong)((UInt128)value * (UInt128.One << 64) % modulus);
@@ -2,6 +2,7 @@ using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using System.Numerics; using System.Numerics;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace EvenPerfectBitScanner.Benchmarks; namespace EvenPerfectBitScanner.Benchmarks;
@@ -32,12 +33,13 @@ public class Pow2MontgomeryModBenchmarks
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
ulong smallModulus = NextSmallOddModulus(); ulong smallModulus = NextSmallOddModulus();
_smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); _smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus);
_smallExponents[i] = NextSmallExponent(); _smallExponents[i] = NextSmallExponent();
_smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(smallModulus, MontgomeryDivisorDataPool.Shared.FromModulus(smallModulus)); _smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(gpu, smallModulus, MontgomeryDivisorDataPool.Shared.FromModulus(smallModulus));
Console.WriteLine($"Calculating large cycle {i + 1} with heuristics"); Console.WriteLine($"Calculating large cycle {i + 1} with heuristics");
(ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(); (ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle();
@@ -45,6 +47,8 @@ public class Pow2MontgomeryModBenchmarks
_largeExponents[i] = NextLargeExponent(); _largeExponents[i] = NextLargeExponent();
_largeCycles[i] = largeCycle; _largeCycles[i] = largeCycle;
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
/// <summary> /// <summary>
@@ -75,11 +79,13 @@ public class Pow2MontgomeryModBenchmarks
GetData(out var exponents, out var divisors, out _); GetData(out var exponents, out var divisors, out _);
ulong checksum = 0UL; ulong checksum = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
checksum ^= exponents[i].Pow2MontgomeryModWindowedConvertGpu(divisors[i]); checksum ^= exponents[i].Pow2MontgomeryModWindowedConvertGpu(gpu, divisors[i]);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -109,11 +115,13 @@ public class Pow2MontgomeryModBenchmarks
GetData(out var exponents, out var divisors, out var cycles); GetData(out var exponents, out var divisors, out var cycles);
ulong checksum = 0UL; ulong checksum = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < SampleCount; i++) for (int i = 0; i < SampleCount; i++)
{ {
checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(cycles[i], divisors[i]); checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(gpu, cycles[i], divisors[i]);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -1,4 +1,5 @@
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace EvenPerfectBitScanner.Benchmarks; namespace EvenPerfectBitScanner.Benchmarks;
@@ -60,12 +61,13 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider
}; };
var cases = new Pow2MontgomeryModWindowedBenchmarkCase[seeds.Length]; var cases = new Pow2MontgomeryModWindowedBenchmarkCase[seeds.Length];
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < seeds.Length; i++) for (int i = 0; i < seeds.Length; i++)
{ {
Pow2MontgomeryModWindowedBenchmarkSeed seed = seeds[i]; Pow2MontgomeryModWindowedBenchmarkSeed seed = seeds[i];
ulong modulus = 2UL * seed.Multiplier * seed.Exponent + 1UL; ulong modulus = 2UL * seed.Multiplier * seed.Exponent + 1UL;
MontgomeryDivisorData divisor = MontgomeryDivisorDataPool.Shared.FromModulus(modulus); MontgomeryDivisorData divisor = MontgomeryDivisorDataPool.Shared.FromModulus(modulus);
ulong cycleLength = MersenneDivisorCycles.CalculateCycleLength(modulus, divisor); ulong cycleLength = MersenneDivisorCycles.CalculateCycleLength(gpu, modulus, divisor);
ulong reducedExponent = seed.Exponent % cycleLength; ulong reducedExponent = seed.Exponent % cycleLength;
cases[i] = new Pow2MontgomeryModWindowedBenchmarkCase( cases[i] = new Pow2MontgomeryModWindowedBenchmarkCase(
seed.Exponent, seed.Exponent,
@@ -75,6 +77,7 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider
reducedExponent); reducedExponent);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return new Pow2MontgomeryModWindowedBenchmarkInputs(cases); return new Pow2MontgomeryModWindowedBenchmarkInputs(cases);
} }
@@ -1,6 +1,7 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Gpu.Accelerators;
using PerfectNumbers.Core.Gpu.Kernels; using PerfectNumbers.Core.Gpu.Kernels;
namespace EvenPerfectBitScanner.Benchmarks; namespace EvenPerfectBitScanner.Benchmarks;
@@ -31,12 +32,14 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
ulong checksum = 0UL; ulong checksum = 0UL;
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases; Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < cases.Length; i++) for (int i = 0; i < cases.Length; i++)
{ {
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i]; ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
checksum ^= current.Exponent.Pow2MontgomeryModWindowedConvertGpu(current.Divisor); checksum ^= current.Exponent.Pow2MontgomeryModWindowedConvertGpu(gpu, current.Divisor);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -49,12 +52,14 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
ulong checksum = 0UL; ulong checksum = 0UL;
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases; Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < cases.Length; i++) for (int i = 0; i < cases.Length; i++)
{ {
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i]; ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
checksum ^= current.Exponent.Pow2MontgomeryModWindowedKeepGpu(current.Divisor); checksum ^= current.Exponent.Pow2MontgomeryModWindowedKeepGpu(gpu, current.Divisor);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return checksum; return checksum;
} }
@@ -28,15 +28,17 @@ public class PrimeTesterBenchmarks
PrimeOrderCalculatorAccelerator.WarmUp(); PrimeOrderCalculatorAccelerator.WarmUp();
// HeuristicGroupABPrimeTesterAccelerator.WarmUp(); // HeuristicGroupABPrimeTesterAccelerator.WarmUp();
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
_ = HeuristicPrimeTester.IsPrimeCpu(_candidates[0]); _ = HeuristicPrimeTester.IsPrimeCpu(_candidates[0]);
_ = HeuristicPrimeTester.IsPrimeGpu(_candidates[0]); _ = HeuristicPrimeTester.IsPrimeGpu(gpu, _candidates[0]);
_ = Prime.Numbers.IsPrime(_candidates[0]); _ = Prime.Numbers.IsPrime(_candidates[0]);
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
// [Benchmark] // [Benchmark]
public int HeuristicCpu() public int HeuristicCpu()
{ {
HeuristicPrimeTester tester = _heuristicTester;
ulong[] values = _candidates; ulong[] values = _candidates;
int primeCount = 0; int primeCount = 0;
@@ -54,17 +56,18 @@ public class PrimeTesterBenchmarks
[Benchmark] [Benchmark]
public int HeuristicGpu() public int HeuristicGpu()
{ {
HeuristicPrimeTester tester = _heuristicTester;
ulong[] values = _candidates; ulong[] values = _candidates;
int primeCount = 0; int primeCount = 0;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < values.Length; i++) for (int i = 0; i < values.Length; i++)
{ {
if (HeuristicPrimeTester.IsPrimeGpu(values[i])) if (HeuristicPrimeTester.IsPrimeGpu(gpu, values[i]))
{ {
primeCount++; primeCount++;
} }
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return primeCount; return primeCount;
} }
@@ -75,9 +78,10 @@ public class PrimeTesterBenchmarks
ulong[] values = _candidates; ulong[] values = _candidates;
int primeCount = 0; int primeCount = 0;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < values.Length; i++) for (int i = 0; i < values.Length; i++)
{ {
if (HeuristicCombinedPrimeTester.IsPrimeGpu(values[i])) if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, values[i]))
{ {
primeCount++; primeCount++;
} }
@@ -109,14 +113,16 @@ public class PrimeTesterBenchmarks
ulong[] values = _candidates; ulong[] values = _candidates;
int primeCount = 0; int primeCount = 0;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int i = 0; i < values.Length; i++) for (int i = 0; i < values.Length; i++)
{ {
if (PrimeTester.IsPrimeGpu(values[i])) if (PrimeTester.IsPrimeGpu(gpu, values[i]))
{ {
primeCount++; primeCount++;
} }
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return primeCount; return primeCount;
} }
+40 -17
View File
@@ -6,6 +6,7 @@ using PerfectNumbers.Core.Gpu;
using EvenPerfectBitScanner.Candidates; using EvenPerfectBitScanner.Candidates;
using EvenPerfectBitScanner.Candidates.Transforms; using EvenPerfectBitScanner.Candidates.Transforms;
using System.Numerics; using System.Numerics;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace EvenPerfectBitScanner.Tests; namespace EvenPerfectBitScanner.Tests;
@@ -104,14 +105,16 @@ public class ProgramTests
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) }); candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) });
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
Program.IsEvenPerfectCandidate(11UL, 32UL, out _, out _).Should().BeFalse(); Program.IsEvenPerfectCandidate(gpu, 11UL, 32UL, out _, out _).Should().BeFalse();
Program.IsEvenPerfectCandidate(5UL, 32UL, out _, out _).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, 5UL, 32UL, out _, out _).Should().BeTrue();
Program.IsEvenPerfectCandidate(127UL, 32UL, out _, out _).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, 127UL, 32UL, out _, out _).Should().BeTrue();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
testerField.SetValue(null, null); testerField.SetValue(null, null);
candidatesField.SetValue(null, null); candidatesField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
@@ -134,14 +137,16 @@ public class ProgramTests
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
candidatesField.SetValue(null, Array.Empty<(ulong, uint)>()); candidatesField.SetValue(null, Array.Empty<(ulong, uint)>());
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
Program.IsEvenPerfectCandidate(11UL, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, 11UL, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailedCheck.Should().BeFalse(); detailedCheck.Should().BeFalse();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
testerField.SetValue(null, null); testerField.SetValue(null, null);
candidatesField.SetValue(null, null); candidatesField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
@@ -163,14 +168,16 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
Program.IsEvenPerfectCandidate(3UL, 4_000_000_000UL, out bool searched, out bool detailed).Should().BeFalse(); Program.IsEvenPerfectCandidate(gpu, 3UL, 4_000_000_000UL, out bool searched, out bool detailed).Should().BeFalse();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeTrue(); detailed.Should().BeTrue();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
testerField.SetValue(null, null); testerField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -191,18 +198,20 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
Program.IsEvenPerfectCandidate(11UL, 0UL, out bool searched, out bool detailed).Should().BeFalse(); Program.IsEvenPerfectCandidate(gpu, 11UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeTrue(); detailed.Should().BeTrue();
Program.IsEvenPerfectCandidate(13UL, 0UL, out searched, out detailed).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, 13UL, 0UL, out searched, out detailed).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeFalse(); detailed.Should().BeFalse();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
testerField.SetValue(null, null); testerField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -222,18 +231,20 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
Program.IsEvenPerfectCandidate(29UL, 0UL, out bool searched, out bool detailed).Should().BeFalse(); Program.IsEvenPerfectCandidate(gpu, 29UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeFalse(); detailed.Should().BeFalse();
Program.IsEvenPerfectCandidate(31UL, 0UL, out searched, out detailed).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, 31UL, 0UL, out searched, out detailed).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeTrue(); detailed.Should().BeTrue();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
mersenneField.SetValue(null, null); mersenneField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -256,18 +267,20 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
ulong[] primes = [107UL, 127UL]; ulong[] primes = [107UL, 127UL];
foreach (ulong prime in primes) foreach (ulong prime in primes)
{ {
Program.IsEvenPerfectCandidate(prime, 1024UL, out bool searched, out bool detailed).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, prime, 1024UL, out bool searched, out bool detailed).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeTrue(); detailed.Should().BeTrue();
} }
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
mersenneField.SetValue(null, null); mersenneField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -291,18 +304,20 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
ulong[] primes = [107UL, 127UL, 521UL, 607UL]; ulong[] primes = [107UL, 127UL, 521UL, 607UL];
Parallel.ForEach(primes, prime => Parallel.ForEach(primes, prime =>
{ {
Program.IsEvenPerfectCandidate(prime, 0UL, out bool searched, out bool detailed).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, prime, 0UL, out bool searched, out bool detailed).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeTrue(); detailed.Should().BeTrue();
}); });
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
mersenneField.SetValue(null, null); mersenneField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -325,18 +340,20 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
Program.IsEvenPerfectCandidate(107UL, 0UL, out bool searched, out bool detailed).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, 107UL, 0UL, out bool searched, out bool detailed).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeTrue(); detailed.Should().BeTrue();
Program.IsEvenPerfectCandidate(127UL, 0UL, out searched, out detailed).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, 127UL, 0UL, out searched, out detailed).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
detailed.Should().BeTrue(); detailed.Should().BeTrue();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
mersenneField.SetValue(null, null); mersenneField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -362,13 +379,15 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
Program.IsEvenPerfectCandidate(exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
mersenneField.SetValue(null, null); mersenneField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -397,13 +416,15 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
Program.IsEvenPerfectCandidate(exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue(); Program.IsEvenPerfectCandidate(gpu, exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
searched.Should().BeTrue(); searched.Should().BeTrue();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
mersenneField.SetValue(null, null); mersenneField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -476,21 +497,23 @@ public class ProgramTests
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true)); primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true)); CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
compositeField.SetValue(null, false); compositeField.SetValue(null, false);
Program.IsEvenPerfectCandidate(9UL, 0UL, out bool searched, out bool detailed).Should().BeFalse(); Program.IsEvenPerfectCandidate(gpu, 9UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
searched.Should().BeFalse(); searched.Should().BeFalse();
detailed.Should().BeFalse(); detailed.Should().BeFalse();
((bool)compositeField.GetValue(null)!).Should().BeTrue(); ((bool)compositeField.GetValue(null)!).Should().BeTrue();
Program.IsEvenPerfectCandidate(11UL, 0UL, out searched, out detailed); Program.IsEvenPerfectCandidate(gpu, 11UL, 0UL, out searched, out detailed);
searched.Should().BeTrue(); searched.Should().BeTrue();
((bool)compositeField.GetValue(null)!).Should().BeFalse(); ((bool)compositeField.GetValue(null)!).Should().BeFalse();
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
mersenneField.SetValue(null, null); mersenneField.SetValue(null, null);
primeField.SetValue(null, null); primeField.SetValue(null, null);
CandidatesCalculator.ResetResidueTrackers(); CandidatesCalculator.ResetResidueTrackers();
@@ -2,6 +2,7 @@ using System.Runtime.CompilerServices;
using Open.Numeric.Primes; using Open.Numeric.Primes;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using EvenPerfectBitScanner.Candidates.Transforms; using EvenPerfectBitScanner.Candidates.Transforms;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace EvenPerfectBitScanner.Candidates; namespace EvenPerfectBitScanner.Candidates;
@@ -53,7 +54,7 @@ internal static class CandidatesCalculator
return trackers.Value!; return trackers.Value!;
} }
internal static bool IsCompositeByResidues(ulong p) internal static bool IsCompositeByResidues(PrimeOrderCalculatorAccelerator gpu, ulong p)
{ {
ModResidueTracker tracker = AcquireResidueTracker(); ModResidueTracker tracker = AcquireResidueTracker();
// Use ModResidueTracker with a small set of primes to pre-filter composite p. // Use ModResidueTracker with a small set of primes to pre-filter composite p.
@@ -71,7 +72,7 @@ internal static class CandidatesCalculator
break; break;
} }
if (tracker.MergeOrAppend(p, primes[primeIndex], out bool divisible) && divisible) if (tracker.MergeOrAppend(gpu, p, primes[primeIndex], out bool divisible) && divisible)
{ {
return true; return true;
} }
+11 -8
View File
@@ -7,6 +7,7 @@ using Open.Collections;
using PerfectNumbers.Core; using PerfectNumbers.Core;
using PerfectNumbers.Core.Cpu; using PerfectNumbers.Core.Cpu;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace EvenPerfectBitScanner; namespace EvenPerfectBitScanner;
@@ -469,6 +470,7 @@ internal static class Program
bool passedAllTests, searchedMersenne, detailedCheck; bool passedAllTests, searchedMersenne, detailedCheck;
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool; ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
ulong[] buffer = pool.Rent(blockSize); ulong[] buffer = pool.Rent(blockSize);
PrimeOrderCalculatorAccelerator gpu = PrimeOrderCalculatorAccelerator.Rent(1);
while (!Volatile.Read(ref _limitReached)) while (!Volatile.Read(ref _limitReached))
{ {
count = CandidatesCalculator.ReserveBlock(buffer, blockSize, ref _limitReached); count = CandidatesCalculator.ReserveBlock(buffer, blockSize, ref _limitReached);
@@ -482,7 +484,7 @@ internal static class Program
for (j = 0; j < count && !Volatile.Read(ref _limitReached); j++) for (j = 0; j < count && !Volatile.Read(ref _limitReached); j++)
{ {
p = buffer[j]; p = buffer[j];
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck); passedAllTests = IsEvenPerfectCandidate(gpu, p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
CalculationResultHandler.HandleResult(p, 0UL, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP); CalculationResultHandler.HandleResult(p, 0UL, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
} }
@@ -505,7 +507,7 @@ internal static class Program
continue; continue;
} }
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck); passedAllTests = IsEvenPerfectCandidate(gpu, p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
CalculationResultHandler.HandleResult(p, 0UL, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP); CalculationResultHandler.HandleResult(p, 0UL, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
if (p == maxP) if (p == maxP)
@@ -522,6 +524,7 @@ internal static class Program
} }
pool.Return(buffer); pool.Return(buffer);
PrimeOrderCalculatorAccelerator.Return(gpu);
}); });
} }
@@ -565,7 +568,7 @@ internal static class Program
} }
} }
internal static bool IsEvenPerfectCandidate(ulong p, ulong divisorCyclesSearchLimit, out bool searchedMersenne, out bool detailedCheck) internal static bool IsEvenPerfectCandidate(PrimeOrderCalculatorAccelerator gpu, ulong p, ulong divisorCyclesSearchLimit, out bool searchedMersenne, out bool detailedCheck)
{ {
searchedMersenne = false; searchedMersenne = false;
detailedCheck = false; detailedCheck = false;
@@ -615,7 +618,7 @@ internal static class Program
} }
// Fast residue-based composite check for p using small primes // Fast residue-based composite check for p using small primes
if (!_cliArguments.UseDivisor && !_cliArguments.UseByDivisor && CandidatesCalculator.IsCompositeByResidues(p)) if (!_cliArguments.UseDivisor && !_cliArguments.UseByDivisor && CandidatesCalculator.IsCompositeByResidues(gpu, p))
{ {
searchedMersenne = false; searchedMersenne = false;
detailedCheck = false; detailedCheck = false;
@@ -628,7 +631,7 @@ internal static class Program
{ {
if (!(_runPrimesOnCpu if (!(_runPrimesOnCpu
? PrimeTester.IsPrime(p) ? PrimeTester.IsPrime(p)
: HeuristicCombinedPrimeTester.IsPrimeGpu(p))) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, p)))
{ {
_lastCompositeP = true; _lastCompositeP = true;
return false; return false;
@@ -641,7 +644,7 @@ internal static class Program
if (_cliArguments.UseDivisor) if (_cliArguments.UseDivisor)
{ {
bool divisorsExhausted; bool divisorsExhausted;
bool isPrime = _divisorTester!.IsPrime(p, _divisor, divisorCyclesSearchLimit, out divisorsExhausted); bool isPrime = _divisorTester!.IsPrime(gpu, p, _divisor, divisorCyclesSearchLimit, out divisorsExhausted);
detailedCheck = divisorsExhausted; detailedCheck = divisorsExhausted;
return isPrime; return isPrime;
} }
@@ -649,10 +652,10 @@ internal static class Program
if (_cliArguments.UseByDivisor) if (_cliArguments.UseByDivisor)
{ {
// Windowed pow2mod kernel handles by-divisor scans. // Windowed pow2mod kernel handles by-divisor scans.
return _byDivisorTester!.IsPrime(p, out detailedCheck, out _); return _byDivisorTester!.IsPrime(gpu, p, out detailedCheck, out _);
} }
detailedCheck = MersenneTesters.Value!.IsMersennePrime(p); detailedCheck = MersenneTesters.Value!.IsMersennePrime(gpu, p);
return detailedCheck; return detailedCheck;
} }
} }
@@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests.Gpu; namespace PerfectNumbers.Core.Tests.Gpu;
@@ -14,7 +15,15 @@ public class LucasLehmerPrimeTesterTests
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
public void IsMersennePrime_gpu_returns_expected_results(ulong p, bool expected) public void IsMersennePrime_gpu_returns_expected_results(ulong p, bool expected)
{ {
new MersenneNumberLucasLehmerGpuTester().IsPrime(p, true).Should().Be(expected); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
new MersenneNumberLucasLehmerGpuTester().IsPrime(gpu, p, true).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Fact] [Fact]
@@ -26,7 +35,15 @@ public class LucasLehmerPrimeTesterTests
const ulong exponent = 128UL; const ulong exponent = 128UL;
var tester = new MersenneNumberLucasLehmerGpuTester(); var tester = new MersenneNumberLucasLehmerGpuTester();
tester.WarmUpNttParameters(exponent); tester.WarmUpNttParameters(exponent);
tester.IsPrime(exponent, true).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, exponent, true).Should().BeFalse();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Fact] [Fact]
@@ -49,7 +66,15 @@ public class LucasLehmerPrimeTesterTests
{ {
var tester = new MersenneNumberLucasLehmerGpuTester(); var tester = new MersenneNumberLucasLehmerGpuTester();
tester.WarmUpNttParameters(exponent); tester.WarmUpNttParameters(exponent);
tester.IsPrime(exponent, true).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, exponent, true).Should().BeFalse();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Theory] [Theory]
@@ -59,7 +84,15 @@ public class LucasLehmerPrimeTesterTests
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
public void IsMersennePrime_cpu_matches_expected_results(ulong p, bool expected) public void IsMersennePrime_cpu_matches_expected_results(ulong p, bool expected)
{ {
new MersenneNumberLucasLehmerCpuTester().IsPrime(p).Should().Be(expected); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
new MersenneNumberLucasLehmerCpuTester().IsPrime(gpu, p).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
} }
@@ -2,6 +2,7 @@ using System.Numerics;
using System.Reflection; using System.Reflection;
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -187,11 +188,12 @@ public class PrimeOrderGpuHeuristicsTests
public void TryPow2ModWide_returns_overflow_for_marked_prime() public void TryPow2ModWide_returns_overflow_for_marked_prime()
{ {
UInt128 prime = ((UInt128)1 << 80) + 7; UInt128 prime = ((UInt128)1 << 80) + 7;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
PrimeOrderGpuHeuristics.OverflowRegistryWide[prime] = 0; PrimeOrderGpuHeuristics.OverflowRegistryWide[prime] = 0;
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(UInt128.One, prime, out UInt128 remainder); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, UInt128.One, prime, out UInt128 remainder);
status.Should().Be(GpuPow2ModStatus.Overflow); status.Should().Be(GpuPow2ModStatus.Overflow);
remainder.Should().Be(UInt128.Zero); remainder.Should().Be(UInt128.Zero);
@@ -199,6 +201,7 @@ public class PrimeOrderGpuHeuristicsTests
finally finally
{ {
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _); PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -208,7 +211,9 @@ public class PrimeOrderGpuHeuristicsTests
UInt128 prime = ((UInt128)1 << 96) + 11; UInt128 prime = ((UInt128)1 << 96) + 11;
UInt128 exponent = ((UInt128)1 << 64) + 3; UInt128 exponent = ((UInt128)1 << 64) + 3;
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out UInt128 remainder); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out UInt128 remainder);
PrimeOrderCalculatorAccelerator.Return(gpu);
status.Should().Be(GpuPow2ModStatus.Success); status.Should().Be(GpuPow2ModStatus.Success);
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponent, (BigInteger)prime); UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponent, (BigInteger)prime);
@@ -240,9 +245,10 @@ public class PrimeOrderGpuHeuristicsTests
UInt128 prime = ((UInt128)1 << 80) + 9; UInt128 prime = ((UInt128)1 << 80) + 9;
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128); var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability); PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(UInt128.One, prime, out UInt128 remainder); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, UInt128.One, prime, out UInt128 remainder);
status.Should().Be(GpuPow2ModStatus.Overflow); status.Should().Be(GpuPow2ModStatus.Overflow);
remainder.Should().Be(UInt128.Zero); remainder.Should().Be(UInt128.Zero);
@@ -251,6 +257,7 @@ public class PrimeOrderGpuHeuristicsTests
{ {
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting(); PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _); PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -259,12 +266,13 @@ public class PrimeOrderGpuHeuristicsTests
{ {
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 4); var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 4);
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability); PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
UInt128 exponent = UInt128.One << 8; UInt128 exponent = UInt128.One << 8;
UInt128 prime = ((UInt128)1 << 70) + 3; UInt128 prime = ((UInt128)1 << 70) + 3;
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out UInt128 remainder); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out UInt128 remainder);
status.Should().Be(GpuPow2ModStatus.Overflow); status.Should().Be(GpuPow2ModStatus.Overflow);
remainder.Should().Be(UInt128.Zero); remainder.Should().Be(UInt128.Zero);
@@ -272,6 +280,7 @@ public class PrimeOrderGpuHeuristicsTests
finally finally
{ {
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting(); PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -282,7 +291,9 @@ public class PrimeOrderGpuHeuristicsTests
UInt128[] exponents = { UInt128.One, ((UInt128)1 << 64) + 5 }; UInt128[] exponents = { UInt128.One, ((UInt128)1 << 64) + 5 };
UInt128[] remainders = { UInt128.Zero, UInt128.Zero }; UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
PrimeOrderCalculatorAccelerator.Return(gpu);
status.Should().Be(GpuPow2ModStatus.Success); status.Should().Be(GpuPow2ModStatus.Success);
for (int i = 0; i < exponents.Length; i++) for (int i = 0; i < exponents.Length; i++)
@@ -300,9 +311,10 @@ public class PrimeOrderGpuHeuristicsTests
UInt128[] remainders = { UInt128.Zero, UInt128.Zero }; UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128); var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability); PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
status.Should().Be(GpuPow2ModStatus.Overflow); status.Should().Be(GpuPow2ModStatus.Overflow);
remainders[0].Should().Be(UInt128.Zero); remainders[0].Should().Be(UInt128.Zero);
@@ -312,6 +324,7 @@ public class PrimeOrderGpuHeuristicsTests
{ {
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting(); PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _); PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -323,9 +336,10 @@ public class PrimeOrderGpuHeuristicsTests
UInt128[] remainders = { UInt128.Zero, UInt128.Zero }; UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 6); var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 6);
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability); PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders); GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
status.Should().Be(GpuPow2ModStatus.Overflow); status.Should().Be(GpuPow2ModStatus.Overflow);
remainders[0].Should().Be(UInt128.Zero); remainders[0].Should().Be(UInt128.Zero);
@@ -334,6 +348,7 @@ public class PrimeOrderGpuHeuristicsTests
finally finally
{ {
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting(); PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -358,17 +373,19 @@ public class PrimeOrderGpuHeuristicsTests
public void Calculate_falls_back_to_cpu_when_gpu_overflow_is_marked() public void Calculate_falls_back_to_cpu_when_gpu_overflow_is_marked()
{ {
const ulong prime = 7UL; const ulong prime = 7UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0; PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
ulong result = PrimeOrderCalculator.Calculate(prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); ulong result = PrimeOrderCalculator.Calculate(gpu, prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
result.Should().Be(3UL); result.Should().Be(3UL);
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _); PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
} }
} }
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests.Gpu; namespace PerfectNumbers.Core.Tests.Gpu;
@@ -9,23 +10,36 @@ public class PrimeTesterGpuTests
[Fact] [Fact]
public void IsPrimeGpu_accepts_known_primes() public void IsPrimeGpu_accepts_known_primes()
{ {
var tester = new PrimeTester();
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL]; ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
foreach (ulong prime in primes) foreach (ulong prime in primes)
{ {
PrimeTester.IsPrimeGpu(prime).Should().BeTrue(); PrimeTester.IsPrimeGpu(gpu, prime).Should().BeTrue();
}
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
[Fact] [Fact]
public void IsPrimeGpu_rejects_composites() public void IsPrimeGpu_rejects_composites()
{ {
var tester = new PrimeTester();
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL]; ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
foreach (ulong composite in composites) foreach (ulong composite in composites)
{ {
PrimeTester.IsPrimeGpu(composite).Should().BeFalse(); PrimeTester.IsPrimeGpu(gpu, composite).Should().BeFalse();
}
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
} }
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -10,6 +11,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
public void GenerateGpu_produces_expected_cycles_for_small_range() public void GenerateGpu_produces_expected_cycles_for_small_range()
{ {
string path = Path.GetTempFileName(); string path = Path.GetTempFileName();
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
MersenneDivisorCycles.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0); MersenneDivisorCycles.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0);
@@ -39,11 +41,12 @@ public class MersenneDivisorCyclesGenerateGpuTests
{ {
expected.Should().Contain(pair.divisor); expected.Should().Contain(pair.divisor);
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor);
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(pair.divisor, divisorData)); pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(gpu, pair.divisor, divisorData));
} }
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
@@ -56,6 +59,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
public void Generate_produces_unique_cycles_for_expected_divisors() public void Generate_produces_unique_cycles_for_expected_divisors()
{ {
string path = Path.GetTempFileName(); string path = Path.GetTempFileName();
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4); MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4);
@@ -88,7 +92,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
{ {
uniqueDivisors.Add(pair.divisor).Should().BeTrue(); uniqueDivisors.Add(pair.divisor).Should().BeTrue();
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor);
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(pair.divisor, divisorData)); pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(gpu, pair.divisor, divisorData));
} }
uniqueDivisors.Should().BeEquivalentTo(expected); uniqueDivisors.Should().BeEquivalentTo(expected);
@@ -98,6 +102,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -17,10 +18,18 @@ public class MersenneDivisorCyclesTests
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
[MemberData(nameof(Divisors))] [MemberData(nameof(Divisors))]
public void CalculateCycleLength_returns_expected_value(ulong divisor, ulong expected) public void CalculateCycleLength_returns_expected_value(ulong divisor, ulong expected)
{
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{ {
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor);
MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData).Should().Be(expected); MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData).Should().Be(expected);
MersenneDivisorCycles.CalculateCycleLengthGpu(divisor).Should().Be(expected); MersenneDivisorCycles.CalculateCycleLengthGpu(divisor).Should().Be(expected);
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
} }
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -57,7 +58,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
return DivisorLimit; return DivisorLimit;
} }
public bool IsPrime(ulong prime, out bool divisorsExhausted, out ulong divisor) public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor)
{ {
divisorsExhausted = true; divisorsExhausted = true;
if (prime % 7UL == 0UL) if (prime % 7UL == 0UL)
@@ -76,7 +77,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
return true; return true;
} }
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession() public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
{ {
return new DummySession(); return new DummySession();
} }
@@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Cpu; using PerfectNumbers.Core.Cpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -13,18 +14,26 @@ public class MersenneNumberDivisorCpuTesterTests
var tester = new MersenneNumberDivisorByDivisorCpuTester(); var tester = new MersenneNumberDivisorByDivisorCpuTester();
tester.ConfigureFromMaxPrime(11UL); tester.ConfigureFromMaxPrime(11UL);
tester.IsPrime(5UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, 5UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
divisorsExhausted.Should().BeTrue(); divisorsExhausted.Should().BeTrue();
divisor.Should().Be(0UL); divisor.Should().Be(0UL);
tester.IsPrime(7UL, out divisorsExhausted, out divisor).Should().BeTrue(); tester.IsPrime(gpu, 7UL, out divisorsExhausted, out divisor).Should().BeTrue();
divisorsExhausted.Should().BeTrue(); divisorsExhausted.Should().BeTrue();
divisor.Should().Be(0UL); divisor.Should().Be(0UL);
tester.IsPrime(11UL, out divisorsExhausted, out divisor).Should().BeFalse(); tester.IsPrime(gpu, 11UL, out divisorsExhausted, out divisor).Should().BeFalse();
divisorsExhausted.Should().BeTrue(); divisorsExhausted.Should().BeTrue();
divisor.Should().BeGreaterThan(0UL); divisor.Should().BeGreaterThan(0UL);
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -33,24 +42,26 @@ public class MersenneNumberDivisorCpuTesterTests
var tester = new MersenneNumberDivisorByDivisorCpuTester(); var tester = new MersenneNumberDivisorByDivisorCpuTester();
tester.ConfigureFromMaxPrime(13UL); tester.ConfigureFromMaxPrime(13UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] primes = { 5UL, 7UL, 11UL, 13UL }; ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL)); ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(gpu, 23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL));
session.CheckDivisor(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL), cycle23, primes, hits); session.CheckDivisor(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL), cycle23, primes, hits);
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
Array.Fill(hits, (byte)0); Array.Fill(hits, (byte)0);
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL)); ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(gpu, 31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL));
session.CheckDivisor(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL), cycle31, primes, hits); session.CheckDivisor(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL), cycle31, primes, hits);
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 }); hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
} }
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -61,14 +72,15 @@ public class MersenneNumberDivisorCpuTesterTests
var tester = new MersenneNumberDivisorByDivisorCpuTester(); var tester = new MersenneNumberDivisorByDivisorCpuTester();
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL }; ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
byte[] hits = new byte[exponents.Length]; byte[] hits = new byte[exponents.Length];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL);
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData); ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 7UL, divisorData);
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits); session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 }); hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
@@ -76,6 +88,7 @@ public class MersenneNumberDivisorCpuTesterTests
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -86,14 +99,15 @@ public class MersenneNumberDivisorCpuTesterTests
var tester = new MersenneNumberDivisorByDivisorCpuTester(); var tester = new MersenneNumberDivisorByDivisorCpuTester();
tester.ConfigureFromMaxPrime(61UL); tester.ConfigureFromMaxPrime(61UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL }; ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
byte[] hits = new byte[exponents.Length]; byte[] hits = new byte[exponents.Length];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL);
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData); ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 11UL, divisorData);
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits); session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 }); hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
@@ -101,6 +115,7 @@ public class MersenneNumberDivisorCpuTesterTests
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
} }
@@ -2,6 +2,7 @@ using System.Reflection;
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Cpu; using PerfectNumbers.Core.Cpu;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -44,17 +45,34 @@ public class MersenneNumberDivisorGpuTesterTests
.GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)! .GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!
.SetValue(null, Array.Empty<(ulong, uint)>()); .SetValue(null, Array.Empty<(ulong, uint)>());
tester.IsPrime(31UL, UInt128.Zero, 0UL, out bool divisorsExhausted).Should().BeTrue(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, 31UL, UInt128.Zero, 0UL, out bool divisorsExhausted).Should().BeTrue();
divisorsExhausted.Should().BeFalse(); divisorsExhausted.Should().BeFalse();
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
public void IsPrime_sets_divisorsExhausted_true_when_divisible_by_specified_divisor() public void IsPrime_sets_divisorsExhausted_true_when_divisible_by_specified_divisor()
{ {
var tester = new MersenneNumberDivisorGpuTester(); var tester = new MersenneNumberDivisorGpuTester();
tester.IsPrime(35UL, 31UL, 0UL, out bool divisorsExhausted).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, 35UL, 31UL, 0UL, out bool divisorsExhausted).Should().BeFalse();
divisorsExhausted.Should().BeTrue(); divisorsExhausted.Should().BeTrue();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Fact] [Fact]
@@ -62,9 +80,17 @@ public class MersenneNumberDivisorGpuTesterTests
public void IsPrime_accepts_large_search_limits() public void IsPrime_accepts_large_search_limits()
{ {
var tester = new MersenneNumberDivisorGpuTester(); var tester = new MersenneNumberDivisorGpuTester();
tester.IsPrime(35UL, 31UL, ulong.MaxValue, out bool exhausted).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, 35UL, 31UL, ulong.MaxValue, out bool exhausted).Should().BeFalse();
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -73,18 +99,26 @@ public class MersenneNumberDivisorGpuTesterTests
var tester = new MersenneNumberDivisorByDivisorGpuTester(); var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
tester.IsPrime(31UL, out bool exhausted, out ulong divisor).Should().BeTrue(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, 31UL, out bool exhausted, out ulong divisor).Should().BeTrue();
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
divisor.Should().Be(0UL); divisor.Should().Be(0UL);
tester.IsPrime(37UL, out exhausted, out divisor).Should().BeFalse(); tester.IsPrime(gpu, 37UL, out exhausted, out divisor).Should().BeFalse();
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
divisor.Should().BeGreaterThan(0UL); divisor.Should().BeGreaterThan(0UL);
tester.IsPrime(43UL, out exhausted, out divisor).Should().BeFalse(); tester.IsPrime(gpu, 43UL, out exhausted, out divisor).Should().BeFalse();
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
divisor.Should().BeGreaterThan(0UL); divisor.Should().BeGreaterThan(0UL);
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -108,16 +142,24 @@ public class MersenneNumberDivisorGpuTesterTests
var tester = new MersenneNumberDivisorByDivisorGpuTester(); var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.ConfigureFromMaxPrime(41UL); tester.ConfigureFromMaxPrime(41UL);
tester.IsPrime(31UL, out _, out ulong divisor).Should().BeTrue(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, 31UL, out _, out ulong divisor).Should().BeTrue();
divisor.Should().Be(0UL); divisor.Should().Be(0UL);
tester.IsPrime(37UL, out bool exhausted, out divisor).Should().BeFalse(); tester.IsPrime(gpu, 37UL, out bool exhausted, out divisor).Should().BeFalse();
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
divisor.Should().BeGreaterThan(0UL); divisor.Should().BeGreaterThan(0UL);
tester.IsPrime(41UL, out bool divisorsExhausted, out divisor).Should().BeFalse(); tester.IsPrime(gpu, 41UL, out bool divisorsExhausted, out divisor).Should().BeFalse();
divisorsExhausted.Should().BeTrue(); divisorsExhausted.Should().BeTrue();
divisor.Should().BeGreaterThan(0UL); divisor.Should().BeGreaterThan(0UL);
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Theory] [Theory]
@@ -130,7 +172,11 @@ public class MersenneNumberDivisorGpuTesterTests
var tester = new MersenneNumberDivisorByDivisorGpuTester(); var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.ConfigureFromMaxPrime(exponent); tester.ConfigureFromMaxPrime(exponent);
tester.IsPrime(exponent, out bool exhausted, out ulong divisor).Should().Be(expectedPrime); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, exponent, out bool exhausted, out ulong divisor).Should().Be(expectedPrime);
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
if (expectedPrime) if (expectedPrime)
{ {
@@ -141,6 +187,12 @@ public class MersenneNumberDivisorGpuTesterTests
divisor.Should().BeGreaterThan(0UL); divisor.Should().BeGreaterThan(0UL);
} }
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
public void ByDivisor_gpu_tester_uses_cycle_remainders_per_divisor() public void ByDivisor_gpu_tester_uses_cycle_remainders_per_divisor()
@@ -152,10 +204,18 @@ public class MersenneNumberDivisorGpuTesterTests
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
tester.IsPrime(41UL, out bool divisorsExhausted, out ulong divisor).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsPrime(gpu, 41UL, out bool divisorsExhausted, out ulong divisor).Should().BeFalse();
divisorsExhausted.Should().BeTrue(); divisorsExhausted.Should().BeTrue();
divisor.Should().BeGreaterThan(0UL); divisor.Should().BeGreaterThan(0UL);
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Fact] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
@@ -164,7 +224,8 @@ public class MersenneNumberDivisorGpuTesterTests
var tester = new MersenneNumberDivisorByDivisorGpuTester(); var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] primes = { 31UL, 37UL, 41UL }; ulong[] primes = { 31UL, 37UL, 41UL };
@@ -177,6 +238,7 @@ public class MersenneNumberDivisorGpuTesterTests
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -187,24 +249,26 @@ public class MersenneNumberDivisorGpuTesterTests
var tester = new MersenneNumberDivisorByDivisorGpuTester(); var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] primes = { 31UL, 37UL, 41UL, 43UL }; ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL)); ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits); session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0 });
Array.Fill(hits, (byte)0); Array.Fill(hits, (byte)0);
ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLength(13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL)); ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLength(gpu, 13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL));
session.CheckDivisor(13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL), cycle13367, primes, hits); session.CheckDivisor(13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL), cycle13367, primes, hits);
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
} }
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -219,13 +283,14 @@ public class MersenneNumberDivisorGpuTesterTests
tester.ConfigureFromMaxPrime(47UL); tester.ConfigureFromMaxPrime(47UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL }; ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL)); ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits); session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
@@ -233,6 +298,7 @@ public class MersenneNumberDivisorGpuTesterTests
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -246,14 +312,15 @@ public class MersenneNumberDivisorGpuTesterTests
}; };
tester.ConfigureFromMaxPrime(43UL); tester.ConfigureFromMaxPrime(43UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL }; ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
byte[] hits = new byte[exponents.Length]; byte[] hits = new byte[exponents.Length];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL);
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData); ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 7UL, divisorData);
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits); session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
@@ -262,6 +329,7 @@ public class MersenneNumberDivisorGpuTesterTests
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -275,14 +343,15 @@ public class MersenneNumberDivisorGpuTesterTests
}; };
tester.ConfigureFromMaxPrime(61UL); tester.ConfigureFromMaxPrime(61UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL }; ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
byte[] hits = new byte[exponents.Length]; byte[] hits = new byte[exponents.Length];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL);
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData); ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 11UL, divisorData);
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits); session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
@@ -291,6 +360,7 @@ public class MersenneNumberDivisorGpuTesterTests
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
@@ -305,13 +375,14 @@ public class MersenneNumberDivisorGpuTesterTests
var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!; var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!;
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles); var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try try
{ {
var patchedTable = new List<(ulong Divisor, ulong Cycle)>(); var patchedTable = new List<(ulong Divisor, ulong Cycle)>();
ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1]; ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(191UL); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(191UL);
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(191UL, divisorData); patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(gpu, 191UL, divisorData);
tableField.SetValue(cycles, patchedTable); tableField.SetValue(cycles, patchedTable);
smallCyclesField.SetValue(cycles, patchedSmall); smallCyclesField.SetValue(cycles, patchedSmall);
@@ -324,12 +395,13 @@ public class MersenneNumberDivisorGpuTesterTests
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)! .GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
.SetValue(tester, 200UL); .SetValue(tester, 200UL);
tester.IsPrime(19UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue(); tester.IsPrime(gpu, 19UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
divisorsExhausted.Should().BeTrue(); divisorsExhausted.Should().BeTrue();
divisor.Should().Be(0UL); divisor.Should().Be(0UL);
} }
finally finally
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
tableField.SetValue(cycles, originalTable); tableField.SetValue(cycles, originalTable);
smallCyclesField.SetValue(cycles, originalSmall); smallCyclesField.SetValue(cycles, originalSmall);
DivisorCycleCache.Shared.RefreshSnapshot(); DivisorCycleCache.Shared.RefreshSnapshot();
@@ -347,13 +419,14 @@ public class MersenneNumberDivisorGpuTesterTests
tester.ConfigureFromMaxPrime(47UL); tester.ConfigureFromMaxPrime(47UL);
var session = tester.CreateDivisorSession(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var session = tester.CreateDivisorSession(gpu);
try try
{ {
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL }; ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
byte[] hits = new byte[primes.Length]; byte[] hits = new byte[primes.Length];
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL)); ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits); session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 }); hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
@@ -361,6 +434,7 @@ public class MersenneNumberDivisorGpuTesterTests
finally finally
{ {
session.Return(); session.Return();
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
} }
@@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Cpu; using PerfectNumbers.Core.Cpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
using UInt128 = System.UInt128; using UInt128 = System.UInt128;
@@ -30,7 +31,15 @@ public class MersenneNumberIncrementalCpuTesterTests
private static void RunCase(MersenneNumberIncrementalCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime) private static void RunCase(MersenneNumberIncrementalCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
{ {
bool isPrime = true; bool isPrime = true;
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
isPrime.Should().Be(expectedPrime); isPrime.Should().Be(expectedPrime);
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
} }
@@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Cpu; using PerfectNumbers.Core.Cpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
using UInt128 = System.UInt128; using UInt128 = System.UInt128;
@@ -30,8 +31,16 @@ public class MersenneNumberOrderCpuTesterTests
private static void RunCase(MersenneNumberOrderCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime) private static void RunCase(MersenneNumberOrderCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
{ {
bool isPrime = true; bool isPrime = true;
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
isPrime.Should().Be(expectedPrime); isPrime.Should().Be(expectedPrime);
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
} }
@@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Cpu; using PerfectNumbers.Core.Cpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
using UInt128 = System.UInt128; using UInt128 = System.UInt128;
@@ -24,7 +25,15 @@ public class MersenneNumberResidueCpuTesterTests
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime) private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
{ {
bool isPrime = true; bool isPrime = true;
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
isPrime.Should().Be(expectedPrime); isPrime.Should().Be(expectedPrime);
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
} }
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -13,7 +14,15 @@ public class MersenneNumberTesterTests
public void IsMersennePrime_detects_small_prime_divisors_for_large_exponents(ulong p) public void IsMersennePrime_detects_small_prime_divisors_for_large_exponents(ulong p)
{ {
var tester = new MersenneNumberTester(); var tester = new MersenneNumberTester();
tester.IsMersennePrime(p).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsMersennePrime(gpu, p).Should().BeFalse();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Theory] [Theory]
@@ -22,7 +31,15 @@ public class MersenneNumberTesterTests
{ {
var tester = new MersenneNumberTester(useOrderCache: true, useGpuOrder: true); var tester = new MersenneNumberTester(useOrderCache: true, useGpuOrder: true);
tester.WarmUpOrders(p, 1_000UL); tester.WarmUpOrders(p, 1_000UL);
tester.IsMersennePrime(p).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsMersennePrime(gpu, p).Should().BeFalse();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Theory] [Theory]
@@ -30,7 +47,15 @@ public class MersenneNumberTesterTests
[InlineData(127UL, false)] [InlineData(127UL, false)]
public void SharesFactorWithExponentMinusOne_detects_common_factors(ulong p, bool expected) public void SharesFactorWithExponentMinusOne_detects_common_factors(ulong p, bool expected)
{ {
p.SharesFactorWithExponentMinusOne().Should().Be(expected); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
p.SharesFactorWithExponentMinusOne(gpu).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
} }
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -14,7 +15,15 @@ public class MersenneResidueModeTests
useIncremental: false, useIncremental: false,
useGpuScan: false, useGpuScan: false,
useGpuOrder: false); useGpuOrder: false);
tester.IsMersennePrime(136_000_002UL).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsMersennePrime(gpu, 136_000_002UL).Should().BeFalse();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Fact] [Fact]
@@ -26,7 +35,15 @@ public class MersenneResidueModeTests
useGpuScan: false, useGpuScan: false,
useGpuOrder: false, useGpuOrder: false,
maxK: 1_000UL); maxK: 1_000UL);
tester.IsMersennePrime(31UL).Should().BeTrue(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsMersennePrime(gpu, 31UL).Should().BeTrue();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
} }
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -11,9 +12,17 @@ public class ModResidueTrackerTests
{ {
var tracker = new ModResidueTracker(ResidueModel.Identity); var tracker = new ModResidueTracker(ResidueModel.Identity);
tracker.IsDivisible(5, 2).Should().BeFalse(); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
tracker.IsDivisible(6, 2).Should().BeTrue(); try
tracker.IsDivisible(7, 2).Should().BeFalse(); {
tracker.IsDivisible(gpu, 5, 2).Should().BeFalse();
tracker.IsDivisible(gpu, 6, 2).Should().BeTrue();
tracker.IsDivisible(gpu, 7, 2).Should().BeFalse();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Theory] [Theory]
@@ -25,42 +34,66 @@ public class ModResidueTrackerTests
public void IsDivisible_handles_mersenne_model(ulong p, ulong divisor, bool expected) public void IsDivisible_handles_mersenne_model(ulong p, ulong divisor, bool expected)
{ {
var tracker = new ModResidueTracker(ResidueModel.Mersenne); var tracker = new ModResidueTracker(ResidueModel.Mersenne);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
tracker.IsDivisible(p, divisor).Should().Be(expected); try
{
tracker.IsDivisible(gpu, p, divisor).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Fact] [Fact]
public void MergeOrAppend_tracks_existing_and_new_divisors() public void MergeOrAppend_tracks_existing_and_new_divisors()
{ {
var tracker = new ModResidueTracker(ResidueModel.Identity); var tracker = new ModResidueTracker(ResidueModel.Identity);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tracker.BeginMerge(10); tracker.BeginMerge(10);
tracker.MergeOrAppend(10, 3, out bool d3).Should().BeTrue(); tracker.MergeOrAppend(gpu, 10, 3, out bool d3).Should().BeTrue();
d3.Should().BeFalse(); d3.Should().BeFalse();
tracker.MergeOrAppend(10, 5, out bool d5).Should().BeTrue(); tracker.MergeOrAppend(gpu, 10, 5, out bool d5).Should().BeTrue();
d5.Should().BeTrue(); d5.Should().BeTrue();
tracker.BeginMerge(10); tracker.BeginMerge(10);
tracker.MergeOrAppend(10, 4, out bool d4).Should().BeTrue(); tracker.MergeOrAppend(gpu, 10, 4, out bool d4).Should().BeTrue();
d4.Should().BeFalse(); d4.Should().BeFalse();
tracker.BeginMerge(11); tracker.BeginMerge(11);
tracker.MergeOrAppend(11, 3, out d3).Should().BeTrue(); tracker.MergeOrAppend(gpu, 11, 3, out d3).Should().BeTrue();
d3.Should().BeFalse(); d3.Should().BeFalse();
tracker.MergeOrAppend(11, 4, out d4).Should().BeTrue(); tracker.MergeOrAppend(gpu, 11, 4, out d4).Should().BeTrue();
d4.Should().BeFalse(); d4.Should().BeFalse();
tracker.MergeOrAppend(11, 5, out d5).Should().BeTrue(); tracker.MergeOrAppend(gpu, 11, 5, out d5).Should().BeTrue();
d5.Should().BeFalse(); d5.Should().BeFalse();
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Fact] [Fact]
public void IsDivisible_requires_non_decreasing_numbers() public void IsDivisible_requires_non_decreasing_numbers()
{ {
var tracker = new ModResidueTracker(ResidueModel.Identity); var tracker = new ModResidueTracker(ResidueModel.Identity);
tracker.IsDivisible(5, 2); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tracker.IsDivisible(gpu, 5, 2);
Action act = () => tracker.IsDivisible(4, 2); Action act = () => tracker.IsDivisible(gpu, 4, 2);
act.Should().Throw<InvalidOperationException>(); act.Should().Throw<InvalidOperationException>();
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
} }
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -42,14 +43,18 @@ public class PrimeOrderCalculatorTests
public void Calculate_heuristic_mode_matches_naive_cycle_length(ulong prime) public void Calculate_heuristic_mode_matches_naive_cycle_length(ulong prime)
{ {
ulong expected = ComputeOrderByDoubling(prime); ulong expected = ComputeOrderByDoubling(prime);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
ulong result = PrimeOrderCalculator.Calculate( ulong result = PrimeOrderCalculator.Calculate(
gpu,
prime, prime,
previousOrder: null, previousOrder: null,
MontgomeryDivisorDataPool.Shared.FromModulus(prime), MontgomeryDivisorDataPool.Shared.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculatorAccelerator.Return(gpu);
result.Should().Be(expected); result.Should().Be(expected);
} }
@@ -60,7 +65,9 @@ public class PrimeOrderCalculatorTests
{ {
ulong previousOrder = ComputeOrderByDoubling(previousPrime); ulong previousOrder = ComputeOrderByDoubling(previousPrime);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
ulong heuristic = PrimeOrderCalculator.Calculate( ulong heuristic = PrimeOrderCalculator.Calculate(
gpu,
prime, prime,
previousOrder, previousOrder,
MontgomeryDivisorDataPool.Shared.FromModulus(prime), MontgomeryDivisorDataPool.Shared.FromModulus(prime),
@@ -68,12 +75,15 @@ public class PrimeOrderCalculatorTests
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong strict = PrimeOrderCalculator.Calculate( ulong strict = PrimeOrderCalculator.Calculate(
gpu,
prime, prime,
previousOrder: null, previousOrder: null,
MontgomeryDivisorDataPool.Shared.FromModulus(prime), MontgomeryDivisorDataPool.Shared.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault, PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculatorAccelerator.Return(gpu);
heuristic.Should().Be(strict); heuristic.Should().Be(strict);
} }
@@ -83,13 +93,16 @@ public class PrimeOrderCalculatorTests
[InlineData(3UL, 2UL)] [InlineData(3UL, 2UL)]
public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder) public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder)
{ {
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
ulong result = PrimeOrderCalculator.Calculate( ulong result = PrimeOrderCalculator.Calculate(
gpu,
prime, prime,
previousOrder: null, previousOrder: null,
MontgomeryDivisorDataPool.Shared.FromModulus(prime), MontgomeryDivisorDataPool.Shared.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculatorAccelerator.Return(gpu);
result.Should().Be(expectedOrder); result.Should().Be(expectedOrder);
} }
@@ -103,13 +116,16 @@ public class PrimeOrderCalculatorTests
maxPowChecks: 8, maxPowChecks: 8,
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic); mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
ulong result = PrimeOrderCalculator.Calculate( ulong result = PrimeOrderCalculator.Calculate(
gpu,
prime: 13UL, prime: 13UL,
previousOrder: null, previousOrder: null,
MontgomeryDivisorDataPool.Shared.FromModulus(13UL), MontgomeryDivisorDataPool.Shared.FromModulus(13UL),
config, config,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculatorAccelerator.Return(gpu);
result.Should().Be(ComputeOrderByDoubling(13UL)); result.Should().Be(ComputeOrderByDoubling(13UL));
} }
@@ -123,8 +139,12 @@ public class PrimeOrderCalculatorTests
maxPowChecks: 1, maxPowChecks: 1,
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic); mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(239UL); MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(239UL);
ulong heuristic = PrimeOrderCalculator.Calculate( ulong heuristic = PrimeOrderCalculator.Calculate(
gpu,
prime: 239UL, prime: 239UL,
previousOrder: null, previousOrder: null,
divisorData, divisorData,
@@ -140,6 +160,7 @@ public class PrimeOrderCalculatorTests
mode: PrimeOrderCalculator.PrimeOrderMode.Strict); mode: PrimeOrderCalculator.PrimeOrderMode.Strict);
ulong strict = PrimeOrderCalculator.Calculate( ulong strict = PrimeOrderCalculator.Calculate(
gpu,
prime: 239UL, prime: 239UL,
previousOrder: null, previousOrder: null,
divisorData, divisorData,
@@ -147,6 +168,12 @@ public class PrimeOrderCalculatorTests
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
strict.Should().Be(heuristic); strict.Should().Be(heuristic);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Fact] [Fact]
@@ -155,12 +182,15 @@ public class PrimeOrderCalculatorTests
{ {
UInt128 prime = UInt128.Parse("18446744073709641691"); UInt128 prime = UInt128.Parse("18446744073709641691");
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
UInt128 result = PrimeOrderCalculator.Calculate( UInt128 result = PrimeOrderCalculator.Calculate(
gpu,
prime, prime,
previousOrder: null, previousOrder: null,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu); PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculatorAccelerator.Return(gpu);
result.Should().Be((UInt128)1229782938247309446UL); result.Should().Be((UInt128)1229782938247309446UL);
} }
@@ -169,7 +199,15 @@ public class PrimeOrderCalculatorTests
public void GetCycle_uses_wide_prime_heuristic() public void GetCycle_uses_wide_prime_heuristic()
{ {
UInt128 prime = UInt128.Parse("18446744073709641691"); UInt128 prime = UInt128.Parse("18446744073709641691");
MersenneDivisorCycles.GetCycle(prime).Should().Be((UInt128)1229782938247309446UL); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
var mersenneDivisorCycles = MersenneDivisorCycles.GetCycle(gpu, prime).Should().Be((UInt128)1229782938247309446UL);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
private static ulong ComputeOrderByDoubling(ulong prime) private static ulong ComputeOrderByDoubling(ulong prime)
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -19,7 +20,15 @@ public class PrimeTesterGcdTests
{ {
ulong[] values = [81UL, 101UL]; ulong[] values = [81UL, 101UL];
Span<byte> results = stackalloc byte[values.Length]; Span<byte> results = stackalloc byte[values.Length];
PrimeTester.SharesFactorWithMaxExponentBatch(values, results); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
PrimeTester.SharesFactorWithMaxExponentBatch(gpu, values, results);
results.ToArray().Should().Equal(new byte[] { 1, 0 }); results.ToArray().Should().Equal(new byte[] { 1, 0 });
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
} }
@@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -39,7 +40,15 @@ public class PrimeTesterIsPrimeTests
{ {
var tester = new PrimeTester(); var tester = new PrimeTester();
PrimeTester.IsPrimeGpu(n).Should().Be(expected); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
PrimeTester.IsPrimeGpu(gpu, n).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Fact] [Fact]
@@ -62,9 +71,12 @@ public class PrimeTesterIsPrimeTests
var results = new bool[primes.Length]; var results = new bool[primes.Length];
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true); var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
Parallel.For(0, primes.Length, i => Parallel.For(0, primes.Length, i =>
{ {
results[i] = PrimeTester.IsPrimeGpu(primes[i]); results[i] = PrimeTester.IsPrimeGpu(gpu, primes[i]);
}); });
results.Should().AllBeEquivalentTo(true); results.Should().AllBeEquivalentTo(true);
@@ -73,9 +85,14 @@ public class PrimeTesterIsPrimeTests
{ {
foreach (ulong prime in primes) foreach (ulong prime in primes)
{ {
PrimeTester.IsPrimeGpu(prime).Should().BeTrue(); PrimeTester.IsPrimeGpu(gpu, prime).Should().BeTrue();
} }
}); });
} }
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
} }
@@ -1,5 +1,6 @@
using System.Numerics; using System.Numerics;
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -54,7 +55,15 @@ public class UInt128ExtensionsTests
{ {
UInt128 q128 = q; UInt128 q128 = q;
ulong expected = NaiveOrder(q); ulong expected = NaiveOrder(q);
q128.CalculateOrder().Should().Be(expected); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
q128.CalculateOrder(gpu).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
private static ulong NaiveOrder(ulong q) private static ulong NaiveOrder(ulong q)
@@ -1,5 +1,6 @@
using System.Numerics; using System.Numerics;
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit; using Xunit;
namespace PerfectNumbers.Core.Tests; namespace PerfectNumbers.Core.Tests;
@@ -71,7 +72,15 @@ public class ULongExtensionsTests
{ {
ulong expected = NaiveOrder(q); ulong expected = NaiveOrder(q);
q.CalculateOrder().Should().Be(expected); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
q.CalculateOrder(gpu).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Theory] [Theory]
@@ -112,7 +121,15 @@ public class ULongExtensionsTests
[InlineData(9UL, false)] [InlineData(9UL, false)]
public void SharesFactorWithExponentMinusOne_detects_relationship(ulong exponent, bool expected) public void SharesFactorWithExponentMinusOne_detects_relationship(ulong exponent, bool expected)
{ {
exponent.SharesFactorWithExponentMinusOne().Should().Be(expected); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
exponent.SharesFactorWithExponentMinusOne(gpu).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
[Theory] [Theory]
@@ -201,7 +218,15 @@ public class ULongExtensionsTests
MersenneDivisorCycles cycles = MersenneDivisorCycles.Shared; MersenneDivisorCycles cycles = MersenneDivisorCycles.Shared;
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus); UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
exponent.PowModCrt((UInt128)modulus, cycles).Should().Be(expected); var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
exponent.PowModCrt(gpu, (UInt128)modulus, cycles).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
} }
private static ulong NaiveOrder(ulong q) private static ulong NaiveOrder(ulong q)
@@ -1,11 +1,14 @@
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core.Cpu; namespace PerfectNumbers.Core.Cpu;
internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession internal sealed class MersenneCpuDivisorScanSession(PrimeOrderCalculatorAccelerator gpu) : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
{ {
public void Reset() public void Reset()
{ {
} }
// TODO: Convert this to static method
public void CheckDivisor( public void CheckDivisor(
ulong divisor, ulong divisor,
in MontgomeryDivisorData divisorData, in MontgomeryDivisorData divisorData,
@@ -29,7 +32,7 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi
if (divisorCycle == 0UL) if (divisorCycle == 0UL)
{ {
divisorCycle = DivisorCycleCache.Shared.GetCycleLength(divisor); divisorCycle = DivisorCycleCache.Shared.GetCycleLength(gpu, divisor);
if (divisorCycle == 0UL) if (divisorCycle == 0UL)
{ {
// DivisorCycleCache guarantees a positive cycle for divisors greater than one. // DivisorCycleCache guarantees a positive cycle for divisors greater than one.
@@ -4,7 +4,7 @@ using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core.Cpu; namespace PerfectNumbers.Core.Cpu;
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester public sealed class MersenneNumberDivisorByDivisorCpuTester() : IMersenneNumberDivisorByDivisorTester
{ {
private ulong _divisorLimit; private ulong _divisorLimit;
private int _batchSize = 1_024; private int _batchSize = 1_024;
@@ -42,7 +42,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit); public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit);
public bool IsPrime(ulong prime, out bool divisorsExhausted, out ulong divisor) public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor)
{ {
ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit); ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
@@ -58,6 +58,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
bool processedAll; bool processedAll;
bool composite = CheckDivisors( bool composite = CheckDivisors(
gpu,
prime, prime,
allowedMax, allowedMax,
out processedAll, out processedAll,
@@ -84,7 +85,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
} }
} }
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession() public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
{ {
MersenneCpuDivisorScanSession? session = ThreadStaticPools.RentMersenneCpuDivisorSession(); MersenneCpuDivisorScanSession? session = ThreadStaticPools.RentMersenneCpuDivisorSession();
if (session is not null) if (session is not null)
@@ -92,16 +93,16 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
return session; return session;
} }
return new MersenneCpuDivisorScanSession(); return new MersenneCpuDivisorScanSession(gpu);
} }
private bool CheckDivisors( private bool CheckDivisors(
PrimeOrderCalculatorAccelerator gpu,
ulong prime, ulong prime,
ulong allowedMax, ulong allowedMax,
out bool processedAll, out bool processedAll,
out ulong foundDivisor) out ulong foundDivisor)
{ {
foundDivisor = 0UL;
// The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs. // The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs.
// Keeping the guard commented out documents the reasoning for benchmarks and tests. // Keeping the guard commented out documents the reasoning for benchmarks and tests.
// if (allowedMax < 3UL) // if (allowedMax < 3UL)
@@ -171,6 +172,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
remainder11 = (byte)(divisorLow % 11UL); remainder11 = (byte)(divisorLow % 11UL);
return CheckDivisors64( return CheckDivisors64(
gpu,
prime, prime,
stepLow, stepLow,
limit.Low, limit.Low,
@@ -203,7 +205,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL); remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL); remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var divisorPool = MontgomeryDivisorDataPool.Shared; var divisorPool = MontgomeryDivisorDataPool.Shared;
while (divisor.CompareTo(limit) <= 0) while (divisor.CompareTo(limit) <= 0)
{ {
@@ -225,6 +226,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
// Divisors produced by 2 * k * p + 1 always exceed PerfectNumberConstants.MaxQForDivisorCycles // Divisors produced by 2 * k * p + 1 always exceed PerfectNumberConstants.MaxQForDivisorCycles
// for the exponents scanned here, so skip the unused cache fallback and compute directly. // for the exponents scanned here, so skip the unused cache fallback and compute directly.
divisorCycle = MersenneDivisorCycles.CalculateCycleLength( divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
gpu,
candidate, candidate,
divisorData, divisorData,
skipPrimeOrderHeuristic: primeOrderFailed); skipPrimeOrderHeuristic: primeOrderFailed);
@@ -241,7 +243,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
// the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p. // the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p.
foundDivisor = candidate; foundDivisor = candidate;
processedAll = true; processedAll = true;
PrimeOrderCalculatorAccelerator.Return(gpu);
return true; return true;
} }
@@ -259,13 +260,13 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
remainder11 = AddMod11(remainder11, step11); remainder11 = AddMod11(remainder11, step11);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
processedAll = true; processedAll = true;
foundDivisor = 0UL; foundDivisor = 0UL;
return false; return false;
} }
private static bool CheckDivisors64( private static bool CheckDivisors64(
PrimeOrderCalculatorAccelerator gpu,
ulong prime, ulong prime,
ulong step, ulong step,
ulong limit, ulong limit,
@@ -285,7 +286,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
out ulong foundDivisor) out ulong foundDivisor)
{ {
bool canAdvance = step <= limit; bool canAdvance = step <= limit;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
if (!canAdvance) if (!canAdvance)
{ {
@@ -304,6 +304,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
out bool primeOrderFailed) || computedCycle == 0UL) out bool primeOrderFailed) || computedCycle == 0UL)
{ {
divisorCycle = MersenneDivisorCycles.CalculateCycleLength( divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
gpu,
divisor, divisor,
divisorData, divisorData,
skipPrimeOrderHeuristic: primeOrderFailed); skipPrimeOrderHeuristic: primeOrderFailed);
@@ -319,7 +320,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
{ {
foundDivisor = divisor; foundDivisor = divisor;
processedAll = true; processedAll = true;
PrimeOrderCalculatorAccelerator.Return(gpu);
return true; return true;
} }
@@ -332,7 +332,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
processedAll = true; processedAll = true;
foundDivisor = 0UL; foundDivisor = 0UL;
PrimeOrderCalculatorAccelerator.Return(gpu);
return false; return false;
} }
@@ -340,7 +339,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
{ {
processedAll = true; processedAll = true;
foundDivisor = 0UL; foundDivisor = 0UL;
PrimeOrderCalculatorAccelerator.Return(gpu);
return false; return false;
} }
@@ -361,6 +359,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
out bool primeOrderFailed) || computedCycle == 0UL) out bool primeOrderFailed) || computedCycle == 0UL)
{ {
divisorCycle = MersenneDivisorCycles.CalculateCycleLength( divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
gpu,
divisor, divisor,
divisorData, divisorData,
skipPrimeOrderHeuristic: primeOrderFailed); skipPrimeOrderHeuristic: primeOrderFailed);
@@ -375,7 +374,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
{ {
foundDivisor = divisor; foundDivisor = divisor;
processedAll = true; processedAll = true;
PrimeOrderCalculatorAccelerator.Return(gpu);
return true; return true;
} }
@@ -390,7 +388,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
{ {
processedAll = true; processedAll = true;
foundDivisor = 0UL; foundDivisor = 0UL;
PrimeOrderCalculatorAccelerator.Return(gpu);
return false; return false;
} }
@@ -1,3 +1,5 @@
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core.Cpu; namespace PerfectNumbers.Core.Cpu;
public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType) public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType)
@@ -5,7 +7,7 @@ public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType)
private static readonly MersenneDivisorCycles _cycles = MersenneDivisorCycles.Shared; private static readonly MersenneDivisorCycles _cycles = MersenneDivisorCycles.Shared;
private readonly GpuKernelType _kernelType = kernelType; private readonly GpuKernelType _kernelType = kernelType;
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime) public void Scan(PrimeOrderCalculatorAccelerator gpu, ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
{ {
UInt128 k = UInt128.One; UInt128 k = UInt128.One;
UInt128 one = k; UInt128 one = k;
@@ -29,7 +31,7 @@ public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType)
{ {
// TODO: Replace this direct GetCycle call with DivisorCycleCache.Lookup so we reuse the single snapshot block // TODO: Replace this direct GetCycle call with DivisorCycleCache.Lookup so we reuse the single snapshot block
// and compute missing cycles on the configured device without queuing additional block generation. // and compute missing cycles on the configured device without queuing additional block generation.
qCycle = MersenneDivisorCycles.GetCycle(q); qCycle = MersenneDivisorCycles.GetCycle(gpu, q);
shouldCheck = auto.Mod10R != 5UL; shouldCheck = auto.Mod10R != 5UL;
if (shouldCheck) if (shouldCheck)
{ {
@@ -1,8 +1,10 @@
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
public class MersenneNumberLucasLehmerCpuTester public class MersenneNumberLucasLehmerCpuTester
{ {
public bool IsPrime(ulong exponent) public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong exponent)
{ {
// Early rejections aligned with incremental/order sieves, but safe for small p: // Early rejections aligned with incremental/order sieves, but safe for small p:
// - If 3 | p and p != 3, then 7 | M_p -> composite. // - If 3 | p and p != 3, then 7 | M_p -> composite.
@@ -20,7 +22,7 @@ public class MersenneNumberLucasLehmerCpuTester
return false; return false;
} }
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne()) if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne(gpu))
{ {
return false; return false;
} }
@@ -32,7 +34,7 @@ public class MersenneNumberLucasLehmerCpuTester
for (UInt128 i = UInt128.Zero; i < limit; i++) for (UInt128 i = UInt128.Zero; i < limit; i++)
{ {
// Lucas–Lehmer squares the evolving residue, so we stay on the Montgomery powmod path rather than the pow2-specific helper. // Lucas–Lehmer squares the evolving residue, so we stay on the Montgomery powmod path rather than the pow2-specific helper.
s = s.PowModWithCycle(two, MersenneDivisorCycles.GetCycle(m)) - two; s = s.PowModWithCycle(two, MersenneDivisorCycles.GetCycle(gpu, m)) - two;
if (s < UInt128.Zero) if (s < UInt128.Zero)
{ {
s += m; s += m;
@@ -1,10 +1,12 @@
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core.Cpu; namespace PerfectNumbers.Core.Cpu;
public class MersenneNumberOrderCpuTester(GpuKernelType kernelType) public class MersenneNumberOrderCpuTester(GpuKernelType kernelType)
{ {
private readonly GpuKernelType _kernelType = kernelType; private readonly GpuKernelType _kernelType = kernelType;
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime) public void Scan(PrimeOrderCalculatorAccelerator gpu, ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
{ {
UInt128 k = 1UL; UInt128 k = 1UL;
var auto = new MersenneResidueAutomaton(exponent); var auto = new MersenneResidueAutomaton(exponent);
@@ -13,7 +15,7 @@ public class MersenneNumberOrderCpuTester(GpuKernelType kernelType)
while (k <= maxK && Volatile.Read(ref isPrime)) while (k <= maxK && Volatile.Read(ref isPrime))
{ {
qCycle = MersenneDivisorCycles.GetCycle(q); qCycle = MersenneDivisorCycles.GetCycle(gpu, q);
// TODO: When this lookup misses the snapshot, invoke the configured device to compute the // TODO: When this lookup misses the snapshot, invoke the configured device to compute the
// single required cycle on demand without persisting it so the CPU order path retains the // single required cycle on demand without persisting it so the CPU order path retains the
// cycle-stepping speedups while honoring the no-extra-cache constraint for large divisors. // cycle-stepping speedups while honoring the no-extra-cache constraint for large divisors.
@@ -1,3 +1,5 @@
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core.Cpu; namespace PerfectNumbers.Core.Cpu;
public class MersenneNumberResidueCpuTester public class MersenneNumberResidueCpuTester
@@ -5,7 +7,7 @@ public class MersenneNumberResidueCpuTester
private ModResidueTracker? _mersenneResidueTracker; private ModResidueTracker? _mersenneResidueTracker;
// CPU residue variant using tracker + unrolled residue updates (no method calls in the loop). // CPU residue variant using tracker + unrolled residue updates (no method calls in the loop).
public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime) public void Scan(PrimeOrderCalculatorAccelerator gpu, ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime)
{ {
// Initialize/update Mersenne residue tracker and start a merge walk for ascending q divisors // Initialize/update Mersenne residue tracker and start a merge walk for ascending q divisors
@@ -92,7 +94,7 @@ public class MersenneNumberResidueCpuTester
// (and below) so every qualifying q reuses precomputed lengths instead of recomputing via // (and below) so every qualifying q reuses precomputed lengths instead of recomputing via
// tracker.MergeOrAppend. // tracker.MergeOrAppend.
// cycle-based quick check for small q // cycle-based quick check for small q
if (localIsPrime && tracker.MergeOrAppend(exponent, q, out isDivider) && isDivider && if (localIsPrime && tracker.MergeOrAppend(gpu, exponent, q, out isDivider) && isDivider &&
(q <= ulong.MaxValue ? ((ulong)q).IsPrimeCandidate() : q.IsPrimeCandidate())) (q <= ulong.MaxValue ? ((ulong)q).IsPrimeCandidate() : q.IsPrimeCandidate()))
{ {
localIsPrime = false; localIsPrime = false;
@@ -105,7 +107,7 @@ public class MersenneNumberResidueCpuTester
((allowMask >> (int)r10_1) & 1) != 0 && (r8_1 == 1UL || r8_1 == 7UL) && r3_1 != 0UL && r5_1 != 0UL && ((allowMask >> (int)r10_1) & 1) != 0 && (r8_1 == 1UL || r8_1 == 7UL) && r3_1 != 0UL && r5_1 != 0UL &&
localIsPrime) localIsPrime)
{ {
if (localIsPrime && tracker.MergeOrAppend(exponent, qIncremental, out isDivider) && isDivider && if (localIsPrime && tracker.MergeOrAppend(gpu, exponent, qIncremental, out isDivider) && isDivider &&
(qIncremental <= ulong.MaxValue ? ((ulong)qIncremental).IsPrimeCandidate() : qIncremental.IsPrimeCandidate())) (qIncremental <= ulong.MaxValue ? ((ulong)qIncremental).IsPrimeCandidate() : qIncremental.IsPrimeCandidate()))
{ {
localIsPrime = false; localIsPrime = false;
@@ -118,7 +120,7 @@ public class MersenneNumberResidueCpuTester
((allowMask >> (int)r10_2) & 1) != 0 && (r8_2 == 1UL || r8_2 == 7UL) && r3_2 != 0UL && r5_2 != 0UL && ((allowMask >> (int)r10_2) & 1) != 0 && (r8_2 == 1UL || r8_2 == 7UL) && r3_2 != 0UL && r5_2 != 0UL &&
localIsPrime) localIsPrime)
{ {
if (localIsPrime && tracker.MergeOrAppend(exponent, qIncremental, out isDivider) && isDivider && if (localIsPrime && tracker.MergeOrAppend(gpu, exponent, qIncremental, out isDivider) && isDivider &&
(qIncremental <= ulong.MaxValue ? ((ulong)qIncremental).IsPrimeCandidate() : qIncremental.IsPrimeCandidate())) (qIncremental <= ulong.MaxValue ? ((ulong)qIncremental).IsPrimeCandidate() : qIncremental.IsPrimeCandidate()))
{ {
localIsPrime = false; localIsPrime = false;
@@ -131,7 +133,7 @@ public class MersenneNumberResidueCpuTester
((allowMask >> (int)r10_3) & 1) != 0 && (r8_3 == 1UL || r8_3 == 7UL) && r3_3 != 0UL && r5_3 != 0UL && ((allowMask >> (int)r10_3) & 1) != 0 && (r8_3 == 1UL || r8_3 == 7UL) && r3_3 != 0UL && r5_3 != 0UL &&
localIsPrime) localIsPrime)
{ {
if (localIsPrime && tracker.MergeOrAppend(exponent, qIncremental, out isDivider) && isDivider && if (localIsPrime && tracker.MergeOrAppend(gpu, exponent, qIncremental, out isDivider) && isDivider &&
(qIncremental <= ulong.MaxValue ? ((ulong)qIncremental).IsPrimeCandidate() : qIncremental.IsPrimeCandidate())) (qIncremental <= ulong.MaxValue ? ((ulong)qIncremental).IsPrimeCandidate() : qIncremental.IsPrimeCandidate()))
{ {
localIsPrime = false; localIsPrime = false;
+7 -7
View File
@@ -57,13 +57,13 @@ public sealed class DivisorCycleCache
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); _snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
} }
public ulong GetCycleLength(ulong divisor, bool skipPrimeOrderHeuristic = false) public ulong GetCycleLength(PrimeOrderCalculatorAccelerator gpu, ulong divisor, bool skipPrimeOrderHeuristic = false)
{ {
if (!skipPrimeOrderHeuristic) if (!skipPrimeOrderHeuristic)
{ {
Span<ulong> result = stackalloc ulong[1]; Span<ulong> result = stackalloc ulong[1];
ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor }; ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor };
GetCycleLengths(singleDivisor, result); GetCycleLengths(gpu, singleDivisor, result);
return result[0]; return result[0];
} }
@@ -90,12 +90,12 @@ public sealed class DivisorCycleCache
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
cycleLength = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: true); cycleLength = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
return cycleLength; return cycleLength;
} }
public void GetCycleLengths(ReadOnlySpan<ulong> divisors, Span<ulong> cycles) public void GetCycleLengths(PrimeOrderCalculatorAccelerator gpu,ReadOnlySpan<ulong> divisors, Span<ulong> cycles)
{ {
ulong[] snapshot = _snapshot; ulong[] snapshot = _snapshot;
int length = divisors.Length; int length = divisors.Length;
@@ -139,7 +139,7 @@ public sealed class DivisorCycleCache
} }
else else
{ {
ComputeCyclesCpu(divisors, cycles, missingIndices); ComputeCyclesCpu(gpu, divisors, cycles, missingIndices);
} }
} }
@@ -187,7 +187,7 @@ public sealed class DivisorCycleCache
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ComputeCyclesCpu(ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices) private static void ComputeCyclesCpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
{ {
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
for (int i = 0; i < indices.Length; i++) for (int i = 0; i < indices.Length; i++)
@@ -195,7 +195,7 @@ public sealed class DivisorCycleCache
int targetIndex = indices[i]; int targetIndex = indices[i];
ulong divisor = divisors[targetIndex]; ulong divisor = divisors[targetIndex];
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData); cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
} }
} }
@@ -50,9 +50,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
// _isConfigured = true; // _isConfigured = true;
} }
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor)
public bool IsPrime(ulong prime, out bool divisorsExhausted, out ulong divisor)
{ {
ulong allowedMax; ulong allowedMax;
int batchCapacity; int batchCapacity;
@@ -87,7 +85,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
// GpuPrimeWorkLimiter(); // GpuPrimeWorkLimiter();
// int acceleratorIndex = AcceleratorPool.Shared.Rent(); // int acceleratorIndex = AcceleratorPool.Shared.Rent();
// var accelerator = _accelerators[acceleratorIndex]; // var accelerator = _accelerators[acceleratorIndex];
var gpu = PrimeOrderCalculatorAccelerator.Rent(batchCapacity);
var acceleratorIndex = gpu.AcceleratorIndex; var acceleratorIndex = gpu.AcceleratorIndex;
// Monitor.Enter(gpuLease.ExecutionLock); // Monitor.Enter(gpuLease.ExecutionLock);
@@ -117,7 +114,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
out processedCount); out processedCount);
ReturnBatchResources(acceleratorIndex, resources); ReturnBatchResources(acceleratorIndex, resources);
PrimeOrderCalculatorAccelerator.Return(gpu);
// Monitor.Exit(gpuLease.ExecutionLock); // Monitor.Exit(gpuLease.ExecutionLock);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
@@ -364,7 +360,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
{ {
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
{ {
return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); return MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
} }
return computedCycle; return computedCycle;
@@ -481,8 +477,8 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
{ {
private readonly MersenneNumberDivisorByDivisorGpuTester _owner; private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
private readonly PrimeOrderCalculatorAccelerator _primeTesterAccelerator;
private readonly Accelerator _accelerator; private readonly Accelerator _accelerator;
private readonly PrimeOrderCalculatorAccelerator _primeOrderCalculatorAccelerator;
private readonly AcceleratorStream _stream; private readonly AcceleratorStream _stream;
private Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> _kernel = null!; private Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> _kernel = null!;
private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> _divisorBuffer = null!; private MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> _divisorBuffer = null!;
@@ -496,32 +492,16 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
private int _capacity; private int _capacity;
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner) internal DivisorScanSession(PrimeOrderCalculatorAccelerator gpu, MersenneNumberDivisorByDivisorGpuTester owner)
{ {
_owner = owner; _owner = owner;
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
_primeTesterAccelerator = PrimeOrderCalculatorAccelerator.Rent(1); _primeOrderCalculatorAccelerator = gpu;
Accelerator accelerator = _primeTesterAccelerator.Accelerator; Accelerator accelerator = gpu.Accelerator;
_accelerator = accelerator; _accelerator = accelerator;
_stream = accelerator.CreateStream(); _stream = accelerator.CreateStream();
_capacity = Math.Max(1, owner._gpuBatchSize); _capacity = Math.Max(1, owner._gpuBatchSize);
}
internal void Reset()
{
}
private void EnsureExecutionResourcesLocked(int requiredCapacity)
{
Accelerator accelerator = _accelerator;
if (_kernel == null)
{
_kernel = KernelUtil.GetKernel(_accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>();
}
if (_divisorBuffer == null)
{
// lock (accelerator) // lock (accelerator)
{ {
_divisorBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(1); _divisorBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(1);
@@ -530,28 +510,36 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
_cycleBuffer = accelerator.Allocate1D<ulong>(1); _cycleBuffer = accelerator.Allocate1D<ulong>(1);
_firstHitBuffer = accelerator.Allocate1D<int>(1); _firstHitBuffer = accelerator.Allocate1D<int>(1);
} }
int desiredCapacity = PerfectNumberConstants.DefaultSmallPrimeFactorSlotCount;
_capacity = desiredCapacity;
// lock (accelerator)
{
_exponentsBuffer = accelerator.Allocate1D<ulong>(desiredCapacity);
_hitBuffer = accelerator.Allocate1D<byte>(desiredCapacity);
} }
bool allocateBuffers = _exponentsBuffer == null; _hostBuffer = ThreadStaticPools.UlongPool.Rent(desiredCapacity);
if (!allocateBuffers && _capacity < requiredCapacity)
_kernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<GpuDivisorPartialData>, ArrayView<int>, ArrayView<int>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>>();
}
internal void Reset()
{ {
_exponentsBuffer?.Dispose(); }
_hitBuffer?.Dispose();
if (_hostBuffer != null) private void EnsureExecutionResourcesLocked(int requiredCapacity)
{ {
if (requiredCapacity > _capacity)
{
_exponentsBuffer.Dispose();
_hitBuffer.Dispose();
ThreadStaticPools.UlongPool.Return(_hostBuffer, clearArray: false); ThreadStaticPools.UlongPool.Return(_hostBuffer, clearArray: false);
_hostBuffer = null!;
}
_exponentsBuffer = null!; Accelerator accelerator = _accelerator;
_hitBuffer = null!;
allocateBuffers = true;
}
if (allocateBuffers)
{
int desiredCapacity = requiredCapacity > _capacity ? requiredCapacity : _capacity; int desiredCapacity = requiredCapacity > _capacity ? requiredCapacity : _capacity;
_capacity = desiredCapacity; _capacity = desiredCapacity;
// lock (accelerator) // lock (accelerator)
{ {
_exponentsBuffer = accelerator.Allocate1D<ulong>(desiredCapacity); _exponentsBuffer = accelerator.Allocate1D<ulong>(desiredCapacity);
@@ -581,13 +569,12 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
ulong cycle = divisorCycle; ulong cycle = divisorCycle;
ulong firstPrime = primes[0]; ulong firstPrime = primes[0];
var gpu = _primeOrderCalculatorAccelerator;
if (cycle == 0UL) if (cycle == 0UL)
{ {
var gpu = _primeTesterAccelerator;
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL)
{ {
cycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
} }
else else
{ {
@@ -714,7 +701,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
return ComputeAllowedMaxDivisorGpu(prime, _divisorLimit); return ComputeAllowedMaxDivisorGpu(prime, _divisorLimit);
} }
public DivisorScanSession CreateDivisorSession() public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
{ {
// The tester is configured once at startup and the session pool relies on thread-safe collections, so the previous synchronization guard stays commented out. // The tester is configured once at startup and the session pool relies on thread-safe collections, so the previous synchronization guard stays commented out.
// lock (_sync) // lock (_sync)
@@ -739,8 +726,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
return session; return session;
} }
return new DivisorScanSession(this); return new DivisorScanSession(gpu, this);
} }
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
} }
@@ -1,5 +1,6 @@
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu;
@@ -86,7 +87,7 @@ public sealed class MersenneNumberDivisorGpuTester
private static (ulong divisor, uint cycle)[]? _divisorCandidates = []; private static (ulong divisor, uint cycle)[]? _divisorCandidates = [];
public bool IsPrime(ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted) public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong p, UInt128 d, ulong divisorCyclesSearchLimit, out bool divisorsExhausted)
{ {
ReadOnlyGpuUInt128 readOnlyDivisor; ReadOnlyGpuUInt128 readOnlyDivisor;
@@ -142,7 +143,7 @@ public sealed class MersenneNumberDivisorGpuTester
continue; continue;
} }
UInt128 cycle128 = MersenneDivisorCycles.GetCycle(d); UInt128 cycle128 = MersenneDivisorCycles.GetCycle(gpu, d);
if ((UInt128)p % cycle128 != UInt128.Zero) if ((UInt128)p % cycle128 != UInt128.Zero)
{ {
continue; continue;
@@ -2,6 +2,7 @@ using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
using PerfectNumbers.Core.Gpu.Accelerators;
using UInt128 = System.UInt128; using UInt128 = System.UInt128;
namespace PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu;
@@ -131,7 +132,7 @@ public class MersenneNumberLucasLehmerGpuTester
// Configurable LL slice size to keep kernels short. Default 32. // Configurable LL slice size to keep kernels short. Default 32.
public int SliceSize = 32; public int SliceSize = 32;
public bool IsPrime(ulong exponent, bool runOnGpu) public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong exponent, bool runOnGpu)
{ {
// Early rejections aligned with incremental/order sieves, but safe for small p: // Early rejections aligned with incremental/order sieves, but safe for small p:
// - If 3 | p and p != 3, then 7 | M_p -> composite. // - If 3 | p and p != 3, then 7 | M_p -> composite.
@@ -142,7 +143,7 @@ public class MersenneNumberLucasLehmerGpuTester
return false; return false;
} }
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne()) if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne(gpu))
{ {
return false; return false;
} }
@@ -8,12 +8,11 @@ namespace PerfectNumbers.Core.Gpu;
public static class Pow2MontgomeryGpuCalculator public static class Pow2MontgomeryGpuCalculator
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong CalculateKeep(ulong exponent, in MontgomeryDivisorData divisor) public static ulong CalculateKeep(PrimeOrderCalculatorAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor)
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
ulong result = 0UL; ulong result = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex; int acceleratorIndex = gpu.AcceleratorIndex;
Accelerator? accelerator = gpu.Accelerator; Accelerator? accelerator = gpu.Accelerator;
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input; MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
@@ -32,37 +31,6 @@ public static class Pow2MontgomeryGpuCalculator
AcceleratorStreamPool.Return(acceleratorIndex, stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
PrimeOrderCalculatorAccelerator.Return(gpu);
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong CalculateConvert(ulong exponent, in MontgomeryDivisorData divisor)
{
// GpuPrimeWorkLimiter.Acquire();
ulong result = 0UL;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
MemoryBuffer1D<ulong, Stride1D.Dense> resultBuffer = gpu.OutputUlong;
int acceleratorIndex = gpu.AcceleratorIndex;
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1);
// We don't need to worry about any left-overs here.
// resultBuffer.MemSetToZero(stream);
var kernelLauncher = gpu.ConvertToStandardKernelLauncher!;
kernelLauncher(stream, 1, exponentBuffer.View, divisor.Modulus, divisor.NPrime, divisor.MontgomeryOne, divisor.MontgomeryTwo, divisor.MontgomeryTwoSquared, resultBuffer.View);
resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream);
// GpuPrimeWorkLimiter.Release();
PrimeOrderCalculatorAccelerator.Return(gpu);
return result; return result;
} }
@@ -243,7 +243,7 @@ internal static partial class PrimeOrderGpuHeuristics
} }
} }
public static GpuPow2ModStatus TryPow2Mod(ulong exponent, ulong prime, out ulong remainder, in MontgomeryDivisorData divisorData) public static GpuPow2ModStatus TryPow2Mod(ulong exponent, ulong prime, out ulong remainder, in MontgomeryDivisorData? divisorData)
{ {
Span<ulong> exponents = stackalloc ulong[1]; Span<ulong> exponents = stackalloc ulong[1];
Span<ulong> remainders = stackalloc ulong[1]; Span<ulong> remainders = stackalloc ulong[1];
@@ -254,7 +254,7 @@ internal static partial class PrimeOrderGpuHeuristics
return status; return status;
} }
public static GpuPow2ModStatus TryPow2ModBatch(ReadOnlySpan<ulong> exponents, ulong prime, Span<ulong> remainders, in MontgomeryDivisorData divisorData) public static GpuPow2ModStatus TryPow2ModBatch(ReadOnlySpan<ulong> exponents, ulong prime, Span<ulong> remainders, in MontgomeryDivisorData? divisorData)
{ {
// This will never occur in production code // This will never occur in production code
// if (exponents.Length == 0) // if (exponents.Length == 0)
@@ -309,20 +309,20 @@ internal static partial class PrimeOrderGpuHeuristics
return computed ? GpuPow2ModStatus.Success : GpuPow2ModStatus.Unavailable; return computed ? GpuPow2ModStatus.Success : GpuPow2ModStatus.Unavailable;
} }
public static GpuPow2ModStatus TryPow2Mod(in UInt128 exponent, in UInt128 prime, out UInt128 remainder) public static GpuPow2ModStatus TryPow2Mod(PrimeOrderCalculatorAccelerator gpu, in UInt128 exponent, in UInt128 prime, out UInt128 remainder)
{ {
Span<UInt128> exponents = stackalloc UInt128[1]; Span<UInt128> exponents = stackalloc UInt128[1];
Span<UInt128> remainders = stackalloc UInt128[1]; Span<UInt128> remainders = stackalloc UInt128[1];
exponents[0] = exponent; exponents[0] = exponent;
GpuPow2ModStatus status = TryPow2ModBatch(exponents, prime, remainders); GpuPow2ModStatus status = TryPow2ModBatch(gpu, exponents, prime, remainders);
remainder = remainders[0]; remainder = remainders[0];
return status; return status;
} }
public static GpuPow2ModStatus TryPow2ModBatch(ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> remainders) public static GpuPow2ModStatus TryPow2ModBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> remainders)
{ {
return TryPow2ModBatchInternal(exponents, prime, remainders); return TryPow2ModBatchInternal(gpu, exponents, prime, remainders);
} }
internal static bool TryCalculateOrder( internal static bool TryCalculateOrder(
@@ -471,7 +471,7 @@ internal static partial class PrimeOrderGpuHeuristics
return kernel; return kernel;
} }
private static GpuPow2ModStatus TryPow2ModBatchInternal(ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> remainders) private static GpuPow2ModStatus TryPow2ModBatchInternal(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> remainders)
{ {
if (exponents.Length == 0) if (exponents.Length == 0)
{ {
@@ -509,11 +509,11 @@ internal static partial class PrimeOrderGpuHeuristics
} }
} }
ComputeOnGpuWide(exponents, prime, target); ComputeOnGpuWide(gpu, exponents, prime, target);
return GpuPow2ModStatus.Success; return GpuPow2ModStatus.Success;
} }
private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results) private static bool TryComputeOnGpu(ReadOnlySpan<ulong> exponents, ulong prime, MontgomeryDivisorData? divisorData, Span<ulong> results)
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
int acceleratorIndex = AcceleratorPool.Shared.Rent(); int acceleratorIndex = AcceleratorPool.Shared.Rent();
@@ -540,6 +540,11 @@ internal static partial class PrimeOrderGpuHeuristics
try try
{ {
if (divisorData is null)
{
divisorData = MontgomeryDivisorData.Empty;
}
kernelLauncher(stream, exponents.Length, exponentBuffer.View, divisorData.Modulus, divisorData.NPrime, divisorData.MontgomeryOne, divisorData.MontgomeryTwo, divisorData.MontgomeryTwoSquared, remainderBuffer.View); kernelLauncher(stream, exponents.Length, exponentBuffer.View, divisorData.Modulus, divisorData.NPrime, divisorData.MontgomeryOne, divisorData.MontgomeryTwo, divisorData.MontgomeryTwoSquared, remainderBuffer.View);
remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(results), exponents.Length); remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(results), exponents.Length);
@@ -573,14 +578,13 @@ internal static partial class PrimeOrderGpuHeuristics
} }
} }
private static void ComputeOnGpuWide(ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> results) private static void ComputeOnGpuWide(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<UInt128> exponents, UInt128 prime, Span<UInt128> results)
{ {
int length = exponents.Length; int length = exponents.Length;
GpuUInt128[]? rentedExponents = null; GpuUInt128[]? rentedExponents = null;
GpuUInt128[]? rentedResults = null; GpuUInt128[]? rentedResults = null;
ArrayPool<GpuUInt128> gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool; ArrayPool<GpuUInt128> gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool;
PrimeOrderCalculatorAccelerator? gpu = null;
bool poolingRequired = length > WideStackThreshold; bool poolingRequired = length > WideStackThreshold;
Span<GpuUInt128> exponentSpan = poolingRequired Span<GpuUInt128> exponentSpan = poolingRequired
? stackalloc GpuUInt128[length] ? stackalloc GpuUInt128[length]
@@ -596,7 +600,6 @@ internal static partial class PrimeOrderGpuHeuristics
} }
GpuUInt128 modulus = (GpuUInt128)prime; GpuUInt128 modulus = (GpuUInt128)prime;
gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex; int acceleratorIndex = gpu.AcceleratorIndex;
Accelerator accelerator = gpu.Accelerator; Accelerator accelerator = gpu.Accelerator;
@@ -625,7 +628,6 @@ internal static partial class PrimeOrderGpuHeuristics
remainderBuffer.Dispose(); remainderBuffer.Dispose();
exponentBuffer.Dispose(); exponentBuffer.Dispose();
PrimeOrderCalculatorAccelerator.Return(gpu);
if (rentedExponents is not null) if (rentedExponents is not null)
{ {
@@ -168,15 +168,15 @@ public sealed class HeuristicCombinedPrimeTester
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10); return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10);
} }
public static bool IsPrimeGpu(ulong n) // public static bool IsPrimeGpu(ulong n)
{ // {
byte nMod10 = (byte)n.Mod10(); // byte nMod10 = (byte)n.Mod10();
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n); // ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1); // var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
bool isPrime = IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10); // bool isPrime = IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10);
PrimeOrderCalculatorAccelerator.Return(gpu); // PrimeOrderCalculatorAccelerator.Return(gpu);
return isPrime; // return isPrime;
} // }
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n) public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
{ {
@@ -520,7 +520,7 @@ public sealed class HeuristicCombinedPrimeTester
} }
primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL; primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL;
ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
cycleFromHint = false; cycleFromHint = false;
cycleComputed = resolvedCycle != 0UL; cycleComputed = resolvedCycle != 0UL;
return resolvedCycle; return resolvedCycle;
+7 -9
View File
@@ -83,17 +83,17 @@ public sealed class HeuristicPrimeTester
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeGpu(ulong n) public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
{ {
byte nMod10 = (byte)n.Mod10(); byte nMod10 = (byte)n.Mod10();
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n); ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
return IsPrimeGpu(n, maxDivisorSquare, nMod10); return IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10) => HeuristicIsPrimeGpuCore(n, maxDivisorSquare, nMod10); public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10) => HeuristicIsPrimeGpuCore(gpu, n, maxDivisorSquare, nMod10);
private static bool HeuristicIsPrimeGpuCore(ulong n, ulong maxDivisorSquare, byte nMod10) private static bool HeuristicIsPrimeGpuCore(PrimeOrderCalculatorAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10)
{ {
// TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner? // TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner?
if (maxDivisorSquare < 9UL) if (maxDivisorSquare < 9UL)
@@ -106,7 +106,7 @@ public sealed class HeuristicPrimeTester
// return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB: false); // return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB: false);
// } // }
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10); bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(gpu, n, maxDivisorSquare, nMod10);
return !compositeDetected; return !compositeDetected;
} }
@@ -239,10 +239,9 @@ public sealed class HeuristicPrimeTester
} }
private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10) private static bool HeuristicTrialDivisionGpuDetectsDivisor(PrimeOrderCalculatorAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10)
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex; int acceleratorIndex = gpu.AcceleratorIndex;
var stream = AcceleratorStreamPool.Rent(acceleratorIndex); var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
var flagView1D = gpu.OutputInt!.View; var flagView1D = gpu.OutputInt!.View;
@@ -313,7 +312,6 @@ public sealed class HeuristicPrimeTester
// } // }
AcceleratorStreamPool.Return(acceleratorIndex, stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
PrimeOrderCalculatorAccelerator.Return(gpu);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
return compositeDetected; return compositeDetected;
} }
@@ -487,7 +485,7 @@ public sealed class HeuristicPrimeTester
} }
primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL; primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL;
ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
cycleFromHint = false; cycleFromHint = false;
cycleComputed = resolvedCycle != 0UL; cycleComputed = resolvedCycle != 0UL;
return resolvedCycle; return resolvedCycle;
@@ -1,3 +1,5 @@
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
public interface IMersenneNumberDivisorByDivisorTester public interface IMersenneNumberDivisorByDivisorTester
@@ -10,9 +12,9 @@ public interface IMersenneNumberDivisorByDivisorTester
ulong GetAllowedMaxDivisor(ulong prime); ulong GetAllowedMaxDivisor(ulong prime);
bool IsPrime(ulong prime, out bool divisorsExhausted, out ulong divisor); bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor);
IDivisorScanSession CreateDivisorSession(); IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu);
void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues); void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues);
+14 -9
View File
@@ -75,7 +75,7 @@ public class MersenneDivisorCycles
} }
} }
if (TryCalculateCycleLengthHeuristic(divisor, divisorData, out ulong heuristicCycle) && heuristicCycle != 0UL) if (TryCalculateCycleLengthHeuristic(gpu, divisor, divisorData, out ulong heuristicCycle) && heuristicCycle != 0UL)
{ {
return heuristicCycle == exponent; return heuristicCycle == exponent;
} }
@@ -163,7 +163,7 @@ public class MersenneDivisorCycles
} }
} }
public ulong GetCycle(ulong divisor, in MontgomeryDivisorData divisorData) public ulong GetCycle(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData)
{ {
// Fast-path: in-memory array for small divisors // Fast-path: in-memory array for small divisors
if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles) if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles)
@@ -208,10 +208,10 @@ public class MersenneDivisorCycles
// TODO: Route this miss to an ephemeral single-cycle computation on the device selected by the current // TODO: Route this miss to an ephemeral single-cycle computation on the device selected by the current
// configuration (and skip persisting the result) when the divisor falls outside the in-memory snapshot // configuration (and skip persisting the result) when the divisor falls outside the in-memory snapshot
// so we respect the large-p memory limits and avoid extra cache locks. // so we respect the large-p memory limits and avoid extra cache locks.
return CalculateCycleLength(divisor, divisorData); return CalculateCycleLength(gpu, divisor, divisorData);
} }
public static UInt128 GetCycle(UInt128 divisor) public static UInt128 GetCycle(PrimeOrderCalculatorAccelerator gpu, UInt128 divisor)
{ {
// For divisor = 2^k, cycle is 1 // For divisor = 2^k, cycle is 1
if ((divisor & (divisor - UInt128.One)) == UInt128.Zero) if ((divisor & (divisor - UInt128.One)) == UInt128.Zero)
@@ -224,12 +224,13 @@ public class MersenneDivisorCycles
ulong divisor64 = (ulong)divisor; ulong divisor64 = (ulong)divisor;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64);
ulong cycle = CalculateCycleLength(divisor64, divisorData); ulong cycle = CalculateCycleLength(gpu, divisor64, divisorData);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
return (UInt128)cycle; return (UInt128)cycle;
} }
UInt128 wideOrder = PrimeOrderCalculator.Calculate( UInt128 wideOrder = PrimeOrderCalculator.Calculate(
gpu,
divisor, divisor,
previousOrder: null, previousOrder: null,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
@@ -285,6 +286,7 @@ public class MersenneDivisorCycles
// } // }
ulong computedOrder = PrimeOrderCalculator.Calculate( ulong computedOrder = PrimeOrderCalculator.Calculate(
gpu,
divisor, divisor,
previousOrder: null, previousOrder: null,
divisorData, divisorData,
@@ -743,6 +745,7 @@ public class MersenneDivisorCycles
// (ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength)); // (ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength));
int localCycleIndex = 0; int localCycleIndex = 0;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (ulong divisor = localStart; divisor <= localEnd; divisor++) for (ulong divisor = localStart; divisor <= localEnd; divisor++)
{ {
if ((divisor & 1UL) == 0UL) if ((divisor & 1UL) == 0UL)
@@ -762,11 +765,12 @@ public class MersenneDivisorCycles
// unrolled-hex calculator so the CPU generator matches // unrolled-hex calculator so the CPU generator matches
// the GPU benchmark leader for large divisors. // the GPU benchmark leader for large divisors.
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor); MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
ulong cycle = CalculateCycleLength(divisor, divisorData); ulong cycle = CalculateCycleLength(gpu, divisor, divisorData);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
localCycles[localCycleIndex++] = (divisor, cycle); localCycles[localCycleIndex++] = (divisor, cycle);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
if (localCycleIndex == 0) if (localCycleIndex == 0)
{ {
ThreadStaticPools.Return(localCycles); ThreadStaticPools.Return(localCycles);
@@ -929,9 +933,9 @@ public class MersenneDivisorCycles
public static ulong CalculateCycleLengthGpu(ulong divisor) => DivisorCycleKernels.CalculateCycleLengthGpu(divisor); public static ulong CalculateCycleLengthGpu(ulong divisor) => DivisorCycleKernels.CalculateCycleLengthGpu(divisor);
public static ulong CalculateCycleLength(ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false) public static ulong CalculateCycleLength(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false)
{ {
if (TryCalculateCycleLengthHeuristic(divisor, divisorData, out ulong cycleLength, skipPrimeOrderHeuristic)) if (TryCalculateCycleLengthHeuristic(gpu, divisor, divisorData, out ulong cycleLength, skipPrimeOrderHeuristic))
return cycleLength; return cycleLength;
return CalculateCycleLengthFallback(divisor); return CalculateCycleLengthFallback(divisor);
@@ -940,7 +944,7 @@ public class MersenneDivisorCycles
// [ThreadStatic] // [ThreadStatic]
// private static HeuristicPrimeTester? _primeTester; // private static HeuristicPrimeTester? _primeTester;
internal static bool TryCalculateCycleLengthHeuristic(ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false) internal static bool TryCalculateCycleLengthHeuristic(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false)
{ {
if ((divisor & (divisor - 1UL)) == 0UL) if ((divisor & (divisor - 1UL)) == 0UL)
{ {
@@ -966,6 +970,7 @@ public class MersenneDivisorCycles
// if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor)) // if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor))
{ {
ulong computedOrder = PrimeOrderCalculator.Calculate( ulong computedOrder = PrimeOrderCalculator.Calculate(
gpu,
divisor, divisor,
previousOrder: null, previousOrder: null,
divisorData, divisorData,
@@ -239,10 +239,10 @@ public static class MersenneNumberDivisorByDivisorTester
int chunkSize = primesPerTask <= 0 ? 1 : primesPerTask; int chunkSize = primesPerTask <= 0 ? 1 : primesPerTask;
void ProcessPrime(ulong prime) void ProcessPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime)
{ {
Console.WriteLine($"Processing {prime}"); Console.WriteLine($"Processing {prime}");
bool isPrime = tester.IsPrime(prime, out bool divisorsExhausted, out ulong divisor); bool isPrime = tester.IsPrime(gpu, prime, out bool divisorsExhausted, out ulong divisor);
if (!isPrime) if (!isPrime)
{ {
@@ -259,10 +259,13 @@ public static class MersenneNumberDivisorByDivisorTester
if (workerCount == 1) if (workerCount == 1)
{ {
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
foreach (ulong prime in filteredPrimes) foreach (ulong prime in filteredPrimes)
{ {
ProcessPrime(prime); ProcessPrime(gpu, prime);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
else else
{ {
@@ -295,11 +298,13 @@ public static class MersenneNumberDivisorByDivisorTester
startGate.Wait(); startGate.Wait();
Console.WriteLine($"Task started for range {rangeStart}"); Console.WriteLine($"Task started for range {rangeStart}");
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
for (int index = rangeStart; index < rangeEnd; index++) for (int index = rangeStart; index < rangeEnd; index++)
{ {
ProcessPrime(filteredPrimes[index]); ProcessPrime(gpu, filteredPrimes[index]);
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
Console.WriteLine($"Task finished for range {rangeStart}"); Console.WriteLine($"Task finished for range {rangeStart}");
PrimeOrderCalculatorAccelerator.DisposeAll(); PrimeOrderCalculatorAccelerator.DisposeAll();
}, },
+13 -10
View File
@@ -63,6 +63,7 @@ public sealed class MersenneNumberTester(
UInt128 twoP = (UInt128)exponent << 1; // 2 * p UInt128 twoP = (UInt128)exponent << 1; // 2 * p
LastDigit lastDigit = (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One; LastDigit lastDigit = (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
// CPU path: compute orders directly and cache // CPU path: compute orders directly and cache
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
if (!_useGpuOrder) if (!_useGpuOrder)
{ {
UInt128 k = UInt128.One; UInt128 k = UInt128.One;
@@ -97,10 +98,11 @@ public sealed class MersenneNumberTester(
continue; continue;
} }
ulong ord = q.CalculateOrder(); ulong ord = q.CalculateOrder(gpu);
_orderCache[q] = ord; _orderCache[q] = ord;
} }
PrimeOrderCalculatorAccelerator.Return(gpu);
return; return;
} }
@@ -190,7 +192,7 @@ public sealed class MersenneNumberTester(
ulong order = orders[i]; ulong order = orders[i];
if (order == 0UL) if (order == 0UL)
{ {
order = qs[offset + i].CalculateOrder(); order = qs[offset + i].CalculateOrder(gpu);
} }
if (useOrderCache) if (useOrderCache)
@@ -205,11 +207,12 @@ public sealed class MersenneNumberTester(
} }
AcceleratorStreamPool.Return(acceleratorIndex,stream); AcceleratorStreamPool.Return(acceleratorIndex,stream);
PrimeOrderCalculatorAccelerator.Return(gpu);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
uInt128Pool.Return(qs); uInt128Pool.Return(qs);
} }
public bool IsMersennePrime(ulong exponent) public bool IsMersennePrime(PrimeOrderCalculatorAccelerator gpu, ulong exponent)
{ {
// Safe early rejections using known orders for tiny primes: // Safe early rejections using known orders for tiny primes:
// 7 | M_p iff 3 | p. Avoid rejecting p == 3 where M_3 == 7 is prime. // 7 | M_p iff 3 | p. Avoid rejecting p == 3 where M_3 == 7 is prime.
@@ -220,7 +223,7 @@ public sealed class MersenneNumberTester(
return false; return false;
} }
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne()) if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne(gpu))
{ {
return false; return false;
} }
@@ -251,7 +254,7 @@ public sealed class MersenneNumberTester(
} }
else else
{ {
_residueCpuTester!.Scan(exponent, twoP, lastDigit, maxK, ref prePrime); _residueCpuTester!.Scan(gpu, exponent, twoP, lastDigit, maxK, ref prePrime);
} }
return prePrime; return prePrime;
@@ -270,7 +273,7 @@ public sealed class MersenneNumberTester(
} }
else else
{ {
_incrementalCpuTester.Scan(exponent, twoPPre, lastDigitPre, maxKPre, ref prePrime); _incrementalCpuTester.Scan(gpu, exponent, twoPPre, lastDigitPre, maxKPre, ref prePrime);
} }
if (!prePrime) if (!prePrime)
@@ -279,8 +282,8 @@ public sealed class MersenneNumberTester(
} }
return _useGpuLucas return _useGpuLucas
? _lucasLehmerGpuTester.IsPrime(exponent, runOnGpu: true) ? _lucasLehmerGpuTester.IsPrime(gpu, exponent, runOnGpu: true)
: _lucasLehmerCpuTester.IsPrime(exponent); : _lucasLehmerCpuTester.IsPrime(gpu, exponent);
} }
bool isPrime = true; bool isPrime = true;
@@ -293,7 +296,7 @@ public sealed class MersenneNumberTester(
} }
else else
{ {
_orderCpuTester.Scan(exponent, twoP, lastDigit, maxK, ref isPrime); _orderCpuTester.Scan(gpu, exponent, twoP, lastDigit, maxK, ref isPrime);
} }
} }
else else
@@ -304,7 +307,7 @@ public sealed class MersenneNumberTester(
} }
else else
{ {
_incrementalCpuTester.Scan(exponent, twoP, lastDigit, maxK, ref isPrime); _incrementalCpuTester.Scan(gpu, exponent, twoP, lastDigit, maxK, ref isPrime);
} }
} }
+8 -8
View File
@@ -1,5 +1,5 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using PerfectNumbers.Core.Gpu.Accelerators;
using UInt128 = System.UInt128; using UInt128 = System.UInt128;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -39,7 +39,7 @@ public sealed class ModResidueTracker
/// (number must be non-decreasing across calls). Adds and initializes new /// (number must be non-decreasing across calls). Adds and initializes new
/// divisors on first use. /// divisors on first use.
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsDivisible(UInt128 number, UInt128 divisor) public bool IsDivisible(PrimeOrderCalculatorAccelerator gpu, UInt128 number, UInt128 divisor)
{ {
if (divisor == UInt128.Zero) if (divisor == UInt128.Zero)
{ {
@@ -52,7 +52,7 @@ public sealed class ModResidueTracker
int idx = LowerBound(divisors, divisor); int idx = LowerBound(divisors, divisor);
if (idx == divisors.Count || divisors[idx] != divisor) if (idx == divisors.Count || divisors[idx] != divisor)
{ {
UInt128 residue = ComputeInitialResidue(number, divisor); UInt128 residue = ComputeInitialResidue(gpu, number, divisor);
divisors.Insert(idx, divisor); divisors.Insert(idx, divisor);
_residues.Insert(idx, residue); _residues.Insert(idx, residue);
if (_hasLastAppended && idx == divisors.Count - 1 && divisor >= _lastAppended) if (_hasLastAppended && idx == divisors.Count - 1 && divisor >= _lastAppended)
@@ -81,7 +81,7 @@ public sealed class ModResidueTracker
// Merge or append current candidate divisor (ascending order required across calls). // Merge or append current candidate divisor (ascending order required across calls).
// Returns true and sets 'divisible' based on residue at 'number'. // Returns true and sets 'divisible' based on residue at 'number'.
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MergeOrAppend(UInt128 number, UInt128 divisor, out bool divisible) public bool MergeOrAppend(PrimeOrderCalculatorAccelerator gpu, UInt128 number, UInt128 divisor, out bool divisible)
{ {
// Assume number is non-decreasing; advance residues lazily when number changes. // Assume number is non-decreasing; advance residues lazily when number changes.
List<UInt128> divisors = _divisors; List<UInt128> divisors = _divisors;
@@ -97,7 +97,7 @@ public sealed class ModResidueTracker
List<UInt128> residues = _residues; List<UInt128> residues = _residues;
if (divisors.Count == 0 || (_hasLastAppended && divisor >= _lastAppended && divisors[^1] <= divisor)) if (divisors.Count == 0 || (_hasLastAppended && divisor >= _lastAppended && divisors[^1] <= divisor))
{ {
residue = ComputeInitialResidue(number, divisor); residue = ComputeInitialResidue(gpu, number, divisor);
divisors.Add(divisor); divisors.Add(divisor);
residues.Add(residue); residues.Add(residue);
_hasLastAppended = true; _hasLastAppended = true;
@@ -123,7 +123,7 @@ public sealed class ModResidueTracker
// Insert at cursor to keep order // Insert at cursor to keep order
residue = ComputeInitialResidue(number, divisor); residue = ComputeInitialResidue(gpu, number, divisor);
divisors.Insert(cursor, divisor); divisors.Insert(cursor, divisor);
residues.Insert(cursor, residue); residues.Insert(cursor, residue);
divisible = residue == UInt128.Zero; divisible = residue == UInt128.Zero;
@@ -238,7 +238,7 @@ public sealed class ModResidueTracker
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private UInt128 ComputeInitialResidue(UInt128 number, UInt128 divisor) private UInt128 ComputeInitialResidue(PrimeOrderCalculatorAccelerator gpu, UInt128 number, UInt128 divisor)
{ {
if (_model == ResidueModel.Identity) if (_model == ResidueModel.Identity)
{ {
@@ -266,7 +266,7 @@ public sealed class ModResidueTracker
if (pow != UInt128.One) if (pow != UInt128.One)
return (pow - UInt128.One) % divisor; return (pow - UInt128.One) % divisor;
ulong ord = divisor.CalculateOrder(); ulong ord = divisor.CalculateOrder(gpu);
if (ord == 0UL || number % (UInt128)ord != UInt128.Zero) if (ord == 0UL || number % (UInt128)ord != UInt128.Zero)
return UInt128.One; // does NOT divide return UInt128.One; // does NOT divide
@@ -44,7 +44,7 @@ internal static partial class PrimeOrderCalculator
} }
} }
private static ulong CalculateInternal(ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config) private static ulong CalculateInternal(PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
{ {
// TODO: Is this condition ever met on EvenPerfectBitScanner's execution path? If not, we can add a clarification comment and comment out the entire block. We want to support p candidates at least greater or equal to 31. // TODO: Is this condition ever met on EvenPerfectBitScanner's execution path? If not, we can add a clarification comment and comment out the entire block. We want to support p candidates at least greater or equal to 31.
if (prime <= 3UL) if (prime <= 3UL)
@@ -54,10 +54,8 @@ internal static partial class PrimeOrderCalculator
ulong phi = prime - 1UL; ulong phi = prime - 1UL;
PrimeOrderCalculatorAccelerator gpu = PrimeOrderCalculatorAccelerator.Rent(1);
if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(gpu, prime, previousOrder, config, divisorData, out ulong gpuOrder)) if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(gpu, prime, previousOrder, config, divisorData, out ulong gpuOrder))
{ {
PrimeOrderCalculatorAccelerator.Return(gpu);
return gpuOrder; return gpuOrder;
} }
@@ -73,13 +71,11 @@ internal static partial class PrimeOrderCalculator
// result = CalculateByFactorizationCpu(gpu, prime, divisorData); // result = CalculateByFactorizationCpu(gpu, prime, divisorData);
phiFactors.Dispose(); phiFactors.Dispose();
PrimeOrderCalculatorAccelerator.Return(gpu);
return result; return result;
} }
result = RunHeuristicPipelineCpu(gpu, prime, previousOrder, config, divisorData, phi, phiFactors); result = RunHeuristicPipelineCpu(gpu, prime, previousOrder, config, divisorData, phi, phiFactors);
phiFactors.Dispose(); phiFactors.Dispose();
PrimeOrderCalculatorAccelerator.Return(gpu);
return result; return result;
} }
@@ -2,6 +2,7 @@ using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
using System.Numerics; using System.Numerics;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -82,7 +83,7 @@ internal static partial class PrimeOrderCalculator
Console.WriteLine(messageFactory()); Console.WriteLine(messageFactory());
} }
} }
private static UInt128 CalculateWideInternal(in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData? divisorData, in PrimeOrderSearchConfig config) private static UInt128 CalculateWideInternal(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData? divisorData, in PrimeOrderSearchConfig config)
{ {
if (prime <= UInt128.One) if (prime <= UInt128.One)
{ {
@@ -100,11 +101,11 @@ internal static partial class PrimeOrderCalculator
UInt128 result; UInt128 result;
if (phiFactors.Factors is null) if (phiFactors.Factors is null)
{ {
result = FinishStrictlyWide(prime, divisorData); result = FinishStrictlyWide(gpu, prime, divisorData);
} }
else else
{ {
result = RunHeuristicPipelineWide(prime, previousOrder, divisorData, config, phi, phiFactors); result = RunHeuristicPipelineWide(gpu, prime, previousOrder, divisorData, config, phi, phiFactors);
} }
debugScope.Dispose(); debugScope.Dispose();
@@ -112,32 +113,33 @@ internal static partial class PrimeOrderCalculator
} }
private static UInt128 RunHeuristicPipelineWide( private static UInt128 RunHeuristicPipelineWide(
PrimeOrderCalculatorAccelerator gpu,
in UInt128 prime, in UInt128 prime,
in UInt128? previousOrder, in UInt128? previousOrder,
in MontgomeryDivisorData divisorData, in MontgomeryDivisorData? divisorData,
in PrimeOrderSearchConfig config, in PrimeOrderSearchConfig config,
in UInt128 phi, in UInt128 phi,
in PartialFactorResult128 phiFactors) in PartialFactorResult128 phiFactors)
{ {
if (phiFactors.FullyFactored && TrySpecialMaxWide(phi, prime, divisorData, phiFactors)) if (phiFactors.FullyFactored && TrySpecialMaxWide(gpu, phi, prime, divisorData, phiFactors))
{ {
return phi; return phi;
} }
UInt128 candidateOrder = InitializeStartingOrderWide(prime, phi, divisorData); UInt128 candidateOrder = InitializeStartingOrderWide(gpu, prime, phi, divisorData);
candidateOrder = ExponentLoweringWide(candidateOrder, prime, divisorData, phiFactors); candidateOrder = ExponentLoweringWide(gpu, candidateOrder, prime, divisorData, phiFactors);
if (TryConfirmOrderWide(prime, candidateOrder, divisorData, config)) if (TryConfirmOrderWide(gpu, prime, candidateOrder, divisorData, config))
{ {
return candidateOrder; return candidateOrder;
} }
if (config.Mode == PrimeOrderMode.Strict) if (config.Mode == PrimeOrderMode.Strict)
{ {
return FinishStrictlyWide(prime, divisorData); return FinishStrictlyWide(gpu, prime, divisorData);
} }
if (TryHeuristicFinishWide(prime, candidateOrder, previousOrder, divisorData, config, out UInt128 order)) if (TryHeuristicFinishWide(gpu, prime, candidateOrder, previousOrder, divisorData, config, out UInt128 order))
{ {
return order; return order;
} }
@@ -145,12 +147,12 @@ internal static partial class PrimeOrderCalculator
return candidateOrder; return candidateOrder;
} }
private static UInt128 FinishStrictlyWide(in UInt128 prime, in MontgomeryDivisorData divisorData) private static UInt128 FinishStrictlyWide(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in MontgomeryDivisorData? divisorData)
{ {
return CalculateByFactorizationWide(prime, divisorData); return CalculateByFactorizationWide(gpu, prime, divisorData);
} }
private static bool TrySpecialMaxWide(in UInt128 phi, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors) private static bool TrySpecialMaxWide(PrimeOrderCalculatorAccelerator gpu, in UInt128 phi, in UInt128 prime, in MontgomeryDivisorData? divisorData, in PartialFactorResult128 factors)
{ {
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors; ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
int length = factors.Count; int length = factors.Count;
@@ -158,7 +160,7 @@ internal static partial class PrimeOrderCalculator
{ {
UInt128 factor = factorSpan[i].Value; UInt128 factor = factorSpan[i].Value;
UInt128 reduced = phi / factor; UInt128 reduced = phi / factor;
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One) if (Pow2ModWide(gpu, reduced, prime, divisorData) == UInt128.One)
{ {
return false; return false;
} }
@@ -167,14 +169,14 @@ internal static partial class PrimeOrderCalculator
return true; return true;
} }
private static UInt128 InitializeStartingOrderWide(in UInt128 prime, in UInt128 phi, in MontgomeryDivisorData divisorData) private static UInt128 InitializeStartingOrderWide(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 phi, in MontgomeryDivisorData? divisorData)
{ {
UInt128 order = phi; UInt128 order = phi;
UInt128 mod8 = prime & (UInt128)7UL; UInt128 mod8 = prime & (UInt128)7UL;
if (mod8 == UInt128.One || mod8 == (UInt128)7UL) if (mod8 == UInt128.One || mod8 == (UInt128)7UL)
{ {
UInt128 half = phi >> 1; UInt128 half = phi >> 1;
if (Pow2ModWide(half, prime, divisorData) == UInt128.One) if (Pow2ModWide(gpu, half, prime, divisorData) == UInt128.One)
{ {
order = half; order = half;
} }
@@ -183,7 +185,7 @@ internal static partial class PrimeOrderCalculator
return order; return order;
} }
private static UInt128 ExponentLoweringWide(UInt128 order, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors) private static UInt128 ExponentLoweringWide(PrimeOrderCalculatorAccelerator gpu, UInt128 order, in UInt128 prime, in MontgomeryDivisorData divisorData, in PartialFactorResult128 factors)
{ {
ArrayPool<FactorEntry128> pool = ThreadStaticPools.FactorEntry128Pool; ArrayPool<FactorEntry128> pool = ThreadStaticPools.FactorEntry128Pool;
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors; ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
@@ -215,7 +217,7 @@ internal static partial class PrimeOrderCalculator
if ((order % primeFactor) == UInt128.Zero) if ((order % primeFactor) == UInt128.Zero)
{ {
UInt128 reduced = order / primeFactor; UInt128 reduced = order / primeFactor;
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One) if (Pow2ModWide(gpu, reduced, prime, divisorData) == UInt128.One)
{ {
order = reduced; order = reduced;
continue; continue;
@@ -230,14 +232,14 @@ internal static partial class PrimeOrderCalculator
return order; return order;
} }
private static bool TryConfirmOrderWide(in UInt128 prime, in UInt128 order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config) private static bool TryConfirmOrderWide(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 order, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
{ {
if (order == UInt128.Zero) if (order == UInt128.Zero)
{ {
return false; return false;
} }
if (Pow2ModWide(order, prime, divisorData) != UInt128.One) if (Pow2ModWide(gpu, order, prime, divisorData) != UInt128.One)
{ {
return false; return false;
} }
@@ -277,7 +279,7 @@ internal static partial class PrimeOrderCalculator
} }
reduced /= primeFactor; reduced /= primeFactor;
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One) if (Pow2ModWide(gpu, reduced, prime, divisorData) == UInt128.One)
{ {
return false; return false;
} }
@@ -288,6 +290,7 @@ internal static partial class PrimeOrderCalculator
} }
private static bool TryHeuristicFinishWide( private static bool TryHeuristicFinishWide(
PrimeOrderCalculatorAccelerator gpu,
in UInt128 prime, in UInt128 prime,
in UInt128 order, in UInt128 order,
in UInt128? previousOrder, in UInt128? previousOrder,
@@ -347,12 +350,12 @@ internal static partial class PrimeOrderCalculator
UInt128 candidate = candidates[i]; UInt128 candidate = candidates[i];
powUsed++; powUsed++;
if (Pow2ModWide(candidate, prime, divisorData) != UInt128.One) if (Pow2ModWide(gpu, candidate, prime, divisorData) != UInt128.One)
{ {
continue; continue;
} }
if (!TryConfirmCandidateWide(prime, candidate, config, ref powUsed, powBudget, divisorData)) if (!TryConfirmCandidateWide(gpu, prime, candidate, config, ref powUsed, powBudget, divisorData))
{ {
continue; continue;
} }
@@ -536,7 +539,7 @@ internal static partial class PrimeOrderCalculator
} }
} }
private static bool TryConfirmCandidateWide(in UInt128 prime, in UInt128 candidate, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData divisorData) private static bool TryConfirmCandidateWide(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128 candidate, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData divisorData)
{ {
PartialFactorResult128 factorization = PartialFactorWide(candidate, config); PartialFactorResult128 factorization = PartialFactorWide(candidate, config);
if (factorization.Factors is null) if (factorization.Factors is null)
@@ -579,7 +582,7 @@ internal static partial class PrimeOrderCalculator
} }
powUsed++; powUsed++;
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One) if (Pow2ModWide(gpu, reduced, prime, divisorData) == UInt128.One)
{ {
return false; return false;
} }
@@ -855,7 +858,7 @@ internal static partial class PrimeOrderCalculator
return 64 + BitOperations.TrailingZeroCount(high); return 64 + BitOperations.TrailingZeroCount(high);
} }
private static UInt128 CalculateByFactorizationWide(in UInt128 prime, in MontgomeryDivisorData divisorData) private static UInt128 CalculateByFactorizationWide(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in MontgomeryDivisorData? divisorData)
{ {
UInt128 phi = prime - UInt128.One; UInt128 phi = prime - UInt128.One;
Dictionary<UInt128, int> counts = new(capacity: 8); Dictionary<UInt128, int> counts = new(capacity: 8);
@@ -879,7 +882,7 @@ internal static partial class PrimeOrderCalculator
if ((order % primeFactor) != UInt128.Zero) if ((order % primeFactor) != UInt128.Zero)
{ {
UInt128 candidate = order / primeFactor; UInt128 candidate = order / primeFactor;
if (Pow2ModWide(candidate, prime, divisorData) == UInt128.One) if (Pow2ModWide(gpu, candidate, prime, divisorData) == UInt128.One)
{ {
order = candidate; order = candidate;
} }
@@ -1026,7 +1029,7 @@ internal static partial class PrimeOrderCalculator
return true; return true;
} }
private static UInt128 Pow2ModWide(in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData divisorData) private static UInt128 Pow2ModWide(PrimeOrderCalculatorAccelerator gpu, in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData? divisorData)
{ {
if (modulus == UInt128.One) if (modulus == UInt128.One)
{ {
@@ -1046,7 +1049,7 @@ internal static partial class PrimeOrderCalculator
} }
} }
GpuPow2ModStatus wideStatus = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, modulus, out UInt128 wideRemainder); GpuPow2ModStatus wideStatus = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, modulus, out UInt128 wideRemainder);
if (wideStatus == GpuPow2ModStatus.Success) if (wideStatus == GpuPow2ModStatus.Success)
{ {
return wideRemainder; return wideRemainder;
+6 -3
View File
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -48,6 +49,7 @@ internal static partial class PrimeOrderCalculator
// TODO: Remove branching on the CPU / GPU paths // TODO: Remove branching on the CPU / GPU paths
// TODO: Split big non-static functions into smaller / extract static code to limit JIT time // TODO: Split big non-static functions into smaller / extract static code to limit JIT time
public static ulong Calculate( public static ulong Calculate(
PrimeOrderCalculatorAccelerator gpu,
ulong prime, ulong prime,
ulong? previousOrder, ulong? previousOrder,
in MontgomeryDivisorData divisorData, in MontgomeryDivisorData divisorData,
@@ -55,12 +57,13 @@ internal static partial class PrimeOrderCalculator
PrimeOrderHeuristicDevice device) PrimeOrderHeuristicDevice device)
{ {
var scope = UsePow2Mode(device); var scope = UsePow2Mode(device);
ulong order = CalculateInternal(prime, previousOrder, divisorData, config); ulong order = CalculateInternal(gpu, prime, previousOrder, divisorData, config);
scope.Dispose(); scope.Dispose();
return order; return order;
} }
public static UInt128 Calculate( public static UInt128 Calculate(
PrimeOrderCalculatorAccelerator gpu,
in UInt128 prime, in UInt128 prime,
in UInt128? previousOrder, in UInt128? previousOrder,
in PrimeOrderSearchConfig config, in PrimeOrderSearchConfig config,
@@ -88,14 +91,14 @@ internal static partial class PrimeOrderCalculator
ulong prime64 = (ulong)prime; ulong prime64 = (ulong)prime;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared; Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
divisorData = divisorPool.FromModulus(prime64); divisorData = divisorPool.FromModulus(prime64);
ulong order64 = Calculate(prime64, previous, divisorData, config, device); ulong order64 = Calculate(gpu, prime64, previous, divisorData, config, device);
divisorPool.Return(divisorData); divisorPool.Return(divisorData);
result = order64 == 0UL ? UInt128.Zero : (UInt128)order64; result = order64 == 0UL ? UInt128.Zero : (UInt128)order64;
} }
else else
{ {
divisorData = MontgomeryDivisorData.Empty; divisorData = MontgomeryDivisorData.Empty;
result = CalculateWideInternal(prime, previousOrder, divisorData, config); result = CalculateWideInternal(gpu, prime, previousOrder, divisorData, config);
} }
scope.Dispose(); scope.Dispose();
+34 -38
View File
@@ -87,45 +87,45 @@ public sealed class PrimeTester
return result; return result;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] // [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeGpu(ulong n) // public static bool IsPrimeGpu(ulong n)
{ // {
var gpu = PrimeOrderCalculatorAccelerator.Rent(1); // var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex; // int acceleratorIndex = gpu.AcceleratorIndex;
var inputView = gpu.Input.View; // var inputView = gpu.Input.View;
var outputView = gpu.OutputByte.View; // var outputView = gpu.OutputByte.View;
// GpuPrimeWorkLimiter.Acquire(); // // GpuPrimeWorkLimiter.Acquire();
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); // AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
inputView.CopyFromCPU(stream, ref n, 1); // inputView.CopyFromCPU(stream, ref n, 1);
var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher; // var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
kernelLauncher( // kernelLauncher(
stream, // stream,
1, // 1,
inputView, // inputView,
gpu.DevicePrimesLastOne, // gpu.DevicePrimesLastOne,
gpu.DevicePrimesLastSeven, // gpu.DevicePrimesLastSeven,
gpu.DevicePrimesLastThree, // gpu.DevicePrimesLastThree,
gpu.DevicePrimesLastNine, // gpu.DevicePrimesLastNine,
gpu.DevicePrimesPow2LastOne, // gpu.DevicePrimesPow2LastOne,
gpu.DevicePrimesPow2LastSeven, // gpu.DevicePrimesPow2LastSeven,
gpu.DevicePrimesPow2LastThree, // gpu.DevicePrimesPow2LastThree,
gpu.DevicePrimesPow2LastNine, // gpu.DevicePrimesPow2LastNine,
outputView); // outputView);
byte flag = 0; // byte flag = 0;
outputView.CopyToCPU(stream, ref flag, 1); // outputView.CopyToCPU(stream, ref flag, 1);
stream.Synchronize(); // stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream); // AcceleratorStreamPool.Return(acceleratorIndex, stream);
PrimeOrderCalculatorAccelerator.Return(gpu); // PrimeOrderCalculatorAccelerator.Return(gpu);
// GpuPrimeWorkLimiter.Release(); // // GpuPrimeWorkLimiter.Release();
return flag != 0; // return flag != 0;
} // }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n) public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
@@ -189,10 +189,9 @@ public sealed class PrimeTester
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsPrimeBatchGpu(ReadOnlySpan<ulong> values, Span<byte> results) public static void IsPrimeBatchGpu(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> values, Span<byte> results)
{ {
// GpuPrimeWorkLimiter.Acquire(); // GpuPrimeWorkLimiter.Acquire();
var gpu = PrimeOrderCalculatorAccelerator.Rent(GpuBatchSize);
int acceleratorIndex = gpu.AcceleratorIndex; int acceleratorIndex = gpu.AcceleratorIndex;
int totalLength = values.Length; int totalLength = values.Length;
int batchSize = GpuBatchSize; int batchSize = GpuBatchSize;
@@ -236,7 +235,6 @@ public sealed class PrimeTester
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
PrimeOrderCalculatorAccelerator.Return(gpu);
// GpuPrimeWorkLimiter.Release(); // GpuPrimeWorkLimiter.Release();
} }
@@ -265,7 +263,7 @@ public sealed class PrimeTester
return n.BinaryGcd(m) != 1UL; return n.BinaryGcd(m) != 1UL;
} }
internal static void SharesFactorWithMaxExponentBatch(ReadOnlySpan<ulong> values, Span<byte> results) internal static void SharesFactorWithMaxExponentBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan<ulong> values, Span<byte> results)
{ {
// TODO: Route this batch helper through the shared GPU kernel pool from // TODO: Route this batch helper through the shared GPU kernel pool from
// GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers, // GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers,
@@ -275,7 +273,6 @@ public sealed class PrimeTester
int length = values.Length; int length = values.Length;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
int acceleratorIndex = gpu.AcceleratorIndex; int acceleratorIndex = gpu.AcceleratorIndex;
gpu.EnsureCapacity(0, length); gpu.EnsureCapacity(0, length);
MemoryBuffer1D<ulong, Stride1D.Dense>? inputBuffer = gpu.Input; MemoryBuffer1D<ulong, Stride1D.Dense>? inputBuffer = gpu.Input;
@@ -291,7 +288,6 @@ public sealed class PrimeTester
stream.Synchronize(); stream.Synchronize();
AcceleratorStreamPool.Return(acceleratorIndex, stream); AcceleratorStreamPool.Return(acceleratorIndex, stream);
PrimeOrderCalculatorAccelerator.Return(gpu);
} }
} }
+3 -2
View File
@@ -1,6 +1,7 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -516,7 +517,7 @@ public static class UInt128Extensions
return (b1, a1); return (b1, a1);
} }
public static ulong CalculateOrder(this UInt128 q) public static ulong CalculateOrder(this UInt128 q, PrimeOrderCalculatorAccelerator gpu)
{ {
if (q <= UInt128Numbers.Two) if (q <= UInt128Numbers.Two)
{ {
@@ -535,7 +536,7 @@ public static class UInt128Extensions
ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2;
int i = 0, primesLength = smallPrimes.Length; int i = 0, primesLength = smallPrimes.Length;
UInt128 cycle = MersenneDivisorCycles.GetCycle(q); UInt128 cycle = MersenneDivisorCycles.GetCycle(gpu, q);
// TODO: If the cache lacks this cycle, immediately schedule the configured device // TODO: If the cache lacks this cycle, immediately schedule the configured device
// (GPU by default) to compute it on the fly and skip inserting it into the cache so // (GPU by default) to compute it on the fly and skip inserting it into the cache so
// wide-order factoring can still leverage cycle stepping without breaching the // wide-order factoring can still leverage cycle stepping without breaching the
+6 -10
View File
@@ -44,27 +44,23 @@ public static partial class ULongExtensions
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Pow2MontgomeryModWindowedKeepGpu(this ulong exponent, in MontgomeryDivisorData divisor) public static ulong Pow2MontgomeryModWindowedKeepGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor)
=> Pow2MontgomeryGpuCalculator.CalculateKeep(exponent, divisor); => Pow2MontgomeryGpuCalculator.CalculateKeep(gpu, exponent, divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Pow2MontgomeryModWindowedConvertGpu(this ulong exponent, in MontgomeryDivisorData divisor)
=> Pow2MontgomeryGpuCalculator.CalculateConvert(exponent, divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Pow2MontgomeryModWindowedConvertGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor) public static ulong Pow2MontgomeryModWindowedConvertGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor)
=> Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, exponent, divisor); => Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, exponent, divisor);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Pow2MontgomeryModWithCycleGpu(this ulong exponent, ulong cycleLength, in MontgomeryDivisorData divisor) public static ulong Pow2MontgomeryModWithCycleGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, ulong cycleLength, in MontgomeryDivisorData divisor)
{ {
ulong rotationCount = exponent % cycleLength; ulong rotationCount = exponent % cycleLength;
return Pow2MontgomeryGpuCalculator.CalculateConvert(rotationCount, divisor); return Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, rotationCount, divisor);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Pow2MontgomeryModFromCycleRemainderGpu(this ulong reducedExponent, in MontgomeryDivisorData divisor) public static ulong Pow2MontgomeryModFromCycleRemainderGpu(this ulong reducedExponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor)
{ {
return Pow2MontgomeryGpuCalculator.CalculateConvert(reducedExponent, divisor); return Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, reducedExponent, divisor);
} }
} }
+8 -7
View File
@@ -1,5 +1,6 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using PerfectNumbers.Core.Gpu.Accelerators;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -98,7 +99,7 @@ public static partial class ULongExtensions
} }
} }
public static ulong CalculateOrder(this ulong q) public static ulong CalculateOrder(this ulong q, PrimeOrderCalculatorAccelerator gpu)
{ {
if (q <= 2UL) if (q <= 2UL)
{ {
@@ -111,7 +112,7 @@ public static partial class ULongExtensions
int i = 0, primesLength = smallPrimes.Length; int i = 0, primesLength = smallPrimes.Length;
UInt128 q128 = q, UInt128 q128 = q,
cycle = MersenneDivisorCycles.GetCycle(q128); cycle = MersenneDivisorCycles.GetCycle(gpu, q128);
// TODO: When the shared cycle snapshot cannot serve this divisor, trigger an on-demand // TODO: When the shared cycle snapshot cannot serve this divisor, trigger an on-demand
// GPU computation (respecting the configured device) without promoting the result into // GPU computation (respecting the configured device) without promoting the result into
// the cache so the order calculator still benefits from cycle stepping while keeping the // the cache so the order calculator still benefits from cycle stepping while keeping the
@@ -860,7 +861,7 @@ public static partial class ULongExtensions
/// Computes 2^exponent mod modulus using iterative CRT composition from mod 10 up to modulus. /// Computes 2^exponent mod modulus using iterative CRT composition from mod 10 up to modulus.
/// Only for modulus >= 10 and reasonable size. /// Only for modulus >= 10 and reasonable size.
/// </summary> /// </summary>
public static UInt128 PowModCrt(this ulong exponent, UInt128 modulus, MersenneDivisorCycles cycles) public static UInt128 PowModCrt(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, UInt128 modulus, MersenneDivisorCycles cycles)
{ {
if (modulus < 10) if (modulus < 10)
return PowMod(exponent, modulus); // fallback to classic return PowMod(exponent, modulus); // fallback to classic
@@ -875,7 +876,7 @@ public static partial class ULongExtensions
for (; modulusCandidate <= modulus; modulusCandidate++) for (; modulusCandidate <= modulus; modulusCandidate++)
{ {
cycle = MersenneDivisorCycles.GetCycle(modulusCandidate); cycle = MersenneDivisorCycles.GetCycle(gpu, modulusCandidate);
remainderForCandidate = cycle > zero remainderForCandidate = cycle > zero
? PowModWithCycle(exponent, modulusCandidate, cycle) ? PowModWithCycle(exponent, modulusCandidate, cycle)
: PowMod(exponent, modulusCandidate); : PowMod(exponent, modulusCandidate);
@@ -935,7 +936,7 @@ public static partial class ULongExtensions
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool SharesFactorWithExponentMinusOne(this ulong exponent) public static bool SharesFactorWithExponentMinusOne(this ulong exponent, PrimeOrderCalculatorAccelerator gpu)
{ {
ulong prime, value = exponent - 1UL; ulong prime, value = exponent - 1UL;
value >>= BitOperations.TrailingZeroCount(value); value >>= BitOperations.TrailingZeroCount(value);
@@ -951,7 +952,7 @@ public static partial class ULongExtensions
continue; continue;
} }
if (exponent % prime.CalculateOrder() == 0UL) if (exponent % prime.CalculateOrder(gpu) == 0UL)
{ {
return true; return true;
} }
@@ -963,7 +964,7 @@ public static partial class ULongExtensions
while (value % prime == 0UL); while (value % prime == 0UL);
} }
if (value > 1UL && exponent % value.CalculateOrder() == 0UL) if (value > 1UL && exponent % value.CalculateOrder(gpu) == 0UL)
{ {
return true; return true;
} }