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
+836
-426
No files matched your search
@@ -4,6 +4,7 @@ using BenchmarkDotNet.Jobs;
|
||||
using System.Numerics;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
@@ -98,11 +99,13 @@ public class Pow2ModBenchmarks
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -134,11 +137,13 @@ public class Pow2ModBenchmarks
|
||||
GetData(out var exponents, out var divisors, out var cycles);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -175,12 +180,14 @@ public class Pow2ModBenchmarks
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -190,12 +197,14 @@ public class Pow2ModBenchmarks
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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]);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return checksum;
|
||||
}
|
||||
|
||||
@@ -209,12 +218,14 @@ public class Pow2ModBenchmarks
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
ulong cycle = CalculateCycleLengthWithHeuristics(divisors[i].Modulus);
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(cycle, divisors[i]);
|
||||
ulong cycle = CalculateCycleLengthWithHeuristics(gpu, divisors[i].Modulus);
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(gpu, cycle, divisors[i]);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return checksum;
|
||||
}
|
||||
|
||||
@@ -223,12 +234,14 @@ public class Pow2ModBenchmarks
|
||||
{
|
||||
UInt128 checksum = UInt128.Zero;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
UInt128 cycle = CalculateCycleLengthWithHeuristics(_wideModuli[i]);
|
||||
UInt128 cycle = CalculateCycleLengthWithHeuristics(gpu, _wideModuli[i]);
|
||||
checksum ^= cycle;
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return checksum;
|
||||
}
|
||||
|
||||
@@ -464,13 +477,14 @@ public class Pow2ModBenchmarks
|
||||
ulong? previousPrimeOrder = null;
|
||||
UInt128? previousWidePrimeOrder = null;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
ulong smallModulus = NextSmallOddModulus(random);
|
||||
Console.WriteLine($"Calculating small cycle {smallModulus}");
|
||||
smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus);
|
||||
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);
|
||||
Console.WriteLine($"Calculating large cycle {largeModulus}");
|
||||
@@ -482,13 +496,13 @@ public class Pow2ModBenchmarks
|
||||
Console.WriteLine($"Calculating very large cycle {veryLargeModulus} with heuristics");
|
||||
veryLargeDivisors[i] = CreateMontgomeryDivisorData(veryLargeModulus);
|
||||
veryLargeExponents[i] = NextVeryLargeExponent(heuristicRandom);
|
||||
veryLargeCycles[i] = CalculateCycleLengthWithHeuristics(veryLargeModulus, ref previousPrimeOrder);
|
||||
veryLargeCycles[i] = CalculateCycleLengthWithHeuristics(gpu, veryLargeModulus, ref previousPrimeOrder);
|
||||
|
||||
UInt128 wideModulus = NextWideOddModulus(wideRandom);
|
||||
Console.WriteLine($"Calculating wide cycle {wideModulus} with heuristics");
|
||||
wideModuli[i] = wideModulus;
|
||||
wideExponents[i] = NextWideExponent(wideRandom);
|
||||
wideCycles[i] = CalculateCycleLengthWithHeuristics(wideModulus, ref previousWidePrimeOrder);
|
||||
wideCycles[i] = CalculateCycleLengthWithHeuristics(gpu, wideModulus, ref previousWidePrimeOrder);
|
||||
}
|
||||
|
||||
SampleCollections collections = new SampleCollections(
|
||||
@@ -507,6 +521,7 @@ public class Pow2ModBenchmarks
|
||||
|
||||
Console.WriteLine("Finished preparing power-of-two modulus benchmark sample collections.");
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return collections;
|
||||
}
|
||||
|
||||
@@ -580,15 +595,16 @@ public class Pow2ModBenchmarks
|
||||
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);
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return MersenneDivisorCycles.CalculateCycleLength(modulus, divisorData);
|
||||
return MersenneDivisorCycles.CalculateCycleLength(gpu, modulus, divisorData);
|
||||
}
|
||||
|
||||
ulong order = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
modulus,
|
||||
previousPrimeOrder,
|
||||
divisorData,
|
||||
@@ -602,17 +618,18 @@ public class Pow2ModBenchmarks
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return MersenneDivisorCycles.GetCycle(modulus);
|
||||
return MersenneDivisorCycles.GetCycle(gpu, modulus);
|
||||
}
|
||||
|
||||
UInt128 order = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
modulus,
|
||||
previousWidePrimeOrder,
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
@@ -625,7 +642,7 @@ public class Pow2ModBenchmarks
|
||||
}
|
||||
|
||||
previousWidePrimeOrder = null;
|
||||
return MersenneDivisorCycles.GetCycle(modulus);
|
||||
return MersenneDivisorCycles.GetCycle(gpu, modulus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,18 +666,19 @@ public class Pow2ModBenchmarks
|
||||
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);
|
||||
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
|
||||
Console.WriteLine("Trying heuristic. Prime order calculation");
|
||||
#endif
|
||||
ulong order = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
modulus,
|
||||
_previousPrimeOrder,
|
||||
divisorData,
|
||||
@@ -676,20 +694,21 @@ public class Pow2ModBenchmarks
|
||||
_previousPrimeOrder = null;
|
||||
|
||||
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)
|
||||
{
|
||||
return MersenneDivisorCycles.GetCycle(modulus);
|
||||
return MersenneDivisorCycles.GetCycle(gpu, modulus);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
Console.WriteLine("Trying heuristic. Wide prime order calculation");
|
||||
#endif
|
||||
UInt128 order = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
modulus,
|
||||
_previousWidePrimeOrder,
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
@@ -704,7 +723,7 @@ public class Pow2ModBenchmarks
|
||||
_previousWidePrimeOrder = null;
|
||||
|
||||
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);
|
||||
|
||||
@@ -2,6 +2,7 @@ using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System.Numerics;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
@@ -32,12 +33,13 @@ public class Pow2MontgomeryModBenchmarks
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
ulong smallModulus = NextSmallOddModulus();
|
||||
_smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus);
|
||||
_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");
|
||||
(ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle();
|
||||
@@ -45,6 +47,8 @@ public class Pow2MontgomeryModBenchmarks
|
||||
_largeExponents[i] = NextLargeExponent();
|
||||
_largeCycles[i] = largeCycle;
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -75,11 +79,13 @@ public class Pow2MontgomeryModBenchmarks
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -109,11 +115,13 @@ public class Pow2MontgomeryModBenchmarks
|
||||
GetData(out var exponents, out var divisors, out var cycles);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
@@ -60,12 +61,13 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider
|
||||
};
|
||||
|
||||
var cases = new Pow2MontgomeryModWindowedBenchmarkCase[seeds.Length];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < seeds.Length; i++)
|
||||
{
|
||||
Pow2MontgomeryModWindowedBenchmarkSeed seed = seeds[i];
|
||||
ulong modulus = 2UL * seed.Multiplier * seed.Exponent + 1UL;
|
||||
MontgomeryDivisorData divisor = MontgomeryDivisorDataPool.Shared.FromModulus(modulus);
|
||||
ulong cycleLength = MersenneDivisorCycles.CalculateCycleLength(modulus, divisor);
|
||||
ulong cycleLength = MersenneDivisorCycles.CalculateCycleLength(gpu, modulus, divisor);
|
||||
ulong reducedExponent = seed.Exponent % cycleLength;
|
||||
cases[i] = new Pow2MontgomeryModWindowedBenchmarkCase(
|
||||
seed.Exponent,
|
||||
@@ -75,6 +77,7 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider
|
||||
reducedExponent);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return new Pow2MontgomeryModWindowedBenchmarkInputs(cases);
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -1,6 +1,7 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using PerfectNumbers.Core.Gpu.Kernels;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
@@ -31,12 +32,14 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < cases.Length; 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;
|
||||
}
|
||||
|
||||
@@ -49,12 +52,14 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < cases.Length; 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,15 +28,17 @@ public class PrimeTesterBenchmarks
|
||||
|
||||
PrimeOrderCalculatorAccelerator.WarmUp();
|
||||
// HeuristicGroupABPrimeTesterAccelerator.WarmUp();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
_ = HeuristicPrimeTester.IsPrimeCpu(_candidates[0]);
|
||||
_ = HeuristicPrimeTester.IsPrimeGpu(_candidates[0]);
|
||||
_ = HeuristicPrimeTester.IsPrimeGpu(gpu, _candidates[0]);
|
||||
_ = Prime.Numbers.IsPrime(_candidates[0]);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
|
||||
// [Benchmark]
|
||||
public int HeuristicCpu()
|
||||
{
|
||||
HeuristicPrimeTester tester = _heuristicTester;
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
|
||||
@@ -54,17 +56,18 @@ public class PrimeTesterBenchmarks
|
||||
[Benchmark]
|
||||
public int HeuristicGpu()
|
||||
{
|
||||
HeuristicPrimeTester tester = _heuristicTester;
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (HeuristicPrimeTester.IsPrimeGpu(values[i]))
|
||||
if (HeuristicPrimeTester.IsPrimeGpu(gpu, values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
}
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
|
||||
return primeCount;
|
||||
}
|
||||
@@ -75,9 +78,10 @@ public class PrimeTesterBenchmarks
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (HeuristicCombinedPrimeTester.IsPrimeGpu(values[i]))
|
||||
if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
@@ -109,14 +113,16 @@ public class PrimeTesterBenchmarks
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (PrimeTester.IsPrimeGpu(values[i]))
|
||||
if (PrimeTester.IsPrimeGpu(gpu, values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return primeCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using PerfectNumbers.Core.Gpu;
|
||||
using EvenPerfectBitScanner.Candidates;
|
||||
using EvenPerfectBitScanner.Candidates.Transforms;
|
||||
using System.Numerics;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Tests;
|
||||
|
||||
@@ -104,14 +105,16 @@ public class ProgramTests
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) });
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(11UL, 32UL, out _, out _).Should().BeFalse();
|
||||
Program.IsEvenPerfectCandidate(5UL, 32UL, out _, out _).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(127UL, 32UL, out _, out _).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(gpu, 11UL, 32UL, out _, out _).Should().BeFalse();
|
||||
Program.IsEvenPerfectCandidate(gpu, 5UL, 32UL, out _, out _).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(gpu, 127UL, 32UL, out _, out _).Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
testerField.SetValue(null, null);
|
||||
candidatesField.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));
|
||||
candidatesField.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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();
|
||||
detailedCheck.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
testerField.SetValue(null, null);
|
||||
candidatesField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
@@ -163,14 +168,16 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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();
|
||||
detailed.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
testerField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
@@ -191,18 +198,20 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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();
|
||||
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();
|
||||
detailed.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
testerField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
@@ -222,18 +231,20 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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();
|
||||
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();
|
||||
detailed.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
@@ -256,18 +267,20 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
ulong[] primes = [107UL, 127UL];
|
||||
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();
|
||||
detailed.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
@@ -291,18 +304,20 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
ulong[] primes = [107UL, 127UL, 521UL, 607UL];
|
||||
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();
|
||||
detailed.Should().BeTrue();
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
@@ -325,18 +340,20 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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();
|
||||
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();
|
||||
detailed.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
@@ -362,13 +379,15 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
@@ -397,13 +416,15 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
@@ -476,21 +497,23 @@ public class ProgramTests
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
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();
|
||||
detailed.Should().BeFalse();
|
||||
((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();
|
||||
((bool)compositeField.GetValue(null)!).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Runtime.CompilerServices;
|
||||
using Open.Numeric.Primes;
|
||||
using PerfectNumbers.Core;
|
||||
using EvenPerfectBitScanner.Candidates.Transforms;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Candidates;
|
||||
|
||||
@@ -53,7 +54,7 @@ internal static class CandidatesCalculator
|
||||
return trackers.Value!;
|
||||
}
|
||||
|
||||
internal static bool IsCompositeByResidues(ulong p)
|
||||
internal static bool IsCompositeByResidues(PrimeOrderCalculatorAccelerator gpu, ulong p)
|
||||
{
|
||||
ModResidueTracker tracker = AcquireResidueTracker();
|
||||
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
|
||||
@@ -71,7 +72,7 @@ internal static class CandidatesCalculator
|
||||
break;
|
||||
}
|
||||
|
||||
if (tracker.MergeOrAppend(p, primes[primeIndex], out bool divisible) && divisible)
|
||||
if (tracker.MergeOrAppend(gpu, p, primes[primeIndex], out bool divisible) && divisible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Open.Collections;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner;
|
||||
|
||||
@@ -469,6 +470,7 @@ internal static class Program
|
||||
bool passedAllTests, searchedMersenne, detailedCheck;
|
||||
ArrayPool<ulong> pool = ThreadStaticPools.UlongPool;
|
||||
ulong[] buffer = pool.Rent(blockSize);
|
||||
PrimeOrderCalculatorAccelerator gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
while (!Volatile.Read(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++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -505,7 +507,7 @@ internal static class Program
|
||||
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);
|
||||
|
||||
if (p == maxP)
|
||||
@@ -522,6 +524,7 @@ internal static class Program
|
||||
}
|
||||
|
||||
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;
|
||||
detailedCheck = false;
|
||||
@@ -615,7 +618,7 @@ internal static class Program
|
||||
}
|
||||
|
||||
// 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;
|
||||
detailedCheck = false;
|
||||
@@ -628,7 +631,7 @@ internal static class Program
|
||||
{
|
||||
if (!(_runPrimesOnCpu
|
||||
? PrimeTester.IsPrime(p)
|
||||
: HeuristicCombinedPrimeTester.IsPrimeGpu(p)))
|
||||
: HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, p)))
|
||||
{
|
||||
_lastCompositeP = true;
|
||||
return false;
|
||||
@@ -641,7 +644,7 @@ internal static class Program
|
||||
if (_cliArguments.UseDivisor)
|
||||
{
|
||||
bool divisorsExhausted;
|
||||
bool isPrime = _divisorTester!.IsPrime(p, _divisor, divisorCyclesSearchLimit, out divisorsExhausted);
|
||||
bool isPrime = _divisorTester!.IsPrime(gpu, p, _divisor, divisorCyclesSearchLimit, out divisorsExhausted);
|
||||
detailedCheck = divisorsExhausted;
|
||||
return isPrime;
|
||||
}
|
||||
@@ -649,10 +652,10 @@ internal static class Program
|
||||
if (_cliArguments.UseByDivisor)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
@@ -14,7 +15,15 @@ public class LucasLehmerPrimeTesterTests
|
||||
[Trait("Category", "Fast")]
|
||||
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]
|
||||
@@ -26,7 +35,15 @@ public class LucasLehmerPrimeTesterTests
|
||||
const ulong exponent = 128UL;
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
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]
|
||||
@@ -49,7 +66,15 @@ public class LucasLehmerPrimeTesterTests
|
||||
{
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
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]
|
||||
@@ -59,7 +84,15 @@ public class LucasLehmerPrimeTesterTests
|
||||
[Trait("Category", "Fast")]
|
||||
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 FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -187,11 +188,12 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
public void TryPow2ModWide_returns_overflow_for_marked_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 7;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
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);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
@@ -199,6 +201,7 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +211,9 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
UInt128 prime = ((UInt128)1 << 96) + 11;
|
||||
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);
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponent, (BigInteger)prime);
|
||||
@@ -240,9 +245,10 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
UInt128 prime = ((UInt128)1 << 80) + 9;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
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);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
@@ -251,6 +257,7 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,12 +266,13 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 4);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
UInt128 exponent = UInt128.One << 8;
|
||||
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);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
@@ -272,6 +280,7 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +291,9 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
UInt128[] exponents = { UInt128.One, ((UInt128)1 << 64) + 5 };
|
||||
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);
|
||||
for (int i = 0; i < exponents.Length; i++)
|
||||
@@ -300,9 +311,10 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
@@ -312,6 +324,7 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,9 +336,10 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 6);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
@@ -334,6 +348,7 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,17 +373,19 @@ public class PrimeOrderGpuHeuristicsTests
|
||||
public void Calculate_falls_back_to_cpu_when_gpu_overflow_is_marked()
|
||||
{
|
||||
const ulong prime = 7UL;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
@@ -9,23 +10,36 @@ public class PrimeTesterGpuTests
|
||||
[Fact]
|
||||
public void IsPrimeGpu_accepts_known_primes()
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(prime).Should().BeTrue();
|
||||
PrimeTester.IsPrimeGpu(gpu, prime).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsPrimeGpu_rejects_composites()
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
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 PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -10,6 +11,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
public void GenerateGpu_produces_expected_cycles_for_small_range()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0);
|
||||
@@ -39,11 +41,12 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
{
|
||||
expected.Should().Contain(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
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
@@ -56,6 +59,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
public void Generate_produces_unique_cycles_for_expected_divisors()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4);
|
||||
@@ -88,7 +92,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
{
|
||||
uniqueDivisors.Add(pair.divisor).Should().BeTrue();
|
||||
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);
|
||||
@@ -98,6 +102,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -17,10 +18,18 @@ public class MersenneDivisorCyclesTests
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(Divisors))]
|
||||
public void CalculateCycleLength_returns_expected_value(ulong divisor, ulong expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -57,7 +58,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
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;
|
||||
if (prime % 7UL == 0UL)
|
||||
@@ -76,7 +77,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
return true;
|
||||
}
|
||||
|
||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
|
||||
{
|
||||
return new DummySession();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -13,18 +14,26 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
@@ -33,24 +42,26 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(13UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||
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);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 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);
|
||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,14 +72,15 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
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);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
@@ -76,6 +88,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,14 +99,15 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
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);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
@@ -101,6 +115,7 @@ public class MersenneNumberDivisorCpuTesterTests
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -44,17 +45,34 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
.GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!
|
||||
.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();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_true_when_divisible_by_specified_divisor()
|
||||
{
|
||||
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();
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -62,9 +80,17 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
public void IsPrime_accepts_large_search_limits()
|
||||
{
|
||||
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();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
@@ -73,18 +99,26 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
@@ -108,16 +142,24 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
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);
|
||||
tester.IsPrime(37UL, out bool exhausted, out divisor).Should().BeFalse();
|
||||
tester.IsPrime(gpu, 37UL, out bool exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
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();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
@@ -130,7 +172,11 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
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();
|
||||
if (expectedPrime)
|
||||
{
|
||||
@@ -141,6 +187,12 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_uses_cycle_remainders_per_divisor()
|
||||
@@ -152,10 +204,18 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
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();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
@@ -164,7 +224,8 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL };
|
||||
@@ -177,6 +238,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,24 +249,26 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
|
||||
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);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 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);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,13 +283,14 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
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);
|
||||
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
@@ -233,6 +298,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,14 +312,15 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
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);
|
||||
|
||||
@@ -262,6 +329,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,14 +343,15 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
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);
|
||||
|
||||
@@ -291,6 +360,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,13 +375,14 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!;
|
||||
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var patchedTable = new List<(ulong Divisor, ulong Cycle)>();
|
||||
ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(191UL);
|
||||
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(191UL, divisorData);
|
||||
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(gpu, 191UL, divisorData);
|
||||
|
||||
tableField.SetValue(cycles, patchedTable);
|
||||
smallCyclesField.SetValue(cycles, patchedSmall);
|
||||
@@ -324,12 +395,13 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||
.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();
|
||||
divisor.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
tableField.SetValue(cycles, originalTable);
|
||||
smallCyclesField.SetValue(cycles, originalSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
@@ -347,13 +419,14 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
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);
|
||||
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
@@ -361,6 +434,7 @@ public class MersenneNumberDivisorGpuTesterTests
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -30,7 +31,15 @@ public class MersenneNumberIncrementalCpuTesterTests
|
||||
private static void RunCase(MersenneNumberIncrementalCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
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);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -30,8 +31,16 @@ public class MersenneNumberOrderCpuTesterTests
|
||||
private static void RunCase(MersenneNumberOrderCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
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);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -24,7 +25,15 @@ public class MersenneNumberResidueCpuTesterTests
|
||||
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
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);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -13,7 +14,15 @@ public class MersenneNumberTesterTests
|
||||
public void IsMersennePrime_detects_small_prime_divisors_for_large_exponents(ulong p)
|
||||
{
|
||||
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]
|
||||
@@ -22,7 +31,15 @@ public class MersenneNumberTesterTests
|
||||
{
|
||||
var tester = new MersenneNumberTester(useOrderCache: true, useGpuOrder: true);
|
||||
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]
|
||||
@@ -30,7 +47,15 @@ public class MersenneNumberTesterTests
|
||||
[InlineData(127UL, false)]
|
||||
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 PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -14,7 +15,15 @@ public class MersenneResidueModeTests
|
||||
useIncremental: false,
|
||||
useGpuScan: 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]
|
||||
@@ -26,7 +35,15 @@ public class MersenneResidueModeTests
|
||||
useGpuScan: false,
|
||||
useGpuOrder: false,
|
||||
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 PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -11,9 +12,17 @@ public class ModResidueTrackerTests
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
|
||||
tracker.IsDivisible(5, 2).Should().BeFalse();
|
||||
tracker.IsDivisible(6, 2).Should().BeTrue();
|
||||
tracker.IsDivisible(7, 2).Should().BeFalse();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
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]
|
||||
@@ -25,42 +34,66 @@ public class ModResidueTrackerTests
|
||||
public void IsDivisible_handles_mersenne_model(ulong p, ulong divisor, bool expected)
|
||||
{
|
||||
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]
|
||||
public void MergeOrAppend_tracks_existing_and_new_divisors()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
try
|
||||
{
|
||||
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();
|
||||
tracker.MergeOrAppend(10, 5, out bool d5).Should().BeTrue();
|
||||
tracker.MergeOrAppend(gpu, 10, 5, out bool d5).Should().BeTrue();
|
||||
d5.Should().BeTrue();
|
||||
|
||||
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();
|
||||
|
||||
tracker.BeginMerge(11);
|
||||
tracker.MergeOrAppend(11, 3, out d3).Should().BeTrue();
|
||||
tracker.MergeOrAppend(gpu, 11, 3, out d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppend(11, 4, out d4).Should().BeTrue();
|
||||
tracker.MergeOrAppend(gpu, 11, 4, out d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
tracker.MergeOrAppend(11, 5, out d5).Should().BeTrue();
|
||||
tracker.MergeOrAppend(gpu, 11, 5, out d5).Should().BeTrue();
|
||||
d5.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDivisible_requires_non_decreasing_numbers()
|
||||
{
|
||||
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>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -42,14 +43,18 @@ public class PrimeOrderCalculatorTests
|
||||
public void Calculate_heuristic_mode_matches_naive_cycle_length(ulong prime)
|
||||
{
|
||||
ulong expected = ComputeOrderByDoubling(prime);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
@@ -60,7 +65,9 @@ public class PrimeOrderCalculatorTests
|
||||
{
|
||||
ulong previousOrder = ComputeOrderByDoubling(previousPrime);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong heuristic = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
@@ -68,12 +75,15 @@ public class PrimeOrderCalculatorTests
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
ulong strict = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
|
||||
heuristic.Should().Be(strict);
|
||||
}
|
||||
|
||||
@@ -83,13 +93,16 @@ public class PrimeOrderCalculatorTests
|
||||
[InlineData(3UL, 2UL)]
|
||||
public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be(expectedOrder);
|
||||
}
|
||||
|
||||
@@ -103,13 +116,16 @@ public class PrimeOrderCalculatorTests
|
||||
maxPowChecks: 8,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime: 13UL,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(13UL),
|
||||
config,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be(ComputeOrderByDoubling(13UL));
|
||||
}
|
||||
|
||||
@@ -123,8 +139,12 @@ public class PrimeOrderCalculatorTests
|
||||
maxPowChecks: 1,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(239UL);
|
||||
ulong heuristic = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
@@ -140,6 +160,7 @@ public class PrimeOrderCalculatorTests
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Strict);
|
||||
|
||||
ulong strict = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
@@ -147,6 +168,12 @@ public class PrimeOrderCalculatorTests
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
strict.Should().Be(heuristic);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -155,12 +182,15 @@ public class PrimeOrderCalculatorTests
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
UInt128 result = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
|
||||
@@ -169,7 +199,15 @@ public class PrimeOrderCalculatorTests
|
||||
public void GetCycle_uses_wide_prime_heuristic()
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -19,7 +20,15 @@ public class PrimeTesterGcdTests
|
||||
{
|
||||
ulong[] values = [81UL, 101UL];
|
||||
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 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -39,7 +40,15 @@ public class PrimeTesterIsPrimeTests
|
||||
{
|
||||
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]
|
||||
@@ -62,9 +71,12 @@ public class PrimeTesterIsPrimeTests
|
||||
var results = new bool[primes.Length];
|
||||
|
||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Parallel.For(0, primes.Length, i =>
|
||||
{
|
||||
results[i] = PrimeTester.IsPrimeGpu(primes[i]);
|
||||
results[i] = PrimeTester.IsPrimeGpu(gpu, primes[i]);
|
||||
});
|
||||
|
||||
results.Should().AllBeEquivalentTo(true);
|
||||
@@ -73,9 +85,14 @@ public class PrimeTesterIsPrimeTests
|
||||
{
|
||||
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 FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -54,7 +55,15 @@ public class UInt128ExtensionsTests
|
||||
{
|
||||
UInt128 q128 = 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)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -71,7 +72,15 @@ public class ULongExtensionsTests
|
||||
{
|
||||
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]
|
||||
@@ -112,7 +121,15 @@ public class ULongExtensionsTests
|
||||
[InlineData(9UL, false)]
|
||||
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]
|
||||
@@ -201,7 +218,15 @@ public class ULongExtensionsTests
|
||||
MersenneDivisorCycles cycles = MersenneDivisorCycles.Shared;
|
||||
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)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core.Cpu;
|
||||
|
||||
internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||
internal sealed class MersenneCpuDivisorScanSession(PrimeOrderCalculatorAccelerator gpu) : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||
{
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: Convert this to static method
|
||||
public void CheckDivisor(
|
||||
ulong divisor,
|
||||
in MontgomeryDivisorData divisorData,
|
||||
@@ -29,7 +32,7 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi
|
||||
|
||||
if (divisorCycle == 0UL)
|
||||
{
|
||||
divisorCycle = DivisorCycleCache.Shared.GetCycleLength(divisor);
|
||||
divisorCycle = DivisorCycleCache.Shared.GetCycleLength(gpu, divisor);
|
||||
if (divisorCycle == 0UL)
|
||||
{
|
||||
// DivisorCycleCache guarantees a positive cycle for divisors greater than one.
|
||||
|
||||
@@ -4,7 +4,7 @@ using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core.Cpu;
|
||||
|
||||
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
|
||||
public sealed class MersenneNumberDivisorByDivisorCpuTester() : IMersenneNumberDivisorByDivisorTester
|
||||
{
|
||||
private ulong _divisorLimit;
|
||||
private int _batchSize = 1_024;
|
||||
@@ -42,7 +42,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
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);
|
||||
|
||||
@@ -58,6 +58,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
bool processedAll;
|
||||
|
||||
bool composite = CheckDivisors(
|
||||
gpu,
|
||||
prime,
|
||||
allowedMax,
|
||||
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();
|
||||
if (session is not null)
|
||||
@@ -92,16 +93,16 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
return session;
|
||||
}
|
||||
|
||||
return new MersenneCpuDivisorScanSession();
|
||||
return new MersenneCpuDivisorScanSession(gpu);
|
||||
}
|
||||
|
||||
private bool CheckDivisors(
|
||||
PrimeOrderCalculatorAccelerator gpu,
|
||||
ulong prime,
|
||||
ulong allowedMax,
|
||||
out bool processedAll,
|
||||
out ulong foundDivisor)
|
||||
{
|
||||
foundDivisor = 0UL;
|
||||
// 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.
|
||||
// if (allowedMax < 3UL)
|
||||
@@ -171,6 +172,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
remainder11 = (byte)(divisorLow % 11UL);
|
||||
|
||||
return CheckDivisors64(
|
||||
gpu,
|
||||
prime,
|
||||
stepLow,
|
||||
limit.Low,
|
||||
@@ -203,7 +205,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
|
||||
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var divisorPool = MontgomeryDivisorDataPool.Shared;
|
||||
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
|
||||
// for the exponents scanned here, so skip the unused cache fallback and compute directly.
|
||||
divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
|
||||
gpu,
|
||||
candidate,
|
||||
divisorData,
|
||||
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.
|
||||
foundDivisor = candidate;
|
||||
processedAll = true;
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -259,13 +260,13 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
remainder11 = AddMod11(remainder11, step11);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
processedAll = true;
|
||||
foundDivisor = 0UL;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool CheckDivisors64(
|
||||
PrimeOrderCalculatorAccelerator gpu,
|
||||
ulong prime,
|
||||
ulong step,
|
||||
ulong limit,
|
||||
@@ -285,7 +286,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
out ulong foundDivisor)
|
||||
{
|
||||
bool canAdvance = step <= limit;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
|
||||
if (!canAdvance)
|
||||
{
|
||||
@@ -304,6 +304,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
out bool primeOrderFailed) || computedCycle == 0UL)
|
||||
{
|
||||
divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
|
||||
gpu,
|
||||
divisor,
|
||||
divisorData,
|
||||
skipPrimeOrderHeuristic: primeOrderFailed);
|
||||
@@ -319,7 +320,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
{
|
||||
foundDivisor = divisor;
|
||||
processedAll = true;
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -332,7 +332,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
|
||||
processedAll = true;
|
||||
foundDivisor = 0UL;
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -340,7 +339,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
{
|
||||
processedAll = true;
|
||||
foundDivisor = 0UL;
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -361,6 +359,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
out bool primeOrderFailed) || computedCycle == 0UL)
|
||||
{
|
||||
divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
|
||||
gpu,
|
||||
divisor,
|
||||
divisorData,
|
||||
skipPrimeOrderHeuristic: primeOrderFailed);
|
||||
@@ -375,7 +374,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
{
|
||||
foundDivisor = divisor;
|
||||
processedAll = true;
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -390,7 +388,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
{
|
||||
processedAll = true;
|
||||
foundDivisor = 0UL;
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core.Cpu;
|
||||
|
||||
public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType)
|
||||
@@ -5,7 +7,7 @@ public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType)
|
||||
private static readonly MersenneDivisorCycles _cycles = MersenneDivisorCycles.Shared;
|
||||
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 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
|
||||
// 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;
|
||||
if (shouldCheck)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
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:
|
||||
// - If 3 | p and p != 3, then 7 | M_p -> composite.
|
||||
@@ -20,7 +22,7 @@ public class MersenneNumberLucasLehmerCpuTester
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne())
|
||||
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne(gpu))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -32,7 +34,7 @@ public class MersenneNumberLucasLehmerCpuTester
|
||||
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.
|
||||
s = s.PowModWithCycle(two, MersenneDivisorCycles.GetCycle(m)) - two;
|
||||
s = s.PowModWithCycle(two, MersenneDivisorCycles.GetCycle(gpu, m)) - two;
|
||||
if (s < UInt128.Zero)
|
||||
{
|
||||
s += m;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core.Cpu;
|
||||
|
||||
public class MersenneNumberOrderCpuTester(GpuKernelType 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;
|
||||
var auto = new MersenneResidueAutomaton(exponent);
|
||||
@@ -13,7 +15,7 @@ public class MersenneNumberOrderCpuTester(GpuKernelType kernelType)
|
||||
|
||||
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
|
||||
// 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.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core.Cpu;
|
||||
|
||||
public class MersenneNumberResidueCpuTester
|
||||
@@ -5,7 +7,7 @@ public class MersenneNumberResidueCpuTester
|
||||
private ModResidueTracker? _mersenneResidueTracker;
|
||||
|
||||
// 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
|
||||
|
||||
@@ -92,7 +94,7 @@ public class MersenneNumberResidueCpuTester
|
||||
// (and below) so every qualifying q reuses precomputed lengths instead of recomputing via
|
||||
// tracker.MergeOrAppend.
|
||||
// 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()))
|
||||
{
|
||||
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 &&
|
||||
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()))
|
||||
{
|
||||
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 &&
|
||||
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()))
|
||||
{
|
||||
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 &&
|
||||
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()))
|
||||
{
|
||||
localIsPrime = false;
|
||||
|
||||
@@ -57,13 +57,13 @@ public sealed class DivisorCycleCache
|
||||
_snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
}
|
||||
|
||||
public ulong GetCycleLength(ulong divisor, bool skipPrimeOrderHeuristic = false)
|
||||
public ulong GetCycleLength(PrimeOrderCalculatorAccelerator gpu, ulong divisor, bool skipPrimeOrderHeuristic = false)
|
||||
{
|
||||
if (!skipPrimeOrderHeuristic)
|
||||
{
|
||||
Span<ulong> result = stackalloc ulong[1];
|
||||
ReadOnlySpan<ulong> singleDivisor = stackalloc ulong[1] { divisor };
|
||||
GetCycleLengths(singleDivisor, result);
|
||||
GetCycleLengths(gpu, singleDivisor, result);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
@@ -90,12 +90,12 @@ public sealed class DivisorCycleCache
|
||||
|
||||
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
|
||||
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
|
||||
cycleLength = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: true);
|
||||
cycleLength = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true);
|
||||
divisorPool.Return(divisorData);
|
||||
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;
|
||||
int length = divisors.Length;
|
||||
@@ -139,7 +139,7 @@ public sealed class DivisorCycleCache
|
||||
}
|
||||
else
|
||||
{
|
||||
ComputeCyclesCpu(divisors, cycles, missingIndices);
|
||||
ComputeCyclesCpu(gpu, divisors, cycles, missingIndices);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public sealed class DivisorCycleCache
|
||||
}
|
||||
|
||||
[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;
|
||||
for (int i = 0; i < indices.Length; i++)
|
||||
@@ -195,7 +195,7 @@ public sealed class DivisorCycleCache
|
||||
int targetIndex = indices[i];
|
||||
ulong divisor = divisors[targetIndex];
|
||||
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
|
||||
cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData);
|
||||
cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData);
|
||||
divisorPool.Return(divisorData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
private ulong _divisorLimit;
|
||||
// private bool _isConfigured;
|
||||
|
||||
private readonly ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>[] _resourcePool = [..AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>())];
|
||||
private readonly ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>[] _resourcePool = [.. AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentBag<MersenneNumberDivisorByDivisorAccelerator>())];
|
||||
|
||||
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = [];
|
||||
|
||||
@@ -50,9 +50,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
// _isConfigured = true;
|
||||
}
|
||||
|
||||
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
|
||||
|
||||
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;
|
||||
int batchCapacity;
|
||||
@@ -87,7 +85,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
// GpuPrimeWorkLimiter();
|
||||
// int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||
// var accelerator = _accelerators[acceleratorIndex];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(batchCapacity);
|
||||
var acceleratorIndex = gpu.AcceleratorIndex;
|
||||
|
||||
// Monitor.Enter(gpuLease.ExecutionLock);
|
||||
@@ -117,7 +114,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
out processedCount);
|
||||
|
||||
ReturnBatchResources(acceleratorIndex, resources);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
// Monitor.Exit(gpuLease.ExecutionLock);
|
||||
// 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)
|
||||
{
|
||||
return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
||||
return MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
||||
}
|
||||
|
||||
return computedCycle;
|
||||
@@ -481,8 +477,8 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
public sealed class DivisorScanSession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||
{
|
||||
private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
|
||||
private readonly PrimeOrderCalculatorAccelerator _primeTesterAccelerator;
|
||||
private readonly Accelerator _accelerator;
|
||||
private readonly PrimeOrderCalculatorAccelerator _primeOrderCalculatorAccelerator;
|
||||
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 MemoryBuffer1D<GpuDivisorPartialData, Stride1D.Dense> _divisorBuffer = null!;
|
||||
@@ -496,32 +492,16 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
private int _capacity;
|
||||
|
||||
|
||||
internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner)
|
||||
internal DivisorScanSession(PrimeOrderCalculatorAccelerator gpu, MersenneNumberDivisorByDivisorGpuTester owner)
|
||||
{
|
||||
_owner = owner;
|
||||
// GpuPrimeWorkLimiter.Acquire();
|
||||
_primeTesterAccelerator = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
Accelerator accelerator = _primeTesterAccelerator.Accelerator;
|
||||
_primeOrderCalculatorAccelerator = gpu;
|
||||
Accelerator accelerator = gpu.Accelerator;
|
||||
_accelerator = accelerator;
|
||||
_stream = accelerator.CreateStream();
|
||||
_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)
|
||||
{
|
||||
_divisorBuffer = accelerator.Allocate1D<GpuDivisorPartialData>(1);
|
||||
@@ -530,28 +510,36 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
_cycleBuffer = accelerator.Allocate1D<ulong>(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;
|
||||
if (!allocateBuffers && _capacity < requiredCapacity)
|
||||
_hostBuffer = ThreadStaticPools.UlongPool.Rent(desiredCapacity);
|
||||
|
||||
_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);
|
||||
_hostBuffer = null!;
|
||||
}
|
||||
|
||||
_exponentsBuffer = null!;
|
||||
_hitBuffer = null!;
|
||||
allocateBuffers = true;
|
||||
}
|
||||
|
||||
if (allocateBuffers)
|
||||
{
|
||||
Accelerator accelerator = _accelerator;
|
||||
int desiredCapacity = requiredCapacity > _capacity ? requiredCapacity : _capacity;
|
||||
_capacity = desiredCapacity;
|
||||
|
||||
// lock (accelerator)
|
||||
{
|
||||
_exponentsBuffer = accelerator.Allocate1D<ulong>(desiredCapacity);
|
||||
@@ -581,13 +569,12 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
|
||||
ulong cycle = divisorCycle;
|
||||
ulong firstPrime = primes[0];
|
||||
|
||||
var gpu = _primeOrderCalculatorAccelerator;
|
||||
if (cycle == 0UL)
|
||||
{
|
||||
var gpu = _primeTesterAccelerator;
|
||||
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
|
||||
{
|
||||
@@ -714,7 +701,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
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.
|
||||
// lock (_sync)
|
||||
@@ -739,8 +726,6 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
|
||||
return session;
|
||||
}
|
||||
|
||||
return new DivisorScanSession(this);
|
||||
return new DivisorScanSession(gpu, this);
|
||||
}
|
||||
|
||||
IMersenneNumberDivisorByDivisorTester.IDivisorScanSession IMersenneNumberDivisorByDivisorTester.CreateDivisorSession() => CreateDivisorSession();
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
|
||||
@@ -86,7 +87,7 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
|
||||
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;
|
||||
|
||||
@@ -142,7 +143,7 @@ public sealed class MersenneNumberDivisorGpuTester
|
||||
continue;
|
||||
}
|
||||
|
||||
UInt128 cycle128 = MersenneDivisorCycles.GetCycle(d);
|
||||
UInt128 cycle128 = MersenneDivisorCycles.GetCycle(gpu, d);
|
||||
if ((UInt128)p % cycle128 != UInt128.Zero)
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
namespace PerfectNumbers.Core.Gpu;
|
||||
@@ -131,7 +132,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
// Configurable LL slice size to keep kernels short. Default 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:
|
||||
// - If 3 | p and p != 3, then 7 | M_p -> composite.
|
||||
@@ -142,7 +143,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne())
|
||||
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne(gpu))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@ namespace PerfectNumbers.Core.Gpu;
|
||||
public static class Pow2MontgomeryGpuCalculator
|
||||
{
|
||||
[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();
|
||||
|
||||
ulong result = 0UL;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||
Accelerator? accelerator = gpu.Accelerator;
|
||||
MemoryBuffer1D<ulong, Stride1D.Dense> exponentBuffer = gpu.Input;
|
||||
@@ -32,37 +31,6 @@ public static class Pow2MontgomeryGpuCalculator
|
||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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> remainders = stackalloc ulong[1];
|
||||
@@ -254,7 +254,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
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
|
||||
// if (exponents.Length == 0)
|
||||
@@ -309,20 +309,20 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
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> remainders = stackalloc UInt128[1];
|
||||
exponents[0] = exponent;
|
||||
|
||||
GpuPow2ModStatus status = TryPow2ModBatch(exponents, prime, remainders);
|
||||
GpuPow2ModStatus status = TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
remainder = remainders[0];
|
||||
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(
|
||||
@@ -471,7 +471,7 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
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)
|
||||
{
|
||||
@@ -509,11 +509,11 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
}
|
||||
}
|
||||
|
||||
ComputeOnGpuWide(exponents, prime, target);
|
||||
ComputeOnGpuWide(gpu, exponents, prime, target);
|
||||
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();
|
||||
int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||
@@ -540,6 +540,11 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
GpuUInt128[]? rentedExponents = null;
|
||||
GpuUInt128[]? rentedResults = null;
|
||||
ArrayPool<GpuUInt128> gpuUInt128Pool = ThreadStaticPools.GpuUInt128Pool;
|
||||
PrimeOrderCalculatorAccelerator? gpu = null;
|
||||
bool poolingRequired = length > WideStackThreshold;
|
||||
Span<GpuUInt128> exponentSpan = poolingRequired
|
||||
? stackalloc GpuUInt128[length]
|
||||
@@ -596,7 +600,6 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
}
|
||||
|
||||
GpuUInt128 modulus = (GpuUInt128)prime;
|
||||
gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||
Accelerator accelerator = gpu.Accelerator;
|
||||
|
||||
@@ -625,7 +628,6 @@ internal static partial class PrimeOrderGpuHeuristics
|
||||
|
||||
remainderBuffer.Dispose();
|
||||
exponentBuffer.Dispose();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
|
||||
if (rentedExponents is not null)
|
||||
{
|
||||
|
||||
@@ -168,15 +168,15 @@ public sealed class HeuristicCombinedPrimeTester
|
||||
return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10);
|
||||
}
|
||||
|
||||
public static bool IsPrimeGpu(ulong n)
|
||||
{
|
||||
byte nMod10 = (byte)n.Mod10();
|
||||
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
bool isPrime = IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return isPrime;
|
||||
}
|
||||
// public static bool IsPrimeGpu(ulong n)
|
||||
// {
|
||||
// byte nMod10 = (byte)n.Mod10();
|
||||
// ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||
// var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
// bool isPrime = IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10);
|
||||
// PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
// return isPrime;
|
||||
// }
|
||||
|
||||
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
|
||||
{
|
||||
@@ -520,7 +520,7 @@ public sealed class HeuristicCombinedPrimeTester
|
||||
}
|
||||
|
||||
primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL;
|
||||
ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
||||
ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
||||
cycleFromHint = false;
|
||||
cycleComputed = resolvedCycle != 0UL;
|
||||
return resolvedCycle;
|
||||
|
||||
@@ -83,17 +83,17 @@ public sealed class HeuristicPrimeTester
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsPrimeGpu(ulong n)
|
||||
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
|
||||
{
|
||||
byte nMod10 = (byte)n.Mod10();
|
||||
ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n);
|
||||
return IsPrimeGpu(n, maxDivisorSquare, nMod10);
|
||||
return IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10);
|
||||
}
|
||||
|
||||
[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?
|
||||
if (maxDivisorSquare < 9UL)
|
||||
@@ -106,7 +106,7 @@ public sealed class HeuristicPrimeTester
|
||||
// return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10, includeGroupB: false);
|
||||
// }
|
||||
|
||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10);
|
||||
bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(gpu, n, maxDivisorSquare, nMod10);
|
||||
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();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||
var stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||
var flagView1D = gpu.OutputInt!.View;
|
||||
@@ -313,7 +312,6 @@ public sealed class HeuristicPrimeTester
|
||||
// }
|
||||
|
||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
// GpuPrimeWorkLimiter.Release();
|
||||
return compositeDetected;
|
||||
}
|
||||
@@ -487,7 +485,7 @@ public sealed class HeuristicPrimeTester
|
||||
}
|
||||
|
||||
primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL;
|
||||
ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
||||
ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed);
|
||||
cycleFromHint = false;
|
||||
cycleComputed = resolvedCycle != 0UL;
|
||||
return resolvedCycle;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
public interface IMersenneNumberDivisorByDivisorTester
|
||||
@@ -10,9 +12,9 @@ public interface IMersenneNumberDivisorByDivisorTester
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
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
|
||||
// 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.
|
||||
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
|
||||
if ((divisor & (divisor - UInt128.One)) == UInt128.Zero)
|
||||
@@ -224,12 +224,13 @@ public class MersenneDivisorCycles
|
||||
ulong divisor64 = (ulong)divisor;
|
||||
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
|
||||
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64);
|
||||
ulong cycle = CalculateCycleLength(divisor64, divisorData);
|
||||
ulong cycle = CalculateCycleLength(gpu, divisor64, divisorData);
|
||||
divisorPool.Return(divisorData);
|
||||
return (UInt128)cycle;
|
||||
}
|
||||
|
||||
UInt128 wideOrder = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
divisor,
|
||||
previousOrder: null,
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
@@ -285,6 +286,7 @@ public class MersenneDivisorCycles
|
||||
// }
|
||||
|
||||
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
divisor,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
@@ -743,6 +745,7 @@ public class MersenneDivisorCycles
|
||||
// (ulong divisor, ulong cycle)[] localCycles = ArrayPool<(ulong, ulong)>.Shared.Rent(checked((int)rangeLength));
|
||||
int localCycleIndex = 0;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (ulong divisor = localStart; divisor <= localEnd; divisor++)
|
||||
{
|
||||
if ((divisor & 1UL) == 0UL)
|
||||
@@ -762,11 +765,12 @@ public class MersenneDivisorCycles
|
||||
// unrolled-hex calculator so the CPU generator matches
|
||||
// the GPU benchmark leader for large divisors.
|
||||
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
|
||||
ulong cycle = CalculateCycleLength(divisor, divisorData);
|
||||
ulong cycle = CalculateCycleLength(gpu, divisor, divisorData);
|
||||
divisorPool.Return(divisorData);
|
||||
localCycles[localCycleIndex++] = (divisor, cycle);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
if (localCycleIndex == 0)
|
||||
{
|
||||
ThreadStaticPools.Return(localCycles);
|
||||
@@ -929,9 +933,9 @@ public class MersenneDivisorCycles
|
||||
|
||||
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 CalculateCycleLengthFallback(divisor);
|
||||
@@ -940,7 +944,7 @@ public class MersenneDivisorCycles
|
||||
// [ThreadStatic]
|
||||
// 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)
|
||||
{
|
||||
@@ -966,6 +970,7 @@ public class MersenneDivisorCycles
|
||||
// if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor))
|
||||
{
|
||||
ulong computedOrder = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
divisor,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
|
||||
@@ -239,10 +239,10 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
|
||||
int chunkSize = primesPerTask <= 0 ? 1 : primesPerTask;
|
||||
|
||||
void ProcessPrime(ulong prime)
|
||||
void ProcessPrime(PrimeOrderCalculatorAccelerator gpu, ulong 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)
|
||||
{
|
||||
@@ -259,10 +259,13 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
|
||||
if (workerCount == 1)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
foreach (ulong prime in filteredPrimes)
|
||||
{
|
||||
ProcessPrime(prime);
|
||||
ProcessPrime(gpu, prime);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -295,11 +298,13 @@ public static class MersenneNumberDivisorByDivisorTester
|
||||
startGate.Wait();
|
||||
Console.WriteLine($"Task started for range {rangeStart}");
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int index = rangeStart; index < rangeEnd; index++)
|
||||
{
|
||||
ProcessPrime(filteredPrimes[index]);
|
||||
ProcessPrime(gpu, filteredPrimes[index]);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
Console.WriteLine($"Task finished for range {rangeStart}");
|
||||
PrimeOrderCalculatorAccelerator.DisposeAll();
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@ public sealed class MersenneNumberTester(
|
||||
UInt128 twoP = (UInt128)exponent << 1; // 2 * p
|
||||
LastDigit lastDigit = (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
// CPU path: compute orders directly and cache
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
if (!_useGpuOrder)
|
||||
{
|
||||
UInt128 k = UInt128.One;
|
||||
@@ -97,10 +98,11 @@ public sealed class MersenneNumberTester(
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong ord = q.CalculateOrder();
|
||||
ulong ord = q.CalculateOrder(gpu);
|
||||
_orderCache[q] = ord;
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,7 +192,7 @@ public sealed class MersenneNumberTester(
|
||||
ulong order = orders[i];
|
||||
if (order == 0UL)
|
||||
{
|
||||
order = qs[offset + i].CalculateOrder();
|
||||
order = qs[offset + i].CalculateOrder(gpu);
|
||||
}
|
||||
|
||||
if (useOrderCache)
|
||||
@@ -205,11 +207,12 @@ public sealed class MersenneNumberTester(
|
||||
}
|
||||
|
||||
AcceleratorStreamPool.Return(acceleratorIndex,stream);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
// GpuPrimeWorkLimiter.Release();
|
||||
uInt128Pool.Return(qs);
|
||||
}
|
||||
|
||||
public bool IsMersennePrime(ulong exponent)
|
||||
public bool IsMersennePrime(PrimeOrderCalculatorAccelerator gpu, ulong exponent)
|
||||
{
|
||||
// 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.
|
||||
@@ -220,7 +223,7 @@ public sealed class MersenneNumberTester(
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne())
|
||||
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne(gpu))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -251,7 +254,7 @@ public sealed class MersenneNumberTester(
|
||||
}
|
||||
else
|
||||
{
|
||||
_residueCpuTester!.Scan(exponent, twoP, lastDigit, maxK, ref prePrime);
|
||||
_residueCpuTester!.Scan(gpu, exponent, twoP, lastDigit, maxK, ref prePrime);
|
||||
}
|
||||
|
||||
return prePrime;
|
||||
@@ -270,7 +273,7 @@ public sealed class MersenneNumberTester(
|
||||
}
|
||||
else
|
||||
{
|
||||
_incrementalCpuTester.Scan(exponent, twoPPre, lastDigitPre, maxKPre, ref prePrime);
|
||||
_incrementalCpuTester.Scan(gpu, exponent, twoPPre, lastDigitPre, maxKPre, ref prePrime);
|
||||
}
|
||||
|
||||
if (!prePrime)
|
||||
@@ -279,8 +282,8 @@ public sealed class MersenneNumberTester(
|
||||
}
|
||||
|
||||
return _useGpuLucas
|
||||
? _lucasLehmerGpuTester.IsPrime(exponent, runOnGpu: true)
|
||||
: _lucasLehmerCpuTester.IsPrime(exponent);
|
||||
? _lucasLehmerGpuTester.IsPrime(gpu, exponent, runOnGpu: true)
|
||||
: _lucasLehmerCpuTester.IsPrime(gpu, exponent);
|
||||
}
|
||||
|
||||
bool isPrime = true;
|
||||
@@ -293,7 +296,7 @@ public sealed class MersenneNumberTester(
|
||||
}
|
||||
else
|
||||
{
|
||||
_orderCpuTester.Scan(exponent, twoP, lastDigit, maxK, ref isPrime);
|
||||
_orderCpuTester.Scan(gpu, exponent, twoP, lastDigit, maxK, ref isPrime);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -304,7 +307,7 @@ public sealed class MersenneNumberTester(
|
||||
}
|
||||
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 PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
@@ -39,7 +39,7 @@ public sealed class ModResidueTracker
|
||||
/// (number must be non-decreasing across calls). Adds and initializes new
|
||||
/// divisors on first use.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsDivisible(UInt128 number, UInt128 divisor)
|
||||
public bool IsDivisible(PrimeOrderCalculatorAccelerator gpu, UInt128 number, UInt128 divisor)
|
||||
{
|
||||
if (divisor == UInt128.Zero)
|
||||
{
|
||||
@@ -52,7 +52,7 @@ public sealed class ModResidueTracker
|
||||
int idx = LowerBound(divisors, divisor);
|
||||
if (idx == divisors.Count || divisors[idx] != divisor)
|
||||
{
|
||||
UInt128 residue = ComputeInitialResidue(number, divisor);
|
||||
UInt128 residue = ComputeInitialResidue(gpu, number, divisor);
|
||||
divisors.Insert(idx, divisor);
|
||||
_residues.Insert(idx, residue);
|
||||
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).
|
||||
// Returns true and sets 'divisible' based on residue at 'number'.
|
||||
[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.
|
||||
List<UInt128> divisors = _divisors;
|
||||
@@ -97,7 +97,7 @@ public sealed class ModResidueTracker
|
||||
List<UInt128> residues = _residues;
|
||||
if (divisors.Count == 0 || (_hasLastAppended && divisor >= _lastAppended && divisors[^1] <= divisor))
|
||||
{
|
||||
residue = ComputeInitialResidue(number, divisor);
|
||||
residue = ComputeInitialResidue(gpu, number, divisor);
|
||||
divisors.Add(divisor);
|
||||
residues.Add(residue);
|
||||
_hasLastAppended = true;
|
||||
@@ -123,7 +123,7 @@ public sealed class ModResidueTracker
|
||||
|
||||
|
||||
// Insert at cursor to keep order
|
||||
residue = ComputeInitialResidue(number, divisor);
|
||||
residue = ComputeInitialResidue(gpu, number, divisor);
|
||||
divisors.Insert(cursor, divisor);
|
||||
residues.Insert(cursor, residue);
|
||||
divisible = residue == UInt128.Zero;
|
||||
@@ -238,7 +238,7 @@ public sealed class ModResidueTracker
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private UInt128 ComputeInitialResidue(UInt128 number, UInt128 divisor)
|
||||
private UInt128 ComputeInitialResidue(PrimeOrderCalculatorAccelerator gpu, UInt128 number, UInt128 divisor)
|
||||
{
|
||||
if (_model == ResidueModel.Identity)
|
||||
{
|
||||
@@ -266,7 +266,7 @@ public sealed class ModResidueTracker
|
||||
if (pow != UInt128.One)
|
||||
return (pow - UInt128.One) % divisor;
|
||||
|
||||
ulong ord = divisor.CalculateOrder();
|
||||
ulong ord = divisor.CalculateOrder(gpu);
|
||||
if (ord == 0UL || number % (UInt128)ord != UInt128.Zero)
|
||||
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.
|
||||
if (prime <= 3UL)
|
||||
@@ -54,10 +54,8 @@ internal static partial class PrimeOrderCalculator
|
||||
|
||||
ulong phi = prime - 1UL;
|
||||
|
||||
PrimeOrderCalculatorAccelerator gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(gpu, prime, previousOrder, config, divisorData, out ulong gpuOrder))
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return gpuOrder;
|
||||
}
|
||||
|
||||
@@ -73,13 +71,11 @@ internal static partial class PrimeOrderCalculator
|
||||
// result = CalculateByFactorizationCpu(gpu, prime, divisorData);
|
||||
|
||||
phiFactors.Dispose();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = RunHeuristicPipelineCpu(gpu, prime, previousOrder, config, divisorData, phi, phiFactors);
|
||||
phiFactors.Dispose();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Buffers;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
@@ -82,7 +83,7 @@ internal static partial class PrimeOrderCalculator
|
||||
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)
|
||||
{
|
||||
@@ -100,11 +101,11 @@ internal static partial class PrimeOrderCalculator
|
||||
UInt128 result;
|
||||
if (phiFactors.Factors is null)
|
||||
{
|
||||
result = FinishStrictlyWide(prime, divisorData);
|
||||
result = FinishStrictlyWide(gpu, prime, divisorData);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = RunHeuristicPipelineWide(prime, previousOrder, divisorData, config, phi, phiFactors);
|
||||
result = RunHeuristicPipelineWide(gpu, prime, previousOrder, divisorData, config, phi, phiFactors);
|
||||
}
|
||||
|
||||
debugScope.Dispose();
|
||||
@@ -112,32 +113,33 @@ internal static partial class PrimeOrderCalculator
|
||||
}
|
||||
|
||||
private static UInt128 RunHeuristicPipelineWide(
|
||||
PrimeOrderCalculatorAccelerator gpu,
|
||||
in UInt128 prime,
|
||||
in UInt128? previousOrder,
|
||||
in MontgomeryDivisorData divisorData,
|
||||
in MontgomeryDivisorData? divisorData,
|
||||
in PrimeOrderSearchConfig config,
|
||||
in UInt128 phi,
|
||||
in PartialFactorResult128 phiFactors)
|
||||
{
|
||||
if (phiFactors.FullyFactored && TrySpecialMaxWide(phi, prime, divisorData, phiFactors))
|
||||
if (phiFactors.FullyFactored && TrySpecialMaxWide(gpu, phi, prime, divisorData, phiFactors))
|
||||
{
|
||||
return phi;
|
||||
}
|
||||
|
||||
UInt128 candidateOrder = InitializeStartingOrderWide(prime, phi, divisorData);
|
||||
candidateOrder = ExponentLoweringWide(candidateOrder, prime, divisorData, phiFactors);
|
||||
UInt128 candidateOrder = InitializeStartingOrderWide(gpu, prime, phi, divisorData);
|
||||
candidateOrder = ExponentLoweringWide(gpu, candidateOrder, prime, divisorData, phiFactors);
|
||||
|
||||
if (TryConfirmOrderWide(prime, candidateOrder, divisorData, config))
|
||||
if (TryConfirmOrderWide(gpu, prime, candidateOrder, divisorData, config))
|
||||
{
|
||||
return candidateOrder;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -145,12 +147,12 @@ internal static partial class PrimeOrderCalculator
|
||||
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;
|
||||
int length = factors.Count;
|
||||
@@ -158,7 +160,7 @@ internal static partial class PrimeOrderCalculator
|
||||
{
|
||||
UInt128 factor = factorSpan[i].Value;
|
||||
UInt128 reduced = phi / factor;
|
||||
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One)
|
||||
if (Pow2ModWide(gpu, reduced, prime, divisorData) == UInt128.One)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -167,14 +169,14 @@ internal static partial class PrimeOrderCalculator
|
||||
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 mod8 = prime & (UInt128)7UL;
|
||||
if (mod8 == UInt128.One || mod8 == (UInt128)7UL)
|
||||
{
|
||||
UInt128 half = phi >> 1;
|
||||
if (Pow2ModWide(half, prime, divisorData) == UInt128.One)
|
||||
if (Pow2ModWide(gpu, half, prime, divisorData) == UInt128.One)
|
||||
{
|
||||
order = half;
|
||||
}
|
||||
@@ -183,7 +185,7 @@ internal static partial class PrimeOrderCalculator
|
||||
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;
|
||||
ReadOnlySpan<FactorEntry128> factorSpan = factors.Factors;
|
||||
@@ -215,7 +217,7 @@ internal static partial class PrimeOrderCalculator
|
||||
if ((order % primeFactor) == UInt128.Zero)
|
||||
{
|
||||
UInt128 reduced = order / primeFactor;
|
||||
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One)
|
||||
if (Pow2ModWide(gpu, reduced, prime, divisorData) == UInt128.One)
|
||||
{
|
||||
order = reduced;
|
||||
continue;
|
||||
@@ -230,14 +232,14 @@ internal static partial class PrimeOrderCalculator
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Pow2ModWide(order, prime, divisorData) != UInt128.One)
|
||||
if (Pow2ModWide(gpu, order, prime, divisorData) != UInt128.One)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -277,7 +279,7 @@ internal static partial class PrimeOrderCalculator
|
||||
}
|
||||
|
||||
reduced /= primeFactor;
|
||||
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One)
|
||||
if (Pow2ModWide(gpu, reduced, prime, divisorData) == UInt128.One)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -288,6 +290,7 @@ internal static partial class PrimeOrderCalculator
|
||||
}
|
||||
|
||||
private static bool TryHeuristicFinishWide(
|
||||
PrimeOrderCalculatorAccelerator gpu,
|
||||
in UInt128 prime,
|
||||
in UInt128 order,
|
||||
in UInt128? previousOrder,
|
||||
@@ -347,12 +350,12 @@ internal static partial class PrimeOrderCalculator
|
||||
UInt128 candidate = candidates[i];
|
||||
powUsed++;
|
||||
|
||||
if (Pow2ModWide(candidate, prime, divisorData) != UInt128.One)
|
||||
if (Pow2ModWide(gpu, candidate, prime, divisorData) != UInt128.One)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryConfirmCandidateWide(prime, candidate, config, ref powUsed, powBudget, divisorData))
|
||||
if (!TryConfirmCandidateWide(gpu, prime, candidate, config, ref powUsed, powBudget, divisorData))
|
||||
{
|
||||
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);
|
||||
if (factorization.Factors is null)
|
||||
@@ -579,7 +582,7 @@ internal static partial class PrimeOrderCalculator
|
||||
}
|
||||
|
||||
powUsed++;
|
||||
if (Pow2ModWide(reduced, prime, divisorData) == UInt128.One)
|
||||
if (Pow2ModWide(gpu, reduced, prime, divisorData) == UInt128.One)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -855,7 +858,7 @@ internal static partial class PrimeOrderCalculator
|
||||
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;
|
||||
Dictionary<UInt128, int> counts = new(capacity: 8);
|
||||
@@ -879,7 +882,7 @@ internal static partial class PrimeOrderCalculator
|
||||
if ((order % primeFactor) != UInt128.Zero)
|
||||
{
|
||||
UInt128 candidate = order / primeFactor;
|
||||
if (Pow2ModWide(candidate, prime, divisorData) == UInt128.One)
|
||||
if (Pow2ModWide(gpu, candidate, prime, divisorData) == UInt128.One)
|
||||
{
|
||||
order = candidate;
|
||||
}
|
||||
@@ -1026,7 +1029,7 @@ internal static partial class PrimeOrderCalculator
|
||||
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)
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
return wideRemainder;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
@@ -48,6 +49,7 @@ internal static partial class PrimeOrderCalculator
|
||||
// TODO: Remove branching on the CPU / GPU paths
|
||||
// TODO: Split big non-static functions into smaller / extract static code to limit JIT time
|
||||
public static ulong Calculate(
|
||||
PrimeOrderCalculatorAccelerator gpu,
|
||||
ulong prime,
|
||||
ulong? previousOrder,
|
||||
in MontgomeryDivisorData divisorData,
|
||||
@@ -55,12 +57,13 @@ internal static partial class PrimeOrderCalculator
|
||||
PrimeOrderHeuristicDevice device)
|
||||
{
|
||||
var scope = UsePow2Mode(device);
|
||||
ulong order = CalculateInternal(prime, previousOrder, divisorData, config);
|
||||
ulong order = CalculateInternal(gpu, prime, previousOrder, divisorData, config);
|
||||
scope.Dispose();
|
||||
return order;
|
||||
}
|
||||
|
||||
public static UInt128 Calculate(
|
||||
PrimeOrderCalculatorAccelerator gpu,
|
||||
in UInt128 prime,
|
||||
in UInt128? previousOrder,
|
||||
in PrimeOrderSearchConfig config,
|
||||
@@ -88,14 +91,14 @@ internal static partial class PrimeOrderCalculator
|
||||
ulong prime64 = (ulong)prime;
|
||||
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
|
||||
divisorData = divisorPool.FromModulus(prime64);
|
||||
ulong order64 = Calculate(prime64, previous, divisorData, config, device);
|
||||
ulong order64 = Calculate(gpu, prime64, previous, divisorData, config, device);
|
||||
divisorPool.Return(divisorData);
|
||||
result = order64 == 0UL ? UInt128.Zero : (UInt128)order64;
|
||||
}
|
||||
else
|
||||
{
|
||||
divisorData = MontgomeryDivisorData.Empty;
|
||||
result = CalculateWideInternal(prime, previousOrder, divisorData, config);
|
||||
result = CalculateWideInternal(gpu, prime, previousOrder, divisorData, config);
|
||||
}
|
||||
|
||||
scope.Dispose();
|
||||
|
||||
@@ -87,45 +87,45 @@ public sealed class PrimeTester
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsPrimeGpu(ulong n)
|
||||
{
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// public static bool IsPrimeGpu(ulong n)
|
||||
// {
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||
var inputView = gpu.Input.View;
|
||||
var outputView = gpu.OutputByte.View;
|
||||
// var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
// int acceleratorIndex = gpu.AcceleratorIndex;
|
||||
// var inputView = gpu.Input.View;
|
||||
// var outputView = gpu.OutputByte.View;
|
||||
|
||||
// GpuPrimeWorkLimiter.Acquire();
|
||||
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||
inputView.CopyFromCPU(stream, ref n, 1);
|
||||
// // GpuPrimeWorkLimiter.Acquire();
|
||||
// AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||
// inputView.CopyFromCPU(stream, ref n, 1);
|
||||
|
||||
var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
|
||||
// var kernelLauncher = gpu.SmallPrimeSieveKernelLauncher;
|
||||
|
||||
kernelLauncher(
|
||||
stream,
|
||||
1,
|
||||
inputView,
|
||||
gpu.DevicePrimesLastOne,
|
||||
gpu.DevicePrimesLastSeven,
|
||||
gpu.DevicePrimesLastThree,
|
||||
gpu.DevicePrimesLastNine,
|
||||
gpu.DevicePrimesPow2LastOne,
|
||||
gpu.DevicePrimesPow2LastSeven,
|
||||
gpu.DevicePrimesPow2LastThree,
|
||||
gpu.DevicePrimesPow2LastNine,
|
||||
outputView);
|
||||
// kernelLauncher(
|
||||
// stream,
|
||||
// 1,
|
||||
// inputView,
|
||||
// gpu.DevicePrimesLastOne,
|
||||
// gpu.DevicePrimesLastSeven,
|
||||
// gpu.DevicePrimesLastThree,
|
||||
// gpu.DevicePrimesLastNine,
|
||||
// gpu.DevicePrimesPow2LastOne,
|
||||
// gpu.DevicePrimesPow2LastSeven,
|
||||
// gpu.DevicePrimesPow2LastThree,
|
||||
// gpu.DevicePrimesPow2LastNine,
|
||||
// outputView);
|
||||
|
||||
byte flag = 0;
|
||||
outputView.CopyToCPU(stream, ref flag, 1);
|
||||
stream.Synchronize();
|
||||
// byte flag = 0;
|
||||
// outputView.CopyToCPU(stream, ref flag, 1);
|
||||
// stream.Synchronize();
|
||||
|
||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
// GpuPrimeWorkLimiter.Release();
|
||||
// AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||
// PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
// // GpuPrimeWorkLimiter.Release();
|
||||
|
||||
return flag != 0;
|
||||
}
|
||||
// return flag != 0;
|
||||
// }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n)
|
||||
@@ -189,10 +189,9 @@ public sealed class PrimeTester
|
||||
}
|
||||
|
||||
[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();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(GpuBatchSize);
|
||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||
int totalLength = values.Length;
|
||||
int batchSize = GpuBatchSize;
|
||||
@@ -236,7 +235,6 @@ public sealed class PrimeTester
|
||||
|
||||
stream.Synchronize();
|
||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
// GpuPrimeWorkLimiter.Release();
|
||||
}
|
||||
|
||||
@@ -265,7 +263,7 @@ public sealed class PrimeTester
|
||||
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
|
||||
// GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers,
|
||||
@@ -275,7 +273,6 @@ public sealed class PrimeTester
|
||||
|
||||
int length = values.Length;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
int acceleratorIndex = gpu.AcceleratorIndex;
|
||||
gpu.EnsureCapacity(0, length);
|
||||
MemoryBuffer1D<ulong, Stride1D.Dense>? inputBuffer = gpu.Input;
|
||||
@@ -291,7 +288,6 @@ public sealed class PrimeTester
|
||||
stream.Synchronize();
|
||||
|
||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
@@ -516,7 +517,7 @@ public static class UInt128Extensions
|
||||
return (b1, a1);
|
||||
}
|
||||
|
||||
public static ulong CalculateOrder(this UInt128 q)
|
||||
public static ulong CalculateOrder(this UInt128 q, PrimeOrderCalculatorAccelerator gpu)
|
||||
{
|
||||
if (q <= UInt128Numbers.Two)
|
||||
{
|
||||
@@ -535,7 +536,7 @@ public static class UInt128Extensions
|
||||
ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2;
|
||||
|
||||
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
|
||||
// (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
|
||||
|
||||
@@ -44,27 +44,23 @@ public static partial class ULongExtensions
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModWindowedKeepGpu(this ulong exponent, in MontgomeryDivisorData divisor)
|
||||
=> Pow2MontgomeryGpuCalculator.CalculateKeep(exponent, divisor);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModWindowedConvertGpu(this ulong exponent, in MontgomeryDivisorData divisor)
|
||||
=> Pow2MontgomeryGpuCalculator.CalculateConvert(exponent, divisor);
|
||||
public static ulong Pow2MontgomeryModWindowedKeepGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor)
|
||||
=> Pow2MontgomeryGpuCalculator.CalculateKeep(gpu, exponent, divisor);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Pow2MontgomeryModWindowedConvertGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor)
|
||||
=> Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, exponent, divisor);
|
||||
|
||||
[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;
|
||||
return Pow2MontgomeryGpuCalculator.CalculateConvert(rotationCount, divisor);
|
||||
return Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, rotationCount, divisor);
|
||||
}
|
||||
|
||||
[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.Runtime.CompilerServices;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -111,7 +112,7 @@ public static partial class ULongExtensions
|
||||
|
||||
int i = 0, primesLength = smallPrimes.Length;
|
||||
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
|
||||
// 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
|
||||
@@ -860,7 +861,7 @@ public static partial class ULongExtensions
|
||||
/// Computes 2^exponent mod modulus using iterative CRT composition from mod 10 up to modulus.
|
||||
/// Only for modulus >= 10 and reasonable size.
|
||||
/// </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)
|
||||
return PowMod(exponent, modulus); // fallback to classic
|
||||
@@ -875,7 +876,7 @@ public static partial class ULongExtensions
|
||||
|
||||
for (; modulusCandidate <= modulus; modulusCandidate++)
|
||||
{
|
||||
cycle = MersenneDivisorCycles.GetCycle(modulusCandidate);
|
||||
cycle = MersenneDivisorCycles.GetCycle(gpu, modulusCandidate);
|
||||
remainderForCandidate = cycle > zero
|
||||
? PowModWithCycle(exponent, modulusCandidate, cycle)
|
||||
: PowMod(exponent, modulusCandidate);
|
||||
@@ -935,7 +936,7 @@ public static partial class ULongExtensions
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool SharesFactorWithExponentMinusOne(this ulong exponent)
|
||||
public static bool SharesFactorWithExponentMinusOne(this ulong exponent, PrimeOrderCalculatorAccelerator gpu)
|
||||
{
|
||||
ulong prime, value = exponent - 1UL;
|
||||
value >>= BitOperations.TrailingZeroCount(value);
|
||||
@@ -951,7 +952,7 @@ public static partial class ULongExtensions
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exponent % prime.CalculateOrder() == 0UL)
|
||||
if (exponent % prime.CalculateOrder(gpu) == 0UL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -963,7 +964,7 @@ public static partial class ULongExtensions
|
||||
while (value % prime == 0UL);
|
||||
}
|
||||
|
||||
if (value > 1UL && exponent % value.CalculateOrder() == 0UL)
|
||||
if (value > 1UL && exponent % value.CalculateOrder(gpu) == 0UL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user