Hybrid device mode implemented as the current --mersenne=bydivisor CPU execution path. CPU path is now limited to CPU calculations, only.

This commit is contained in:
mlemanczyk committed 2025-12-01 03:45:39 +01:00
1 parent 353a121d03
commit 941733905b
49 files changed
+4965 -1726

No files matched your search

@@ -398,7 +398,7 @@ public class PrimeOrderGpuHeuristicsTests
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
ulong result = PrimeOrderCalculator.Calculate(gpu, prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
ulong result = PrimeOrderCalculator.CalculateGpu(gpu, prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
result.Should().Be(3UL);
}
@@ -19,7 +19,7 @@ public class ULongExtensionsGpuTests
public void Pow2ModWindowedGpu_matches_montgomery_cpu_for_known_values(ulong exponent, ulong modulus)
{
MontgomeryDivisorData divisor = MontgomeryDivisorDataPool.Shared.FromModulus(modulus);
ulong expected = exponent.Pow2MontgomeryModWindowedCpu(divisor, keepMontgomery: false);
ulong expected = exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisor);
exponent.Pow2ModWindowedGpu(modulus).Should().Be(expected);
}
@@ -55,7 +55,7 @@ public class ULongExtensionsGpuTests
MontgomeryDivisorData divisor = MontgomeryDivisorDataPool.Shared.FromModulus(modulus);
foreach (ulong exponent in exponents)
{
ulong expected = exponent.Pow2MontgomeryModWindowedCpu(divisor, keepMontgomery: false);
ulong expected = exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisor);
exponent.Pow2ModWindowedGpu(modulus).Should().Be(expected);
}
}
@@ -41,7 +41,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
{
expected.Should().Contain(pair.divisor);
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor);
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(gpu, pair.divisor, divisorData));
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLengthCpu(pair.divisor, divisorData));
}
}
finally
@@ -59,7 +59,6 @@ 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);
@@ -92,7 +91,7 @@ public class MersenneDivisorCyclesGenerateGpuTests
{
uniqueDivisors.Add(pair.divisor).Should().BeTrue();
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor);
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(gpu, pair.divisor, divisorData));
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLengthCpu(pair.divisor, divisorData));
}
uniqueDivisors.Should().BeEquivalentTo(expected);
@@ -102,7 +101,6 @@ public class MersenneDivisorCyclesGenerateGpuTests
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
if (File.Exists(path))
{
File.Delete(path);
@@ -23,7 +23,7 @@ public class MersenneDivisorCyclesTests
try
{
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor);
MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData).Should().Be(expected);
MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData).Should().Be(expected);
MersenneDivisorCycles.CalculateCycleLengthGpu(divisor).Should().Be(expected);
}
finally
@@ -46,17 +46,17 @@ public class MersenneNumberDivisorCpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
ulong[] primes = [5UL, 7UL, 11UL, 13UL];
byte[] hits = new byte[primes.Length];
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(gpu, 23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL));
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLengthCpu(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL));
session.CheckDivisor(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL), cycle23, primes, hits);
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
hits.Should().ContainInOrder([0, 0, 1, 0]);
Array.Fill(hits, (byte)0);
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(gpu, 31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL));
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLengthCpu(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL));
session.CheckDivisor(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL), cycle31, primes, hits);
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
hits.Should().ContainInOrder([1, 0, 0, 0]);
}
finally
{
@@ -76,14 +76,14 @@ public class MersenneNumberDivisorCpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
ulong[] exponents = [6UL, 7UL, 9UL, 10UL, 12UL];
byte[] hits = new byte[exponents.Length];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL);
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 7UL, divisorData);
ulong cycle = MersenneDivisorCycles.CalculateCycleLengthCpu(7UL, divisorData);
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
hits.Should().Equal([1, 0, 1, 0, 1]);
}
finally
{
@@ -103,14 +103,14 @@ public class MersenneNumberDivisorCpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
ulong[] exponents = [10UL, 11UL, 20UL, 21UL, 30UL];
byte[] hits = new byte[exponents.Length];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL);
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 11UL, divisorData);
ulong cycle = MersenneDivisorCycles.CalculateCycleLengthCpu(11UL, divisorData);
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
hits.Should().Equal([1, 0, 1, 0, 1]);
}
finally
{
@@ -128,7 +128,7 @@ public class MersenneNumberDivisorGpuTesterTests
tester.DivisorLimit.Should().Be(0UL);
ulong[] primes = { 31UL, 37UL };
ulong[] primes = [31UL, 37UL];
ulong[] allowed = new ulong[primes.Length];
tester.PrepareCandidates(primes, allowed);
@@ -228,12 +228,12 @@ public class MersenneNumberDivisorGpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] primes = { 31UL, 37UL, 41UL };
ulong[] primes = [31UL, 37UL, 41UL];
byte[] hits = new byte[primes.Length];
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), 0UL, primes, hits);
hits.Should().ContainInOrder(new byte[] { 0, 1, 0 });
hits.Should().ContainInOrder([0, 1, 0]);
}
finally
{
@@ -253,17 +253,17 @@ public class MersenneNumberDivisorGpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
ulong[] primes = [31UL, 37UL, 41UL, 43UL];
byte[] hits = new byte[primes.Length];
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(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 });
hits.Should().ContainInOrder([0, 1, 0, 0]);
Array.Fill(hits, (byte)0);
ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLength(gpu, 13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL));
ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLengthGpu(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 });
hits.Should().ContainInOrder([0, 0, 1, 0]);
}
finally
{
@@ -287,13 +287,13 @@ public class MersenneNumberDivisorGpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
ulong[] primes = [31UL, 37UL, 41UL, 43UL, 47UL];
byte[] hits = new byte[primes.Length];
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(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 });
hits.Should().ContainInOrder([0, 1, 0, 0, 0]);
}
finally
{
@@ -316,15 +316,15 @@ public class MersenneNumberDivisorGpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
ulong[] exponents = [6UL, 7UL, 9UL, 10UL, 12UL];
byte[] hits = new byte[exponents.Length];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL);
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 7UL, divisorData);
ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 7UL, divisorData);
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
hits.Should().Equal([1, 0, 1, 0, 1]);
}
finally
{
@@ -347,15 +347,15 @@ public class MersenneNumberDivisorGpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
ulong[] exponents = [10UL, 11UL, 20UL, 21UL, 30UL];
byte[] hits = new byte[exponents.Length];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL);
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 11UL, divisorData);
ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 11UL, divisorData);
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
hits.Should().Equal([1, 0, 1, 0, 1]);
}
finally
{
@@ -382,7 +382,7 @@ public class MersenneNumberDivisorGpuTesterTests
ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(191UL);
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(gpu, 191UL, divisorData);
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 191UL, divisorData);
tableField.SetValue(cycles, patchedTable);
smallCyclesField.SetValue(cycles, patchedSmall);
@@ -423,13 +423,13 @@ public class MersenneNumberDivisorGpuTesterTests
var session = tester.CreateDivisorSession(gpu);
try
{
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
ulong[] primes = [31UL, 37UL, 41UL, 43UL, 47UL];
byte[] hits = new byte[primes.Length];
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(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 });
hits.Should().ContainInOrder([0, 1, 0, 0, 0]);
}
finally
{
@@ -25,15 +25,8 @@ public class MersenneNumberResidueCpuTesterTests
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
{
bool isPrime = true;
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);
}
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
isPrime.Should().Be(expectedPrime);
}
}
@@ -1,5 +1,6 @@
using FluentAssertions;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit;
using UInt128 = System.UInt128;
@@ -7,27 +8,36 @@ namespace PerfectNumbers.Core.Tests;
public class MersenneNumberResidueGpuTesterTests
{
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
[Theory]
[InlineData(false)]
[InlineData(true)]
[Trait("Category", "Fast")]
public void Scan_handles_various_prime_exponents(bool useGpuOrder)
{
var tester = new MersenneNumberResidueGpuTester(useGpuOrder);
[Theory]
[InlineData(false)]
[InlineData(true)]
[Trait("Category", "Fast")]
public void Scan_handles_various_prime_exponents(bool useGpuOrder)
{
var tester = new MersenneNumberResidueGpuTester(useGpuOrder);
RunCase(tester, 23UL, 2UL, expectedPrime: false);
RunCase(tester, 29UL, 37UL, expectedPrime: false);
RunCase(tester, 31UL, 1_000UL, expectedPrime: true);
RunCase(tester, 89UL, 1_001UL, expectedPrime: true);
RunCase(tester, 127UL, 1_001UL, expectedPrime: true);
}
RunCase(tester, 23UL, 2UL, expectedPrime: false);
RunCase(tester, 29UL, 37UL, expectedPrime: false);
RunCase(tester, 31UL, 1_000UL, expectedPrime: true);
RunCase(tester, 89UL, 1_001UL, expectedPrime: true);
RunCase(tester, 127UL, 1_001UL, expectedPrime: true);
}
private static void RunCase(MersenneNumberResidueGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
{
bool isPrime = true;
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
isPrime.Should().Be(expectedPrime);
}
private static void RunCase(MersenneNumberResidueGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
{
bool isPrime = true;
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);
}
}
}
@@ -27,10 +27,27 @@ public class MersenneNumberTesterTests
[Theory]
[InlineData(125UL)]
public void WarmUpOrders_populates_cache_without_affecting_results(ulong p)
public void WarmUpOrdersCpu_populates_cache_without_affecting_results(ulong p)
{
var tester = new MersenneNumberTester(useOrderCache: true, useGpuOrder: true);
tester.WarmUpOrders(p, 1_000UL);
tester.WarmUpOrdersCpu(p, 1_000UL);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tester.IsMersennePrime(gpu, p).Should().BeFalse();
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Theory]
[InlineData(125UL)]
public void WarmUpOrdersGpu_populates_cache_without_affecting_results(ulong p)
{
var tester = new MersenneNumberTester(useOrderCache: true, useGpuOrder: true);
tester.WarmUpOrdersGpu(p, 1_000UL);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
@@ -45,12 +62,20 @@ public class MersenneNumberTesterTests
[Theory]
[InlineData(125UL, true)]
[InlineData(127UL, false)]
public void SharesFactorWithExponentMinusOne_detects_common_factors(ulong p, bool expected)
public void SharesFactorWithExponentMinusOneCpu_detects_common_factors(ulong p, bool expected)
{
p.SharesFactorWithExponentMinusOneCpu().Should().Be(expected);
}
[Theory]
[InlineData(125UL, true)]
[InlineData(127UL, false)]
public void SharesFactorWithExponentMinusOneGpu_detects_common_factors(ulong p, bool expected)
{
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
p.SharesFactorWithExponentMinusOne(gpu).Should().Be(expected);
p.SharesFactorWithExponentMinusOneGpu(gpu).Should().Be(expected);
}
finally
{
@@ -8,16 +8,26 @@ namespace PerfectNumbers.Core.Tests;
public class ModResidueTrackerTests
{
[Fact]
public void IsDivisible_advances_residue_for_identity()
public void IsDivisibleCpu_advances_residue_for_identity()
{
var tracker = new ModResidueTracker(ResidueModel.Identity);
tracker.IsDivisibleCpu(5, 2).Should().BeFalse();
tracker.IsDivisibleCpu(6, 2).Should().BeTrue();
tracker.IsDivisibleCpu(7, 2).Should().BeFalse();
}
[Fact]
public void IsDivisibleGpu_advances_residue_for_identity()
{
var tracker = new ModResidueTracker(ResidueModel.Identity);
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();
tracker.IsDivisibleGpu(gpu, 5, 2).Should().BeFalse();
tracker.IsDivisibleGpu(gpu, 6, 2).Should().BeTrue();
tracker.IsDivisibleGpu(gpu, 7, 2).Should().BeFalse();
}
finally
{
@@ -31,14 +41,26 @@ public class ModResidueTrackerTests
[InlineData(5UL, 7UL, false)]
[InlineData(11UL, 23UL, true)]
[InlineData(11UL, 31UL, false)]
public void IsDivisible_handles_mersenne_model(ulong p, ulong divisor, bool expected)
public void IsDivisibleCpu_handles_mersenne_model(ulong p, ulong divisor, bool expected)
{
var tracker = new ModResidueTracker(ResidueModel.Mersenne);
tracker.IsDivisibleCpu(p, divisor).Should().Be(expected);
}
[Theory]
[InlineData(3UL, 7UL, true)]
[InlineData(5UL, 31UL, true)]
[InlineData(5UL, 7UL, false)]
[InlineData(11UL, 23UL, true)]
[InlineData(11UL, 31UL, false)]
public void IsDivisibleGpu_handles_mersenne_model(ulong p, ulong divisor, bool expected)
{
var tracker = new ModResidueTracker(ResidueModel.Mersenne);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tracker.IsDivisible(gpu, p, divisor).Should().Be(expected);
tracker.IsDivisibleGpu(gpu, p, divisor).Should().Be(expected);
}
finally
{
@@ -47,7 +69,31 @@ public class ModResidueTrackerTests
}
[Fact]
public void MergeOrAppend_tracks_existing_and_new_divisors()
public void MergeOrAppendCpu_tracks_existing_and_new_divisors()
{
var tracker = new ModResidueTracker(ResidueModel.Identity);
tracker.BeginMerge(10);
tracker.MergeOrAppendCpu(10, 3, out bool d3).Should().BeTrue();
d3.Should().BeFalse();
tracker.MergeOrAppendCpu(10, 5, out bool d5).Should().BeTrue();
d5.Should().BeTrue();
tracker.BeginMerge(10);
tracker.MergeOrAppendCpu(10, 4, out bool d4).Should().BeTrue();
d4.Should().BeFalse();
tracker.BeginMerge(11);
tracker.MergeOrAppendCpu(11, 3, out d3).Should().BeTrue();
d3.Should().BeFalse();
tracker.MergeOrAppendCpu(11, 4, out d4).Should().BeTrue();
d4.Should().BeFalse();
tracker.MergeOrAppendCpu(11, 5, out d5).Should().BeTrue();
d5.Should().BeFalse();
}
[Fact]
public void MergeOrAppendGpu_tracks_existing_and_new_divisors()
{
var tracker = new ModResidueTracker(ResidueModel.Identity);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
@@ -55,21 +101,21 @@ public class ModResidueTrackerTests
try
{
tracker.BeginMerge(10);
tracker.MergeOrAppend(gpu, 10, 3, out bool d3).Should().BeTrue();
tracker.MergeOrAppendGpu(gpu, 10, 3, out bool d3).Should().BeTrue();
d3.Should().BeFalse();
tracker.MergeOrAppend(gpu, 10, 5, out bool d5).Should().BeTrue();
tracker.MergeOrAppendGpu(gpu, 10, 5, out bool d5).Should().BeTrue();
d5.Should().BeTrue();
tracker.BeginMerge(10);
tracker.MergeOrAppend(gpu, 10, 4, out bool d4).Should().BeTrue();
tracker.MergeOrAppendGpu(gpu, 10, 4, out bool d4).Should().BeTrue();
d4.Should().BeFalse();
tracker.BeginMerge(11);
tracker.MergeOrAppend(gpu, 11, 3, out d3).Should().BeTrue();
tracker.MergeOrAppendGpu(gpu, 11, 3, out d3).Should().BeTrue();
d3.Should().BeFalse();
tracker.MergeOrAppend(gpu, 11, 4, out d4).Should().BeTrue();
tracker.MergeOrAppendGpu(gpu, 11, 4, out d4).Should().BeTrue();
d4.Should().BeFalse();
tracker.MergeOrAppend(gpu, 11, 5, out d5).Should().BeTrue();
tracker.MergeOrAppendGpu(gpu, 11, 5, out d5).Should().BeTrue();
d5.Should().BeFalse();
}
finally
@@ -79,15 +125,26 @@ public class ModResidueTrackerTests
}
[Fact]
public void IsDivisible_requires_non_decreasing_numbers()
public void IsDivisibleCpu_requires_non_decreasing_numbers()
{
var tracker = new ModResidueTracker(ResidueModel.Identity);
tracker.IsDivisibleCpu(5, 2);
Action act = () => tracker.IsDivisibleCpu(4, 2);
act.Should().Throw<InvalidOperationException>();
}
[Fact]
public void IsDivisibleGpu_requires_non_decreasing_numbers()
{
var tracker = new ModResidueTracker(ResidueModel.Identity);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
tracker.IsDivisible(gpu, 5, 2);
tracker.IsDivisibleGpu(gpu, 5, 2);
Action act = () => tracker.IsDivisible(gpu, 4, 2);
Action act = () => tracker.IsDivisibleGpu(gpu, 4, 2);
act.Should().Throw<InvalidOperationException>();
}
finally
@@ -45,13 +45,12 @@ public class PrimeOrderCalculatorTests
ulong expected = ComputeOrderByDoubling(prime);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
ulong result = PrimeOrderCalculator.Calculate(
ulong result = PrimeOrderCalculator.CalculateGpu(
gpu,
prime,
previousOrder: null,
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
PrimeOrderCalculatorAccelerator.Return(gpu);
@@ -66,21 +65,19 @@ public class PrimeOrderCalculatorTests
ulong previousOrder = ComputeOrderByDoubling(previousPrime);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
ulong heuristic = PrimeOrderCalculator.Calculate(
ulong heuristic = PrimeOrderCalculator.CalculateGpu(
gpu,
prime,
previousOrder,
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
ulong strict = PrimeOrderCalculator.Calculate(
ulong strict = PrimeOrderCalculator.CalculateGpu(
gpu,
prime,
previousOrder: null,
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault);
PrimeOrderCalculatorAccelerator.Return(gpu);
@@ -94,13 +91,12 @@ public class PrimeOrderCalculatorTests
public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder)
{
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
ulong result = PrimeOrderCalculator.Calculate(
ulong result = PrimeOrderCalculator.CalculateGpu(
gpu,
prime,
previousOrder: null,
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
PrimeOrderCalculatorAccelerator.Return(gpu);
result.Should().Be(expectedOrder);
@@ -117,13 +113,12 @@ public class PrimeOrderCalculatorTests
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
ulong result = PrimeOrderCalculator.Calculate(
ulong result = PrimeOrderCalculator.CalculateGpu(
gpu,
prime: 13UL,
previousOrder: null,
MontgomeryDivisorDataPool.Shared.FromModulus(13UL),
config,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
config);
PrimeOrderCalculatorAccelerator.Return(gpu);
result.Should().Be(ComputeOrderByDoubling(13UL));
@@ -143,13 +138,12 @@ public class PrimeOrderCalculatorTests
try
{
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(239UL);
ulong heuristic = PrimeOrderCalculator.Calculate(
ulong heuristic = PrimeOrderCalculator.CalculateGpu(
gpu,
prime: 239UL,
previousOrder: null,
divisorData,
heuristicConfig,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
heuristicConfig);
heuristic.Should().Be(ComputeOrderByDoubling(239UL));
@@ -159,13 +153,12 @@ public class PrimeOrderCalculatorTests
maxPowChecks: 1,
mode: PrimeOrderCalculator.PrimeOrderMode.Strict);
ulong strict = PrimeOrderCalculator.Calculate(
ulong strict = PrimeOrderCalculator.CalculateGpu(
gpu,
prime: 239UL,
previousOrder: null,
divisorData,
strictConfig,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
strictConfig);
strict.Should().Be(heuristic);
}
@@ -182,12 +175,11 @@ public class PrimeOrderCalculatorTests
UInt128 prime = UInt128.Parse("18446744073709641691");
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
UInt128 result = PrimeOrderCalculator.Calculate(
UInt128 result = PrimeOrderCalculator.CalculateGpu(
gpu,
prime,
previousOrder: null,
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault);
PrimeOrderCalculatorAccelerator.Return(gpu);
result.Should().Be((UInt128)1229782938247309446UL);
@@ -201,7 +193,7 @@ public class PrimeOrderCalculatorTests
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
var mersenneDivisorCycles = MersenneDivisorCycles.GetCycle(gpu, prime).Should().Be((UInt128)1229782938247309446UL);
var mersenneDivisorCycles = MersenneDivisorCycles.GetCycleGpu(gpu, prime).Should().Be((UInt128)1229782938247309446UL);
}
finally
{
@@ -51,14 +51,26 @@ public class UInt128ExtensionsTests
[InlineData(3UL)]
[InlineData(5UL)]
[InlineData(7UL)]
public void CalculateOrder_matches_naive(ulong q)
public void CalculateOrderCpu_matches_naive(ulong q)
{
UInt128 q128 = q;
ulong expected = NaiveOrder(q);
q128.CalculateOrderCpu().Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(3UL)]
[InlineData(5UL)]
[InlineData(7UL)]
public void CalculateOrderGpu_matches_naive(ulong q)
{
UInt128 q128 = q;
ulong expected = NaiveOrder(q);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
q128.CalculateOrder(gpu).Should().Be(expected);
q128.CalculateOrderGpu(gpu).Should().Be(expected);
}
finally
{
@@ -68,14 +68,27 @@ public class ULongExtensionsTests
[InlineData(5UL)]
[InlineData(7UL)]
[InlineData(11UL)]
public void CalculateOrder_matches_naive_algorithm(ulong q)
public void CalculateOrderCpu_matches_naive_algorithm(ulong q)
{
ulong expected = NaiveOrder(q);
q.CalculateOrderCpu().Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(3UL)]
[InlineData(5UL)]
[InlineData(7UL)]
[InlineData(11UL)]
public void CalculateOrderGpu_matches_naive_algorithm(ulong q)
{
ulong expected = NaiveOrder(q);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
q.CalculateOrder(gpu).Should().Be(expected);
q.CalculateOrderGpu(gpu).Should().Be(expected);
}
finally
{
@@ -119,12 +132,22 @@ public class ULongExtensionsTests
[InlineData(15UL, true)]
[InlineData(7UL, false)]
[InlineData(9UL, false)]
public void SharesFactorWithExponentMinusOne_detects_relationship(ulong exponent, bool expected)
public void SharesFactorWithExponentMinusOneCpu_detects_relationship(ulong exponent, bool expected)
{
exponent.SharesFactorWithExponentMinusOneCpu().Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(15UL, true)]
[InlineData(7UL, false)]
[InlineData(9UL, false)]
public void SharesFactorWithExponentMinusOneGpu_detects_relationship(ulong exponent, bool expected)
{
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
exponent.SharesFactorWithExponentMinusOne(gpu).Should().Be(expected);
exponent.SharesFactorWithExponentMinusOneGpu(gpu).Should().Be(expected);
}
finally
{
@@ -213,15 +236,24 @@ public class ULongExtensionsTests
[Theory]
[Trait("Category", "Fast")]
[InlineData(11UL, 13UL)]
public void PowModCrt_matches_BigInteger(ulong exponent, ulong modulus)
public void PowModCrtCpu_matches_BigInteger(ulong exponent, ulong modulus)
{
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
exponent.PowModCrtCpu((UInt128)modulus).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(11UL, 13UL)]
public void PowModCrtGpu_matches_BigInteger(ulong exponent, ulong modulus)
{
MersenneDivisorCycles cycles = MersenneDivisorCycles.Shared;
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
exponent.PowModCrt(gpu, (UInt128)modulus, cycles).Should().Be(expected);
exponent.PowModCrtGpu(gpu, (UInt128)modulus).Should().Be(expected);
}
finally
{