Require filtered primes for by-divisor scanning

This commit is contained in:
Marek Lemańczyk committed 2025-09-21 11:11:51 +02:00
1 parent b705488b35
commit 5ce0cf74fc
3 files changed
+111 -31

No files matched your search

+39 -22
View File
@@ -374,13 +374,19 @@ internal static class Program
{ {
continueCyclesGeneration = true; continueCyclesGeneration = true;
} }
} }
if (showHelp) if (useByDivisor && string.IsNullOrEmpty(filterFile))
{ {
PrintHelp(); Console.WriteLine("--mersenne=bydivisor requires --filter-p=<path>.");
return; return;
} }
if (showHelp)
{
PrintHelp();
return;
}
// Apply GPU prime sieve runtime configuration // Apply GPU prime sieve runtime configuration
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads); GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
@@ -566,12 +572,12 @@ internal static class Program
File.WriteAllText(ResultsFileName, $"p,searchedMersenne,detailedCheck,passedAllTests{Environment.NewLine}"); File.WriteAllText(ResultsFileName, $"p,searchedMersenne,detailedCheck,passedAllTests{Environment.NewLine}");
} }
bool useFilter = !string.IsNullOrEmpty(filterFile); bool useFilter = !string.IsNullOrEmpty(filterFile);
HashSet<ulong> filter = []; HashSet<ulong> filter = [];
ulong maxP = 0UL; ulong maxP = 0UL;
if (useFilter) if (useFilter)
{ {
Console.WriteLine("Loading filter..."); Console.WriteLine("Loading filter...");
ulong[] localFilter = new ulong[1024]; ulong[] localFilter = new ulong[1024];
int count = 0; int count = 0;
LoadResultsFile(filterFile, (p, detailedCheck, passedAllTests) => LoadResultsFile(filterFile, (p, detailedCheck, passedAllTests) =>
@@ -596,15 +602,26 @@ internal static class Program
if (count > 0) if (count > 0)
{ {
filter.AddRange(localFilter[..count]); filter.AddRange(localFilter[..count]);
} }
// Restore this if you want to use List<ulong> instead of HashSet<ulong> // Restore this if you want to use List<ulong> instead of HashSet<ulong>
// filter.Sort(); // filter.Sort();
} }
if (_primeCount >= 2) if (useByDivisor)
{ {
_primeFoundAfterInit = true; if (maxP <= 1UL)
} {
Console.WriteLine("The filter specified by --filter-p must contain at least one prime exponent greater than 1 for --mersenne=bydivisor.");
return;
}
_byDivisorTester!.ConfigureFromMaxPrime(maxP);
}
if (_primeCount >= 2)
{
_primeFoundAfterInit = true;
}
_outputBuilder = StringBuilderPool.Rent(); _outputBuilder = StringBuilderPool.Rent();
@@ -785,7 +802,7 @@ internal static class Program
Console.WriteLine(" --use-order test primality via q order"); Console.WriteLine(" --use-order test primality via q order");
Console.WriteLine(" --workaround-mod avoid '%' operator on the GPU"); Console.WriteLine(" --workaround-mod avoid '%' operator on the GPU");
// mod-automaton removed // mod-automaton removed
Console.WriteLine(" --filter-p=<path> process only p from previous run results"); Console.WriteLine(" --filter-p=<path> process only p from previous run results (required for --mersenne=bydivisor)");
Console.WriteLine(" --write-batch-size=<value> overwrite frequency of disk writes (default 100 lines)"); Console.WriteLine(" --write-batch-size=<value> overwrite frequency of disk writes (default 100 lines)");
Console.WriteLine(" --gcd-filter enable early sieve based on GCD"); Console.WriteLine(" --gcd-filter enable early sieve based on GCD");
Console.WriteLine(" --help, -help, --?, -?, /? show this help message"); Console.WriteLine(" --help, -help, --?, -?, /? show this help message");
@@ -1143,7 +1160,7 @@ internal static class Program
searchedMersenne = true; searchedMersenne = true;
if (_useByDivisorMode) if (_useByDivisorMode)
{ {
return _byDivisorTester!.IsPrime(p, divisorCyclesSearchLimit, out detailedCheck); return _byDivisorTester!.IsPrime(p, out detailedCheck);
} }
if (_useDivisor) if (_useDivisor)
@@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu;
using System;
using System.Numerics; using System.Numerics;
using System.Reflection; using System.Reflection;
using Xunit; using Xunit;
@@ -66,14 +67,40 @@ public class MersenneNumberDivisorGpuTesterTests
public void ByDivisor_tester_tracks_divisors_across_primes() public void ByDivisor_tester_tracks_divisors_across_primes()
{ {
var tester = new MersenneNumberDivisorByDivisorGpuTester(); var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.IsPrime(5UL, 64UL, out bool exhausted).Should().BeTrue(); tester.ConfigureFromMaxPrime(11UL);
tester.IsPrime(5UL, out bool exhausted).Should().BeTrue();
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
tester.IsPrime(7UL, 96UL, out exhausted).Should().BeTrue(); tester.IsPrime(7UL, out exhausted).Should().BeTrue();
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
tester.IsPrime(11UL, 128UL, out exhausted).Should().BeFalse(); tester.IsPrime(11UL, out exhausted).Should().BeFalse();
exhausted.Should().BeTrue(); exhausted.Should().BeTrue();
} }
[Fact]
[Trait("Category", "Fast")]
public void ByDivisor_tester_requires_configuration()
{
var tester = new MersenneNumberDivisorByDivisorGpuTester();
Action act = () => tester.IsPrime(5UL, out _);
act.Should().Throw<InvalidOperationException>();
}
[Fact]
[Trait("Category", "Fast")]
public void ByDivisor_tester_respects_filter_based_limit()
{
var tester = new MersenneNumberDivisorByDivisorGpuTester();
tester.ConfigureFromMaxPrime(5UL);
tester.IsPrime(5UL, out _).Should().BeTrue();
tester.IsPrime(7UL, out bool exhausted).Should().BeTrue();
exhausted.Should().BeTrue();
tester.IsPrime(11UL, out bool divisorsExhausted).Should().BeTrue();
divisorsExhausted.Should().BeTrue();
}
} }
@@ -16,6 +16,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
private readonly List<ulong> _lastPrimes = new(); private readonly List<ulong> _lastPrimes = new();
private readonly object _sync = new(); private readonly object _sync = new();
private ulong _divisorLimit; private ulong _divisorLimit;
private bool _isConfigured;
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>> _updateKernelCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>> _updateKernelCache = new();
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>> _initialKernelCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ulong, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>> _initialKernelCache = new();
@@ -26,12 +27,32 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
private Action<Index1D, ulong, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> GetInitialKernel(Accelerator accelerator) => private Action<Index1D, ulong, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> GetInitialKernel(Accelerator accelerator) =>
_initialKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(InitialKernel)); _initialKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(InitialKernel));
public bool IsPrime(ulong prime, ulong divisorLimit, out bool divisorsExhausted) public void ConfigureFromMaxPrime(ulong maxPrime)
{ {
lock (_sync) lock (_sync)
{ {
_divisorLimit = divisorLimit; if (_divisors.Count > 0)
ulong allowedMax = ComputeAllowedMaxDivisor(prime, divisorLimit); {
_divisors.Clear();
_remainders.Clear();
_lastPrimes.Clear();
}
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
_isConfigured = true;
}
}
public bool IsPrime(ulong prime, out bool divisorsExhausted)
{
lock (_sync)
{
if (!_isConfigured)
{
throw new InvalidOperationException("ConfigureFromMaxPrime must be called before using the tester.");
}
ulong allowedMax = ComputeAllowedMaxDivisor(prime);
if (allowedMax < 3UL) if (allowedMax < 3UL)
{ {
divisorsExhausted = true; divisorsExhausted = true;
@@ -177,7 +198,22 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
return composite; return composite;
} }
private static ulong ComputeAllowedMaxDivisor(ulong prime, ulong divisorLimit) private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime)
{
if (maxPrime <= 1UL)
{
return 0UL;
}
if (maxPrime - 1UL >= 64UL)
{
return ulong.MaxValue;
}
return (1UL << (int)(maxPrime - 1UL)) - 1UL;
}
private ulong ComputeAllowedMaxDivisor(ulong prime)
{ {
if (prime <= 1UL) if (prime <= 1UL)
{ {
@@ -186,11 +222,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester
if (prime - 1UL >= 64UL) if (prime - 1UL >= 64UL)
{ {
return divisorLimit; return _divisorLimit;
} }
ulong maxByPrime = (1UL << (int)(prime - 1UL)) - 1UL; ulong maxByPrime = (1UL << (int)(prime - 1UL)) - 1UL;
return Math.Min(maxByPrime, divisorLimit); return Math.Min(maxByPrime, _divisorLimit);
} }
private static void UpdateKernel(Index1D index, ulong prime, ArrayView<ulong> divisors, ArrayView<ulong> remainders, ArrayView<ulong> lastPrimes, ArrayView<byte> hits) private static void UpdateKernel(Index1D index, ulong prime, ArrayView<ulong> divisors, ArrayView<ulong> remainders, ArrayView<ulong> lastPrimes, ArrayView<byte> hits)