PrimeOrderCalculatorAccelerator.Rent is now created outside of the loops and re-used for all calculations.
This commit is contained in:
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user