Refine by-divisor prime test time limits
This commit is contained in:
1 parent
b47e758235
commit
6a1925dd47
9 files changed
+260
-71
No files matched your search
@@ -44,6 +44,7 @@ internal static class Program
|
|||||||
private static string? _resultsPrefix;
|
private static string? _resultsPrefix;
|
||||||
private static readonly Optimized PrimeIterator = new();
|
private static readonly Optimized PrimeIterator = new();
|
||||||
private static ulong _byDivisorStartPrime;
|
private static ulong _byDivisorStartPrime;
|
||||||
|
private static TimeSpan? _primeTestLimit;
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static bool _lastCompositeP;
|
private static bool _lastCompositeP;
|
||||||
@@ -181,6 +182,17 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
mersenneOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
mersenneOnGpu = !arg.AsSpan(arg.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
else if (arg.StartsWith("--prime-test-limit=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
ReadOnlySpan<char> value = arg.AsSpan(arg.IndexOf('=') + 1);
|
||||||
|
if (!TryParsePrimeTestLimit(value, out TimeSpan parsedLimitValue))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to parse --prime-test-limit value.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_primeTestLimit = parsedLimitValue;
|
||||||
|
}
|
||||||
else if (arg.Equals("--workaround-mod", StringComparison.OrdinalIgnoreCase))
|
else if (arg.Equals("--workaround-mod", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
useModuloWorkaround = true;
|
useModuloWorkaround = true;
|
||||||
@@ -667,7 +679,7 @@ internal static class Program
|
|||||||
|
|
||||||
if (_useByDivisorMode)
|
if (_useByDivisorMode)
|
||||||
{
|
{
|
||||||
RunByDivisorMode(byDivisorCandidates, threadCount);
|
RunByDivisorMode(byDivisorCandidates, threadCount, _primeTestLimit);
|
||||||
FlushBuffer();
|
FlushBuffer();
|
||||||
StringBuilderPool.Return(_outputBuilder!);
|
StringBuilderPool.Return(_outputBuilder!);
|
||||||
return;
|
return;
|
||||||
@@ -808,7 +820,7 @@ internal static class Program
|
|||||||
return candidates;
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RunByDivisorMode(List<ulong> candidates, int threadCount)
|
private static void RunByDivisorMode(List<ulong> candidates, int threadCount, TimeSpan? primeTestLimit)
|
||||||
{
|
{
|
||||||
if (_byDivisorTester is null)
|
if (_byDivisorTester is null)
|
||||||
{
|
{
|
||||||
@@ -825,7 +837,8 @@ internal static class Program
|
|||||||
static () => _lastCompositeP = true,
|
static () => _lastCompositeP = true,
|
||||||
static () => _lastCompositeP = false,
|
static () => _lastCompositeP = false,
|
||||||
PrintResult,
|
PrintResult,
|
||||||
threadCount);
|
threadCount,
|
||||||
|
primeTestLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe void LoadResultsFile(string resultsFileName, Action<ulong, bool, bool> lineProcessorAction)
|
private static unsafe void LoadResultsFile(string resultsFileName, Action<ulong, bool, bool> lineProcessorAction)
|
||||||
@@ -892,6 +905,51 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParsePrimeTestLimit(ReadOnlySpan<char> value, out TimeSpan result)
|
||||||
|
{
|
||||||
|
result = TimeSpan.Zero;
|
||||||
|
if (value.IsEmpty)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<char> magnitudeSpan = value;
|
||||||
|
double magnitude;
|
||||||
|
|
||||||
|
if (value.Length >= 2 && value.EndsWith("ms", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
magnitudeSpan = value[..^2];
|
||||||
|
if (!double.TryParse(magnitudeSpan, NumberStyles.Float, CultureInfo.InvariantCulture, out magnitude))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = TimeSpan.FromMilliseconds(magnitude);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char suffix = char.ToLowerInvariant(value[^1]);
|
||||||
|
if (suffix == 's' || suffix == 'm')
|
||||||
|
{
|
||||||
|
magnitudeSpan = value[..^1];
|
||||||
|
if (!double.TryParse(magnitudeSpan, NumberStyles.Float, CultureInfo.InvariantCulture, out magnitude))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = suffix == 's' ? TimeSpan.FromSeconds(magnitude) : TimeSpan.FromMinutes(magnitude);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out magnitude))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = TimeSpan.FromMilliseconds(magnitude);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void PrintHelp()
|
private static void PrintHelp()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Usage: EvenPerfectBitScanner [options]");
|
Console.WriteLine("Usage: EvenPerfectBitScanner [options]");
|
||||||
@@ -925,6 +983,7 @@ internal static class Program
|
|||||||
Console.WriteLine(" --divisor-cycles-batch=<value> batch size for cycles generation (default 512)");
|
Console.WriteLine(" --divisor-cycles-batch=<value> batch size for cycles generation (default 512)");
|
||||||
Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation");
|
Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation");
|
||||||
Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor");
|
Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor");
|
||||||
|
Console.WriteLine(" --prime-test-limit=<value> Time limit for --mersenne=bydivisor prime checks (e.g. 5s, 500ms)");
|
||||||
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
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
markComposite: () => compositeMarks++,
|
markComposite: () => compositeMarks++,
|
||||||
clearComposite: () => clearedMarks++,
|
clearComposite: () => clearedMarks++,
|
||||||
printResult: (prime, searched, detailed, passed) => results.Add((prime, searched, detailed, passed)),
|
printResult: (prime, searched, detailed, passed) => results.Add((prime, searched, detailed, passed)),
|
||||||
threadCount: 1);
|
threadCount: 1,
|
||||||
|
primeTestLimit: null);
|
||||||
|
|
||||||
tester.ConfiguredMaxPrime.Should().Be(13UL);
|
tester.ConfiguredMaxPrime.Should().Be(13UL);
|
||||||
compositeMarks.Should().Be(2);
|
compositeMarks.Should().Be(2);
|
||||||
@@ -59,7 +60,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
|||||||
return DivisorLimit;
|
return DivisorLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
public bool IsPrime(ulong prime, out bool divisorsExhausted, TimeSpan? timeLimit = null)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
divisorsExhausted = true;
|
||||||
return prime % 7UL != 0UL && prime % 11UL != 0UL;
|
return prime % 7UL != 0UL && prime % 11UL != 0UL;
|
||||||
|
|||||||
@@ -7,78 +7,88 @@ namespace PerfectNumbers.Core.Tests;
|
|||||||
|
|
||||||
public class MersenneNumberDivisorCpuTesterTests
|
public class MersenneNumberDivisorCpuTesterTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Category", "Fast")]
|
[Trait("Category", "Fast")]
|
||||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||||
{
|
{
|
||||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||||
tester.ConfigureFromMaxPrime(11UL);
|
tester.ConfigureFromMaxPrime(11UL);
|
||||||
|
|
||||||
tester.IsPrime(5UL, out bool divisorsExhausted).Should().BeTrue();
|
tester.IsPrime(5UL, out bool divisorsExhausted).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
|
||||||
tester.IsPrime(7UL, out divisorsExhausted).Should().BeTrue();
|
tester.IsPrime(7UL, out divisorsExhausted).Should().BeTrue();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
|
||||||
tester.IsPrime(11UL, out divisorsExhausted).Should().BeFalse();
|
tester.IsPrime(11UL, out divisorsExhausted).Should().BeFalse();
|
||||||
divisorsExhausted.Should().BeTrue();
|
divisorsExhausted.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Category", "Fast")]
|
[Trait("Category", "Fast")]
|
||||||
public void ByDivisor_session_checks_divisors_across_primes()
|
public void ByDivisor_prime_test_limit_disables_divisor_exhaustion()
|
||||||
{
|
{
|
||||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||||
tester.ConfigureFromMaxPrime(13UL);
|
tester.ConfigureFromMaxPrime(11UL);
|
||||||
|
|
||||||
using var session = tester.CreateDivisorSession();
|
tester.IsPrime(5UL, out bool divisorsExhausted, TimeSpan.Zero).Should().BeTrue();
|
||||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
divisorsExhausted.Should().BeFalse();
|
||||||
byte[] hits = new byte[primes.Length];
|
}
|
||||||
|
|
||||||
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(23UL, MontgomeryDivisorData.FromModulus(23UL));
|
[Fact]
|
||||||
session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primes, hits);
|
[Trait("Category", "Fast")]
|
||||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
public void ByDivisor_session_checks_divisors_across_primes()
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||||
|
tester.ConfigureFromMaxPrime(13UL);
|
||||||
|
|
||||||
Array.Fill(hits, (byte)0);
|
using var session = tester.CreateDivisorSession();
|
||||||
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(31UL, MontgomeryDivisorData.FromModulus(31UL));
|
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||||
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits);
|
byte[] hits = new byte[primes.Length];
|
||||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(23UL, MontgomeryDivisorData.FromModulus(23UL));
|
||||||
[Trait("Category", "Fast")]
|
session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primes, hits);
|
||||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||||
{
|
|
||||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
|
||||||
tester.ConfigureFromMaxPrime(43UL);
|
|
||||||
|
|
||||||
using var session = tester.CreateDivisorSession();
|
Array.Fill(hits, (byte)0);
|
||||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(31UL, MontgomeryDivisorData.FromModulus(31UL));
|
||||||
byte[] hits = new byte[exponents.Length];
|
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits);
|
||||||
|
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||||
|
}
|
||||||
|
|
||||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
[Fact]
|
||||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
[Trait("Category", "Fast")]
|
||||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||||
|
tester.ConfigureFromMaxPrime(43UL);
|
||||||
|
|
||||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
using var session = tester.CreateDivisorSession();
|
||||||
}
|
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||||
|
byte[] hits = new byte[exponents.Length];
|
||||||
|
|
||||||
[Fact]
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||||
[Trait("Category", "Fast")]
|
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||||
{
|
|
||||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
|
||||||
tester.ConfigureFromMaxPrime(61UL);
|
|
||||||
|
|
||||||
using var session = tester.CreateDivisorSession();
|
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
}
|
||||||
byte[] hits = new byte[exponents.Length];
|
|
||||||
|
|
||||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
[Fact]
|
||||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
[Trait("Category", "Fast")]
|
||||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||||
|
tester.ConfigureFromMaxPrime(61UL);
|
||||||
|
|
||||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
using var session = tester.CreateDivisorSession();
|
||||||
}
|
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||||
|
byte[] hits = new byte[exponents.Length];
|
||||||
|
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||||
|
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||||
|
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||||
|
|
||||||
|
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +86,17 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
exhausted.Should().BeTrue();
|
exhausted.Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void ByDivisor_gpu_prime_test_limit_stops_scan_immediately()
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||||
|
tester.ConfigureFromMaxPrime(43UL);
|
||||||
|
|
||||||
|
tester.IsPrime(31UL, out bool divisorsExhausted, TimeSpan.Zero).Should().BeTrue();
|
||||||
|
divisorsExhausted.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Category", "Fast")]
|
[Trait("Category", "Fast")]
|
||||||
public void ByDivisor_tester_requires_configuration()
|
public void ByDivisor_tester_requires_configuration()
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
public bool IsPrime(ulong prime, out bool divisorsExhausted, TimeSpan? timeLimit = null)
|
||||||
{
|
{
|
||||||
ulong allowedMax;
|
ulong allowedMax;
|
||||||
lock (_sync)
|
lock (_sync)
|
||||||
@@ -89,7 +89,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
allowedMax,
|
allowedMax,
|
||||||
out lastProcessed,
|
out lastProcessed,
|
||||||
out processedAll,
|
out processedAll,
|
||||||
out processedCount);
|
out processedCount,
|
||||||
|
timeLimit);
|
||||||
|
|
||||||
if (processedCount > 0UL)
|
if (processedCount > 0UL)
|
||||||
{
|
{
|
||||||
@@ -163,12 +164,22 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
ulong allowedMax,
|
ulong allowedMax,
|
||||||
out ulong lastProcessed,
|
out ulong lastProcessed,
|
||||||
out bool processedAll,
|
out bool processedAll,
|
||||||
out ulong processedCount)
|
out ulong processedCount,
|
||||||
|
TimeSpan? timeLimit)
|
||||||
{
|
{
|
||||||
lastProcessed = 0UL;
|
lastProcessed = 0UL;
|
||||||
processedCount = 0UL;
|
processedCount = 0UL;
|
||||||
processedAll = false;
|
processedAll = false;
|
||||||
|
|
||||||
|
PrimeTestTimeLimit limitGuard;
|
||||||
|
if (!PrimeTestTimeLimit.TryCreate(timeLimit, out limitGuard))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enforceLimit = limitGuard.IsActive;
|
||||||
|
ulong iterationCounter = 0UL;
|
||||||
|
|
||||||
if (allowedMax < 3UL)
|
if (allowedMax < 3UL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -216,6 +227,13 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
while (divisor <= limit)
|
while (divisor <= limit)
|
||||||
{
|
{
|
||||||
|
if (enforceLimit && (iterationCounter == 0UL || (iterationCounter & 63UL) == 0UL) && limitGuard.HasExpired())
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterationCounter++;
|
||||||
ulong candidate = (ulong)divisor;
|
ulong candidate = (ulong)divisor;
|
||||||
processedCount++;
|
processedCount++;
|
||||||
lastProcessed = candidate;
|
lastProcessed = candidate;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
public bool IsPrime(ulong prime, out bool divisorsExhausted, TimeSpan? timeLimit = null)
|
||||||
{
|
{
|
||||||
ulong allowedMax;
|
ulong allowedMax;
|
||||||
int batchCapacity;
|
int batchCapacity;
|
||||||
@@ -108,6 +108,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
composite = CheckDivisors(
|
composite = CheckDivisors(
|
||||||
prime,
|
prime,
|
||||||
allowedMax,
|
allowedMax,
|
||||||
|
timeLimit,
|
||||||
accelerator,
|
accelerator,
|
||||||
kernel,
|
kernel,
|
||||||
resources.DivisorsBuffer,
|
resources.DivisorsBuffer,
|
||||||
@@ -175,6 +176,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
private static bool CheckDivisors(
|
private static bool CheckDivisors(
|
||||||
ulong prime,
|
ulong prime,
|
||||||
ulong allowedMax,
|
ulong allowedMax,
|
||||||
|
TimeSpan? timeLimit,
|
||||||
Accelerator accelerator,
|
Accelerator accelerator,
|
||||||
Action<Index1D, ArrayView<MontgomeryDivisorData>, ArrayView<ulong>, ArrayView<byte>> kernel,
|
Action<Index1D, ArrayView<MontgomeryDivisorData>, ArrayView<ulong>, ArrayView<byte>> kernel,
|
||||||
MemoryBuffer1D<MontgomeryDivisorData, Stride1D.Dense> divisorsBuffer,
|
MemoryBuffer1D<MontgomeryDivisorData, Stride1D.Dense> divisorsBuffer,
|
||||||
@@ -202,6 +204,15 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
bool processedAll = false;
|
bool processedAll = false;
|
||||||
processedCount = 0UL;
|
processedCount = 0UL;
|
||||||
lastProcessed = 0UL;
|
lastProcessed = 0UL;
|
||||||
|
coveredRange = false;
|
||||||
|
|
||||||
|
PrimeTestTimeLimit limitGuard;
|
||||||
|
if (!PrimeTestTimeLimit.TryCreate(timeLimit, out limitGuard))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enforceLimit = limitGuard.IsActive;
|
||||||
|
|
||||||
Span<MontgomeryDivisorData> divisorDataSpan;
|
Span<MontgomeryDivisorData> divisorDataSpan;
|
||||||
Span<ulong> divisorSpan;
|
Span<ulong> divisorSpan;
|
||||||
@@ -277,6 +288,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
{
|
{
|
||||||
while (currentK <= maxK)
|
while (currentK <= maxK)
|
||||||
{
|
{
|
||||||
|
if (enforceLimit && limitGuard.HasExpired())
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int chunkCount = Math.Min(chunkCapacity, batchCapacity);
|
int chunkCount = Math.Min(chunkCapacity, batchCapacity);
|
||||||
ulong remainingK = maxK - currentK + 1UL;
|
ulong remainingK = maxK - currentK + 1UL;
|
||||||
if ((ulong)chunkCount > remainingK)
|
if ((ulong)chunkCount > remainingK)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public interface IMersenneNumberDivisorByDivisorTester
|
|||||||
|
|
||||||
ulong GetAllowedMaxDivisor(ulong prime);
|
ulong GetAllowedMaxDivisor(ulong prime);
|
||||||
|
|
||||||
bool IsPrime(ulong prime, out bool divisorsExhausted);
|
bool IsPrime(ulong prime, out bool divisorsExhausted, TimeSpan? timeLimit = null);
|
||||||
|
|
||||||
IDivisorScanSession CreateDivisorSession();
|
IDivisorScanSession CreateDivisorSession();
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
Action markComposite,
|
Action markComposite,
|
||||||
Action clearComposite,
|
Action clearComposite,
|
||||||
Action<ulong, bool, bool, bool> printResult,
|
Action<ulong, bool, bool, bool> printResult,
|
||||||
int threadCount)
|
int threadCount,
|
||||||
|
TimeSpan? primeTestLimit)
|
||||||
{
|
{
|
||||||
if (candidates.Count == 0)
|
if (candidates.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -243,7 +244,7 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
|
|
||||||
void ProcessPrime(ulong prime)
|
void ProcessPrime(ulong prime)
|
||||||
{
|
{
|
||||||
bool isPrime = tester.IsPrime(prime, out bool divisorsExhausted);
|
bool isPrime = tester.IsPrime(prime, out bool divisorsExhausted, primeTestLimit);
|
||||||
|
|
||||||
if (!isPrime)
|
if (!isPrime)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
|
internal readonly struct PrimeTestTimeLimit
|
||||||
|
{
|
||||||
|
private readonly long _deadlineTimestamp;
|
||||||
|
|
||||||
|
private PrimeTestTimeLimit(bool isActive, long deadlineTimestamp)
|
||||||
|
{
|
||||||
|
IsActive = isActive;
|
||||||
|
_deadlineTimestamp = deadlineTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool IsActive { get; }
|
||||||
|
|
||||||
|
internal static bool TryCreate(TimeSpan? timeLimit, out PrimeTestTimeLimit limit)
|
||||||
|
{
|
||||||
|
if (!timeLimit.HasValue)
|
||||||
|
{
|
||||||
|
limit = new PrimeTestTimeLimit(isActive: false, deadlineTimestamp: long.MaxValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpan limitValue = timeLimit.GetValueOrDefault();
|
||||||
|
if (limitValue <= TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
limit = new PrimeTestTimeLimit(isActive: true, deadlineTimestamp: long.MinValue);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double seconds = limitValue.TotalSeconds;
|
||||||
|
if (double.IsInfinity(seconds))
|
||||||
|
{
|
||||||
|
limit = new PrimeTestTimeLimit(isActive: true, deadlineTimestamp: long.MaxValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double scaled = seconds * Stopwatch.Frequency;
|
||||||
|
long limitTicks = scaled >= long.MaxValue ? long.MaxValue : (long)scaled;
|
||||||
|
long startTimestamp = Stopwatch.GetTimestamp();
|
||||||
|
long deadline = startTimestamp <= long.MaxValue - limitTicks ? startTimestamp + limitTicks : long.MaxValue;
|
||||||
|
if (deadline < startTimestamp)
|
||||||
|
{
|
||||||
|
deadline = long.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
limit = new PrimeTestTimeLimit(isActive: true, deadlineTimestamp: deadline);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool HasExpired()
|
||||||
|
{
|
||||||
|
if (!IsActive)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_deadlineTimestamp == long.MaxValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_deadlineTimestamp == long.MinValue)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stopwatch.GetTimestamp() >= _deadlineTimestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user