Predictive scanning implemented for bydivisor method.

This commit is contained in:
mlemanczyk committed 2026-01-03 20:01:22 +01:00
1 parent ef8034b258
commit a6d408f2aa
10 files changed
+1562 -40

No files matched your search

+39 -21
View File
@@ -25,17 +25,19 @@ namespace EvenPerfectBitScanner;
internal readonly bool UseResidue;
internal readonly bool UseDivisor;
internal readonly bool UseByDivisor;
internal readonly bool UseDivisorPow2Increment;
internal readonly int ByDivisorSpecialRange;
internal readonly bool UseDivisorPow2Increment;
internal readonly bool UseDivisorPredictiveIncrement;
internal readonly int ByDivisorSpecialRange;
internal readonly bool UseGcdFilter;
internal readonly bool TestMode;
internal readonly bool UseGpuCycles;
internal readonly ComputationDevice MersenneDevice;
internal readonly ComputationDevice OrderDevice;
internal readonly int ScanBatchSize;
internal readonly int SliceSize;
internal readonly ulong ResidueKMax;
internal readonly string FilterFile;
internal readonly bool TestMode;
internal readonly bool UseGpuCycles;
internal readonly ComputationDevice MersenneDevice;
internal readonly ComputationDevice OrderDevice;
internal readonly int ScanBatchSize;
internal readonly int SliceSize;
internal readonly ulong MaxKLimit;
internal readonly bool MaxKConfigured;
internal readonly string FilterFile;
internal readonly bool ForcePrimeKernelsOnCpu;
internal readonly ModReductionMode ModReductionMode;
internal readonly NttBackend NttBackend;
@@ -75,6 +77,7 @@ namespace EvenPerfectBitScanner;
bool useByDivisor,
bool useGcdFilter,
bool useDivisorPow2Increment,
bool useDivisorPredictiveIncrement,
int byDivisorSpecialRange,
bool testMode,
bool useGpuCycles,
@@ -82,7 +85,8 @@ namespace EvenPerfectBitScanner;
ComputationDevice orderDevice,
int scanBatchSize,
int sliceSize,
ulong residueKMax,
ulong maxKLimit,
bool maxKConfigured,
string filterFile,
bool forcePrimeKernelsOnCpu,
ModReductionMode modReductionMode,
@@ -121,6 +125,7 @@ namespace EvenPerfectBitScanner;
UseDivisor = useDivisor;
UseByDivisor = useByDivisor;
UseDivisorPow2Increment = useDivisorPow2Increment;
UseDivisorPredictiveIncrement = useDivisorPredictiveIncrement;
ByDivisorSpecialRange = byDivisorSpecialRange;
UseGcdFilter = useGcdFilter;
TestMode = testMode;
@@ -129,7 +134,8 @@ namespace EvenPerfectBitScanner;
OrderDevice = orderDevice;
ScanBatchSize = scanBatchSize;
SliceSize = sliceSize;
ResidueKMax = residueKMax;
MaxKLimit = maxKLimit;
MaxKConfigured = maxKConfigured;
FilterFile = filterFile;
ForcePrimeKernelsOnCpu = forcePrimeKernelsOnCpu;
ModReductionMode = modReductionMode;
@@ -173,6 +179,7 @@ namespace EvenPerfectBitScanner;
bool useDivisor = false;
bool useByDivisor = false;
bool useDivisorPow2Increment = false;
bool useDivisorPredictiveIncrement = false;
int byDivisorSpecialRange = 0;
bool useGcdFilter = false;
bool testMode = false;
@@ -181,7 +188,8 @@ namespace EvenPerfectBitScanner;
ComputationDevice orderDevice = ComputationDevice.Gpu;
int scanBatchSize = 2_097_152;
int sliceSize = 32;
ulong residueKMax = 5_000_000UL;
ulong maxKLimit = 5_000_000UL;
bool maxKConfigured = false;
string filterFile = string.Empty;
bool forcePrimeKernelsOnCpu = false;
ModReductionMode modReductionMode = ModReductionMode.Auto;
@@ -189,7 +197,7 @@ namespace EvenPerfectBitScanner;
string? resultsDirectory = null;
string? resultsPrefix = null;
int writeBatchSize = CalculationResultHandler.DefaultWriteBatchSize;
string cyclesPath = DefaultCyclesPath;
string cyclesPath = DefaultCyclesPath;
bool continueCyclesGeneration = false;
int cyclesBatchSize = 512;
ulong divisorCyclesSearchLimit = PerfectNumberConstants.ExtraDivisorCycleSearchLimit;
@@ -307,14 +315,21 @@ namespace EvenPerfectBitScanner;
if (value.Equals("pow2groups", StringComparison.OrdinalIgnoreCase))
{
useDivisorPow2Increment = true;
useDivisorPredictiveIncrement = false;
}
else if (value.Equals("sequential", StringComparison.OrdinalIgnoreCase))
{
useDivisorPow2Increment = false;
useDivisorPredictiveIncrement = false;
}
else if (value.Equals("predictive", StringComparison.OrdinalIgnoreCase))
{
useDivisorPow2Increment = false;
useDivisorPredictiveIncrement = true;
}
else
{
Console.WriteLine("Invalid value for --bydivisor-k-increment. Use onebyone or pow2groups.");
Console.WriteLine("Invalid value for --bydivisor-k-increment. Use sequential, pow2groups, or predictive.");
return default;
}
@@ -346,11 +361,12 @@ namespace EvenPerfectBitScanner;
continue;
}
if (argument.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase))
if (argument.StartsWith("--max-k=", StringComparison.OrdinalIgnoreCase))
{
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--residue-max-k=".Length), out ulong parsedResidueMax))
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--max-k=".Length), out ulong parsedMaxK))
{
residueKMax = parsedResidueMax;
maxKLimit = parsedMaxK;
maxKConfigured = true;
}
continue;
@@ -637,6 +653,7 @@ namespace EvenPerfectBitScanner;
useByDivisor,
useGcdFilter,
useDivisorPow2Increment,
useDivisorPredictiveIncrement,
byDivisorSpecialRange,
testMode,
useGpuCycles,
@@ -644,7 +661,8 @@ namespace EvenPerfectBitScanner;
orderDevice,
scanBatchSize,
sliceSize,
residueKMax,
maxKLimit,
maxKConfigured,
filterFile,
forcePrimeKernelsOnCpu,
modReductionMode,
@@ -678,9 +696,9 @@ namespace EvenPerfectBitScanner;
Console.WriteLine(" --increment=bit|add exponent increment method (default add)");
Console.WriteLine(" --threads=<value> number of worker threads");
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
Console.WriteLine(" --bydivisor-k-increment=sequential|pow2groups k iteration strategy for --mersenne=bydivisor (default sequential)");
Console.WriteLine(" --bydivisor-k-increment=sequential|pow2groups|predictive k iteration strategy for --mersenne=bydivisor (default sequential)");
Console.WriteLine(" --bydivisor-special-range=<n> widen pow2groups special values to +/- n percent (default 0 = points)");
Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)");
Console.WriteLine(" --max-k=<value> max k for Mersenne tests (q = 2*p*k + 1), including --mersenne=bydivisor");
Console.WriteLine(" --mersenne-device=cpu|gpu|hybrid Device for Mersenne method (default gpu)");
Console.WriteLine(" --primes-device=cpu|gpu device for prime-scan kernels (default gpu)");
Console.WriteLine(" --gpu-prime-batch=<n> batch size for GPU primality sieve (default 262144)");
+172 -8
View File
@@ -3,9 +3,11 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
using EvenPerfectBitScanner.Candidates;
using EvenPerfectBitScanner.IO;
using System.Numerics;
using Open.Collections;
using PerfectNumbers.Core;
using PerfectNumbers.Core.Cpu;
using PerfectNumbers.Core.ByDivisor;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Accelerators;
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForOneByOneDivisorSetForCpuOrder;
@@ -14,6 +16,9 @@ using MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder = PerfectNumbers.Core.
using ByDivisorCpuOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForCpuOrder;
using ByDivisorHybridOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForHybridOrder;
using ByDivisorGpuOrderPow2 = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPow2GroupsDivisorSetForGpuOrder;
using ByDivisorCpuOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForCpuOrder;
using ByDivisorHybridOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForHybridOrder;
using ByDivisorGpuOrderPredictive = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForPredictiveDivisorSetForGpuOrder;
using System.Globalization;
namespace EvenPerfectBitScanner;
@@ -31,12 +36,20 @@ private static ThreadLocal<object>? MersenneTesters;
private static ByDivisorCpuOrderPow2 _byDivisorCpuOrderTesterPow2;
private static ByDivisorHybridOrderPow2 _byDivisorHybridOrderTesterPow2;
private static ByDivisorGpuOrderPow2 _byDivisorGpuOrderTesterPow2;
private static ByDivisorCpuOrderPredictive _byDivisorCpuOrderTesterPredictive;
private static ByDivisorHybridOrderPredictive _byDivisorHybridOrderTesterPredictive;
private static ByDivisorGpuOrderPredictive _byDivisorGpuOrderTesterPredictive;
private static Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? _byDivisorPreviousResults;
private static string? _byDivisorModelPath;
private static bool _limitReached;
private static ulong _byDivisorStartPrime;
private static CliArguments _cliArguments;
private static int _testTargetPrimeCount;
private static int _testProcessedPrimeCount;
private static readonly object _byDivisorModelRefreshSync = new();
private static int _byDivisorPendingRebuilds;
private static DateTime _byDivisorLastRebuildUtc = DateTime.MinValue;
private static readonly TimeSpan _byDivisorRebuildMinInterval = TimeSpan.FromSeconds(30);
private static ComputationDevice _mersenneDevice;
private static OrderDevice _orderCalculationDevice;
private static CacheStatus _cacheStatus;
@@ -125,7 +138,8 @@ private static ThreadLocal<object>? MersenneTesters;
_orderCalculationDevice = orderCalculationDevice;
_cacheStatus = CacheStatus.Disabled;
int sliceSize = Math.Max(1, _cliArguments.SliceSize);
ulong residueKMax = _cliArguments.ResidueKMax;
ulong maxKLimit = _cliArguments.MaxKLimit;
bool maxKConfigured = _cliArguments.MaxKConfigured;
string filterFile = _cliArguments.FilterFile;
UInt128 maxPrimeLimit = _cliArguments.MaxPrimeLimit;
bool maxPrimeConfigured = _cliArguments.MaxPrimeConfigured;
@@ -161,6 +175,12 @@ private static ThreadLocal<object>? MersenneTesters;
Directory.CreateDirectory(PerfectNumberConstants.ByDivisorStateDirectory);
if (useByDivisor && !maxKConfigured)
{
// For by-divisor mode without explicit --max-k, rely on per-prime limits.
maxKLimit = ulong.MaxValue;
}
if (!File.Exists(cyclesPath) || continueCyclesGeneration)
{
@@ -246,7 +266,7 @@ private static ThreadLocal<object>? MersenneTesters;
_orderCalculationDevice,
mersenneDevice,
kernelType,
residueKMax);
maxKLimit);
if (!useLucas)
{
Console.WriteLine("Warming up orders");
@@ -270,12 +290,17 @@ private static ThreadLocal<object>? MersenneTesters;
}
bool usePow2Divisors = _cliArguments.UseDivisorPow2Increment;
bool usePredictiveDivisors = _cliArguments.UseDivisorPredictiveIncrement;
if (EnvironmentConfiguration.UseCpuOrder)
{
if (usePow2Divisors)
{
_byDivisorCpuOrderTesterPow2 = new ByDivisorCpuOrderPow2();
}
else if (usePredictiveDivisors)
{
_byDivisorCpuOrderTesterPredictive = new ByDivisorCpuOrderPredictive();
}
else
{
_byDivisorCpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder();
@@ -287,6 +312,10 @@ private static ThreadLocal<object>? MersenneTesters;
{
_byDivisorHybridOrderTesterPow2 = new ByDivisorHybridOrderPow2();
}
else if (usePredictiveDivisors)
{
_byDivisorHybridOrderTesterPredictive = new ByDivisorHybridOrderPredictive();
}
else
{
_byDivisorHybridOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder();
@@ -298,6 +327,10 @@ private static ThreadLocal<object>? MersenneTesters;
{
_byDivisorGpuOrderTesterPow2 = new ByDivisorGpuOrderPow2();
}
else if (usePredictiveDivisors)
{
_byDivisorGpuOrderTesterPredictive = new ByDivisorGpuOrderPredictive();
}
else
{
_byDivisorGpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder();
@@ -402,6 +435,29 @@ private static ThreadLocal<object>? MersenneTesters;
CalculationResultHandler.CreateResultsFileWithHeader();
}
if (useByDivisor)
{
_byDivisorModelPath = Path.Combine(PerfectNumberConstants.ByDivisorStateDirectory, "bydivisor_k10_model.json");
var byDivisorTuning = new ByDivisorScanTuning
{
CheapLimit = EnvironmentConfiguration.ByDivisorCheapKLimit,
};
EnvironmentConfiguration.ByDivisorScanTuning = byDivisorTuning;
bool modelExists = File.Exists(_byDivisorModelPath);
bool resultsExist = File.Exists(resultsFileName);
bool needsBuild = !modelExists || (resultsExist && File.GetLastWriteTimeUtc(resultsFileName) > File.GetLastWriteTimeUtc(_byDivisorModelPath));
Console.WriteLine(needsBuild
? $"Building by-divisor class model from {resultsFileName}..."
: $"Loading by-divisor class model from {_byDivisorModelPath}...");
EnvironmentConfiguration.ByDivisorClassModel = ByDivisorClassModelLoader.LoadOrBuild(
_byDivisorModelPath,
resultsFileName,
byDivisorTuning,
byDivisorTuning.CheapLimit,
rebuildWhenResultsNewer: true);
Console.WriteLine($"By-divisor class model ready (cheap<={byDivisorTuning.CheapLimit:F0}).");
}
bool useFilter = !testMode && !string.IsNullOrEmpty(filterFile);
HashSet<ulong> filter = [];
List<ulong> byDivisorCandidates = [];
@@ -518,6 +574,7 @@ private static ThreadLocal<object>? MersenneTesters;
{
int byDivisorPrimeRange = Math.Max(1, _cliArguments.BlockSize);
bool usePow2Divisors = _cliArguments.UseDivisorPow2Increment;
bool usePredictiveDivisors = _cliArguments.UseDivisorPredictiveIncrement;
if (EnvironmentConfiguration.UseCpuOrder)
{
if (usePow2Divisors)
@@ -530,7 +587,21 @@ private static ThreadLocal<object>? MersenneTesters;
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
static (candidate, searchedMersenne, detailedCheck, passedAllTests, divisor) => CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
else if (usePredictiveDivisors)
{
Console.WriteLine("Using by-divisor predictive increment (CPU order).");
MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates,
ref _byDivisorCpuOrderTesterPredictive,
_byDivisorPreviousResults,
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
@@ -543,7 +614,7 @@ private static ThreadLocal<object>? MersenneTesters;
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
static (candidate, searchedMersenne, detailedCheck, passedAllTests, divisor) => CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
@@ -560,7 +631,21 @@ private static ThreadLocal<object>? MersenneTesters;
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
static (candidate, searchedMersenne, detailedCheck, passedAllTests, divisor) => CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
else if (usePredictiveDivisors)
{
Console.WriteLine("Using by-divisor predictive increment (Hybrid order).");
MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates,
ref _byDivisorHybridOrderTesterPredictive,
_byDivisorPreviousResults,
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
@@ -573,7 +658,7 @@ private static ThreadLocal<object>? MersenneTesters;
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
static (candidate, searchedMersenne, detailedCheck, passedAllTests, divisor) => CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
@@ -590,7 +675,21 @@ private static ThreadLocal<object>? MersenneTesters;
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
static (candidate, searchedMersenne, detailedCheck, passedAllTests, divisor) => CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
else if (usePredictiveDivisors)
{
Console.WriteLine("Using by-divisor predictive increment (GPU order).");
MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates,
ref _byDivisorGpuOrderTesterPredictive,
_byDivisorPreviousResults,
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
@@ -603,7 +702,7 @@ private static ThreadLocal<object>? MersenneTesters;
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
static (candidate, searchedMersenne, detailedCheck, passedAllTests, divisor) => CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
}
@@ -1016,5 +1115,70 @@ private static ThreadLocal<object>? MersenneTesters;
detailedCheck = IsMersennePrime(gpu, p);
return detailedCheck;
}
private static void HandleByDivisorResult(ulong candidate, bool searchedMersenne, bool detailedCheck, bool passedAllTests, BigInteger divisor)
{
CalculationResultHandler.HandleResult(candidate, divisor, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
if (divisor.IsZero || _byDivisorModelPath is null || EnvironmentConfiguration.ByDivisorClassModel is null)
{
return;
}
TryRefreshByDivisorModel();
}
private static void TryRefreshByDivisorModel()
{
string? modelPath = _byDivisorModelPath;
if (modelPath is null)
{
return;
}
string resultsPath = CalculationResultHandler.ResultsFileName;
if (string.IsNullOrEmpty(resultsPath))
{
return;
}
bool shouldRefresh = false;
lock (_byDivisorModelRefreshSync)
{
_byDivisorPendingRebuilds++;
DateTime now = DateTime.UtcNow;
if (_byDivisorPendingRebuilds >= 3 || now - _byDivisorLastRebuildUtc >= _byDivisorRebuildMinInterval)
{
_byDivisorPendingRebuilds = 0;
_byDivisorLastRebuildUtc = now;
shouldRefresh = true;
}
}
if (!shouldRefresh)
{
return;
}
lock (_byDivisorModelRefreshSync)
{
try
{
double cheapLimit = EnvironmentConfiguration.ByDivisorCheapKLimit;
Console.WriteLine("Refreshing by-divisor class model from results...");
ByDivisorClassModelLoader.RebuildAndSave(modelPath, resultsPath, cheapLimit);
EnvironmentConfiguration.ByDivisorClassModel = ByDivisorClassModelLoader.LoadOrBuild(
modelPath,
resultsPath,
EnvironmentConfiguration.ByDivisorScanTuning,
cheapLimit,
rebuildWhenResultsNewer: false);
Console.WriteLine($"By-divisor class model refreshed (cheap<={cheapLimit:F0}).");
}
catch (Exception ex)
{
Console.WriteLine($"By-divisor model refresh failed: {ex.Message}");
}
}
}
}