Expand residue thread coverage tests
This commit is contained in:
1 parent
e148f5b6bc
commit
33aeb88c87
3 files changed
+264
-16
No files matched your search
@@ -33,19 +33,52 @@ public class MersenneNumberResidueCpuTesterTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(31UL)]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Scan_matches_cli_residue_configuration_for_primes(ulong exponent)
|
||||||
|
{
|
||||||
|
RunCase(new MersenneNumberResidueCpuTester(), exponent, 32UL, expectedPrime: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(31UL, 1_024UL, 1UL)]
|
||||||
|
[InlineData(31UL, 512UL, 2UL)]
|
||||||
|
[InlineData(31UL, 256UL, 4UL)]
|
||||||
|
[InlineData(127UL, 256UL, 1UL)]
|
||||||
|
[InlineData(127UL, 128UL, 2UL)]
|
||||||
|
[InlineData(127UL, 64UL, 4UL)]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Scan_handles_multiple_residue_sets_for_known_primes(ulong exponent, ulong perSetLimit, ulong setCount)
|
||||||
|
{
|
||||||
|
ulong overallLimit = perSetLimit * setCount;
|
||||||
|
RunCase(new MersenneNumberResidueCpuTester(), exponent, perSetLimit, setCount, overallLimit, expectedPrime: true);
|
||||||
|
}
|
||||||
|
|
||||||
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||||
|
{
|
||||||
|
RunCase(tester, exponent, maxK, 1UL, maxK, expectedPrime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunCase(
|
||||||
|
MersenneNumberResidueCpuTester tester,
|
||||||
|
ulong exponent,
|
||||||
|
ulong perSetLimit,
|
||||||
|
ulong setCount,
|
||||||
|
ulong overallLimit,
|
||||||
|
bool expectedPrime)
|
||||||
{
|
{
|
||||||
bool isPrime = true;
|
bool isPrime = true;
|
||||||
bool exhausted = false;
|
bool exhausted = false;
|
||||||
tester.Scan(
|
tester.Scan(
|
||||||
exponent,
|
exponent,
|
||||||
(UInt128)exponent << 1,
|
(UInt128)exponent << 1,
|
||||||
LastDigitIsSeven(exponent),
|
LastDigitIsSeven(exponent),
|
||||||
(UInt128)maxK,
|
(UInt128)perSetLimit,
|
||||||
UInt128.One,
|
(UInt128)setCount,
|
||||||
(UInt128)maxK,
|
(UInt128)overallLimit,
|
||||||
ref isPrime,
|
ref isPrime,
|
||||||
ref exhausted);
|
ref exhausted);
|
||||||
isPrime.Should().Be(expectedPrime);
|
isPrime.Should().Be(expectedPrime);
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,32 @@ public class MersenneNumberResidueGpuTesterTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(31UL)]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Scan_matches_cli_residue_configuration_for_primes(ulong exponent)
|
||||||
|
{
|
||||||
|
RunCliCase(useGpuOrder: false, exponent);
|
||||||
|
RunCliCase(useGpuOrder: true, exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(31UL, 1_024UL, 1UL)]
|
||||||
|
[InlineData(31UL, 256UL, 4UL)]
|
||||||
|
[InlineData(107UL, 512UL, 1UL)]
|
||||||
|
[InlineData(107UL, 128UL, 4UL)]
|
||||||
|
[InlineData(107UL, 15_625UL, 64UL)]
|
||||||
|
[InlineData(127UL, 512UL, 1UL)]
|
||||||
|
[InlineData(127UL, 128UL, 4UL)]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Scan_handles_multiple_residue_sets_for_known_primes(ulong exponent, ulong perSetLimit, ulong setCount)
|
||||||
|
{
|
||||||
|
ulong overallLimit = perSetLimit * setCount;
|
||||||
|
|
||||||
|
RunCase(new MersenneNumberResidueGpuTester(useGpuOrder: false), exponent, perSetLimit, setCount, overallLimit, expectedPrime: true);
|
||||||
|
RunCase(new MersenneNumberResidueGpuTester(useGpuOrder: true), exponent, perSetLimit, setCount, overallLimit, expectedPrime: true);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
[InlineData(true)]
|
[InlineData(true)]
|
||||||
@@ -86,19 +112,36 @@ public class MersenneNumberResidueGpuTesterTests
|
|||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RunCliCase(bool useGpuOrder, ulong exponent)
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberResidueGpuTester(useGpuOrder);
|
||||||
|
RunCase(tester, exponent, 8UL, expectedPrime: true);
|
||||||
|
}
|
||||||
|
|
||||||
private static void RunCase(MersenneNumberResidueGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
private static void RunCase(MersenneNumberResidueGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||||
|
{
|
||||||
|
RunCase(tester, exponent, maxK, 1UL, maxK, expectedPrime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunCase(
|
||||||
|
MersenneNumberResidueGpuTester tester,
|
||||||
|
ulong exponent,
|
||||||
|
ulong perSetLimit,
|
||||||
|
ulong setCount,
|
||||||
|
ulong overallLimit,
|
||||||
|
bool expectedPrime)
|
||||||
{
|
{
|
||||||
bool isPrime = true;
|
bool isPrime = true;
|
||||||
bool exhausted = false;
|
bool exhausted = false;
|
||||||
tester.Scan(
|
tester.Scan(
|
||||||
exponent,
|
exponent,
|
||||||
(UInt128)exponent << 1,
|
(UInt128)exponent << 1,
|
||||||
LastDigitIsSeven(exponent),
|
LastDigitIsSeven(exponent),
|
||||||
(UInt128)maxK,
|
(UInt128)perSetLimit,
|
||||||
UInt128.One,
|
(UInt128)setCount,
|
||||||
(UInt128)maxK,
|
(UInt128)overallLimit,
|
||||||
ref isPrime,
|
ref isPrime,
|
||||||
ref exhausted);
|
ref exhausted);
|
||||||
isPrime.Should().Be(expectedPrime);
|
isPrime.Should().Be(expectedPrime);
|
||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using PerfectNumbers.Core;
|
||||||
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
using Xunit;
|
||||||
|
using UInt128 = System.UInt128;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Tests;
|
||||||
|
|
||||||
|
public class MersenneNumberResidueIntegrationTests
|
||||||
|
{
|
||||||
|
public static IEnumerable<object[]> ResidueThreadCounts()
|
||||||
|
{
|
||||||
|
yield return new object[] { 1 };
|
||||||
|
yield return new object[] { 2 };
|
||||||
|
yield return new object[] { 4 };
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ResidueThreadCounts))]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Residue_gpu_mode_matches_cli_configuration_for_known_primes(int threadCount)
|
||||||
|
{
|
||||||
|
ulong[] exponents = MersennePrimeTestData.Exponents;
|
||||||
|
ulong[][] partitions = PartitionExponents(exponents, threadCount);
|
||||||
|
var results = new ConcurrentDictionary<ulong, (bool IsPrime, bool Exhausted)>();
|
||||||
|
UInt128 residueSetCount = (UInt128)PerfectNumberConstants.ExtraDivisorCycleSearchLimit;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Task[] tasks = partitions.Select(partition => Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (partition.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tester = new MersenneNumberTester(
|
||||||
|
useIncremental: false,
|
||||||
|
useOrderCache: false,
|
||||||
|
kernelType: GpuKernelType.Pow2Mod,
|
||||||
|
useModuloWorkaround: false,
|
||||||
|
useOrder: false,
|
||||||
|
useGpuLucas: false,
|
||||||
|
useGpuScan: true,
|
||||||
|
useGpuOrder: true,
|
||||||
|
useResidue: true,
|
||||||
|
maxK: (UInt128)1_024UL,
|
||||||
|
residueDivisorSets: residueSetCount);
|
||||||
|
|
||||||
|
foreach (ulong exponent in partition)
|
||||||
|
{
|
||||||
|
bool isPrime = tester.IsMersennePrime(exponent, out bool divisorsExhausted);
|
||||||
|
results[exponent] = (isPrime, divisorsExhausted);
|
||||||
|
}
|
||||||
|
})).ToArray();
|
||||||
|
|
||||||
|
Task.WaitAll(tasks);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
GpuContextPool.DisposeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
results.Keys.Should().BeEquivalentTo(exponents);
|
||||||
|
foreach (ulong exponent in exponents)
|
||||||
|
{
|
||||||
|
results[exponent].Should().Be((true, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ResidueThreadCounts))]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Residue_cpu_mode_matches_cli_configuration_for_known_primes(int threadCount)
|
||||||
|
{
|
||||||
|
ulong[] exponents = MersennePrimeTestData.Exponents;
|
||||||
|
ulong[][] partitions = PartitionExponents(exponents, threadCount);
|
||||||
|
var results = new ConcurrentDictionary<ulong, (bool IsPrime, bool Exhausted)>();
|
||||||
|
UInt128 residueSetCount = (UInt128)PerfectNumberConstants.ExtraDivisorCycleSearchLimit;
|
||||||
|
|
||||||
|
Task[] tasks = partitions.Select(partition => Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (partition.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tester = new MersenneNumberTester(
|
||||||
|
useIncremental: false,
|
||||||
|
useOrderCache: false,
|
||||||
|
kernelType: GpuKernelType.Pow2Mod,
|
||||||
|
useModuloWorkaround: false,
|
||||||
|
useOrder: false,
|
||||||
|
useGpuLucas: false,
|
||||||
|
useGpuScan: false,
|
||||||
|
useGpuOrder: false,
|
||||||
|
useResidue: true,
|
||||||
|
maxK: (UInt128)1_024UL,
|
||||||
|
residueDivisorSets: residueSetCount);
|
||||||
|
|
||||||
|
foreach (ulong exponent in partition)
|
||||||
|
{
|
||||||
|
bool isPrime = tester.IsMersennePrime(exponent, out bool divisorsExhausted);
|
||||||
|
results[exponent] = (isPrime, divisorsExhausted);
|
||||||
|
}
|
||||||
|
})).ToArray();
|
||||||
|
|
||||||
|
Task.WaitAll(tasks);
|
||||||
|
|
||||||
|
results.Keys.Should().BeEquivalentTo(exponents);
|
||||||
|
foreach (ulong exponent in exponents)
|
||||||
|
{
|
||||||
|
results[exponent].Should().Be((true, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Residue_cpu_mode_respects_filter_configuration()
|
||||||
|
{
|
||||||
|
var filter = new HashSet<ulong>(MersennePrimeTestData.Exponents);
|
||||||
|
ulong[] candidates = [31UL, 37UL, 73UL];
|
||||||
|
|
||||||
|
var tester = new MersenneNumberTester(
|
||||||
|
useIncremental: false,
|
||||||
|
useOrderCache: false,
|
||||||
|
kernelType: GpuKernelType.Pow2Mod,
|
||||||
|
useModuloWorkaround: false,
|
||||||
|
useOrder: false,
|
||||||
|
useGpuLucas: false,
|
||||||
|
useGpuScan: false,
|
||||||
|
useGpuOrder: false,
|
||||||
|
useResidue: true,
|
||||||
|
maxK: (UInt128)32UL,
|
||||||
|
residueDivisorSets: (UInt128)4UL);
|
||||||
|
|
||||||
|
List<ulong> processed = new();
|
||||||
|
List<ulong> skipped = new();
|
||||||
|
|
||||||
|
filter.Count.Should().Be(MersennePrimeTestData.Exponents.Length);
|
||||||
|
|
||||||
|
foreach (ulong exponent in candidates)
|
||||||
|
{
|
||||||
|
if (!filter.Contains(exponent))
|
||||||
|
{
|
||||||
|
skipped.Add(exponent);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPrime = tester.IsMersennePrime(exponent, out bool divisorsExhausted);
|
||||||
|
isPrime.Should().BeTrue();
|
||||||
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
processed.Add(exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
processed.Should().ContainSingle().Which.Should().Be(31UL);
|
||||||
|
skipped.Should().BeEquivalentTo(new[] { 37UL, 73UL });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong[][] PartitionExponents(ulong[] exponents, int threadCount)
|
||||||
|
{
|
||||||
|
return Enumerable
|
||||||
|
.Range(0, threadCount)
|
||||||
|
.Select(partition => exponents.Where((_, index) => index % threadCount == partition).ToArray())
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user