TODO updated and small cleanups

This commit is contained in:
mlemanczyk committed 2025-10-18 22:31:36 +02:00
1 parent 4ac2ef1e6e
commit 98afb3ff9e
2 files changed
+367 -372

No files matched your search

+313 -314
View File
@@ -1,377 +1,376 @@
using System.Buffers.Text; using System.Buffers.Text;
using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading;
using PerfectNumbers.Core; using PerfectNumbers.Core;
namespace EvenPerfectBitScanner; namespace EvenPerfectBitScanner;
// TODO: Convert this struct to an equivalent of a static class. Make all fields / properties / methods static and initialize it during program startup. Don't check if it's initialized within. Rely on the flow enforcing it.
internal struct CalculationResultHandler internal struct CalculationResultHandler
{ {
private const string ResultsHeader = "p,searchedMersenne,detailedCheck,passedAllTests"; private const string ResultsHeader = "p,searchedMersenne,detailedCheck,passedAllTests";
private const string PrimeFoundSuffix = " (FOUND VALID CANDIDATES)"; private const string PrimeFoundSuffix = " (FOUND VALID CANDIDATES)";
internal const int DefaultWriteBatchSize = 100; internal const int DefaultWriteBatchSize = 100;
private static readonly UTF8Encoding Utf8Encoding = new(false, true); private static readonly UTF8Encoding Utf8Encoding = new(false, true);
private readonly object _sync; private readonly object _sync;
private readonly object _fileWriteSync; private readonly object _fileWriteSync;
private StringBuilder? _outputBuilder; private StringBuilder? _outputBuilder;
private TextFileWriter? _resultsWriter; private TextFileWriter? _resultsWriter;
private int _consoleCounter; private int _consoleCounter;
private int _writeIndex; private int _writeIndex;
private readonly int _writeBatchSize; private readonly int _writeBatchSize;
private readonly string _resultsFileName; private readonly string _resultsFileName;
private int _primeCount; private int _primeCount;
private bool _primeFoundAfterInit; private bool _primeFoundAfterInit;
private bool IsInitialized => _sync is not null; private readonly bool IsInitialized => _sync is not null;
private bool HasFileSync => _fileWriteSync is not null; private readonly bool HasFileSync => _fileWriteSync is not null;
internal CalculationResultHandler(string resultsFileName, int writeBatchSize) internal CalculationResultHandler(string resultsFileName, int writeBatchSize)
{ {
_sync = new object(); _sync = new object();
_fileWriteSync = new object(); _fileWriteSync = new object();
_outputBuilder = null; _outputBuilder = null;
_resultsWriter = null; _resultsWriter = null;
_consoleCounter = 0; _consoleCounter = 0;
_writeIndex = 0; _writeIndex = 0;
_writeBatchSize = writeBatchSize > 0 ? writeBatchSize : DefaultWriteBatchSize; _writeBatchSize = writeBatchSize > 0 ? writeBatchSize : DefaultWriteBatchSize;
_resultsFileName = resultsFileName; _resultsFileName = resultsFileName;
_primeCount = 0; _primeCount = 0;
_primeFoundAfterInit = false; _primeFoundAfterInit = false;
} }
internal string ResultsFileName => _resultsFileName; internal readonly string ResultsFileName => _resultsFileName;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InitializeOutputBuffer() internal void InitializeOutputBuffer()
{ {
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below. // Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized) // if (!IsInitialized)
// { // {
// return; // return;
// } // }
_outputBuilder = StringBuilderPool.Rent(); _outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0; _writeIndex = 0;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ReleaseOutputBuffer() internal void ReleaseOutputBuffer()
{ {
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below. // Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized) // if (!IsInitialized)
// { // {
// return; // return;
// } // }
StringBuilder builder = _outputBuilder!; StringBuilder builder = _outputBuilder!;
// Program flow guarantees the output builder is populated when releasing. Do not uncomment the guard below. // Program flow guarantees the output builder is populated when releasing. Do not uncomment the guard below.
// if (builder is null) // if (builder is null)
// { // {
// return; // return;
// } // }
_outputBuilder = null; _outputBuilder = null;
_writeIndex = 0; _writeIndex = 0;
StringBuilderPool.Return(builder); StringBuilderPool.Return(builder);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void CreateResultsFileWithHeader() internal void CreateResultsFileWithHeader()
{ {
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below. // Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync) // if (!HasFileSync)
// { // {
// return; // return;
// } // }
lock (_fileWriteSync) lock (_fileWriteSync)
{ {
_resultsWriter?.Dispose(); _resultsWriter?.Dispose();
var writer = new TextFileWriter(_resultsFileName, FileMode.Create); var writer = new TextFileWriter(_resultsFileName, FileMode.Create);
writer.WriteLine(ResultsHeader.AsSpan()); writer.WriteLine(ResultsHeader.AsSpan());
_resultsWriter = writer; _resultsWriter = writer;
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RegisterExistingResult(bool detailedCheck, bool passedAllTests) internal void RegisterExistingResult(bool detailedCheck, bool passedAllTests)
{ {
if (!detailedCheck || !passedAllTests) if (!detailedCheck || !passedAllTests)
{ {
return; return;
} }
int newCount = _primeCount + 1; int newCount = _primeCount + 1;
_primeCount = newCount; _primeCount = newCount;
if (newCount >= 2) if (newCount >= 2)
{ {
_primeFoundAfterInit = true; _primeFoundAfterInit = true;
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void HandleResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite) internal void HandleResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
{ {
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below. // Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized) // if (!IsInitialized)
// { // {
// return; // return;
// } // }
Span<byte> stackBuffer = stackalloc byte[128]; Span<byte> stackBuffer = stackalloc byte[128];
byte[]? rentedBuffer = null; byte[]? rentedBuffer = null;
Span<byte> utf8Span = stackBuffer; Span<byte> utf8Span = stackBuffer;
int byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests); int byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
if (byteCount < 0) if (byteCount < 0)
{ {
rentedBuffer = ThreadStaticPools.BytePool.Rent(256); rentedBuffer = ThreadStaticPools.BytePool.Rent(256);
utf8Span = rentedBuffer; utf8Span = rentedBuffer;
byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests); byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
if (byteCount < 0) if (byteCount < 0)
{ {
ThreadStaticPools.BytePool.Return(rentedBuffer); ThreadStaticPools.BytePool.Return(rentedBuffer);
throw new InvalidOperationException("Result buffer too small."); throw new InvalidOperationException("Result buffer too small.");
} }
} }
ReadOnlySpan<byte> utf8Line = utf8Span[..byteCount]; ReadOnlySpan<byte> utf8Line = utf8Span[..byteCount];
Span<char> charBuffer = stackalloc char[128]; Span<char> charBuffer = stackalloc char[128];
int charCount = Utf8Encoding.GetChars(utf8Line, charBuffer); int charCount = Utf8Encoding.GetChars(utf8Line, charBuffer);
ReadOnlySpan<char> recordSpan = charBuffer[..charCount]; ReadOnlySpan<char> recordSpan = charBuffer[..charCount];
bool primeCandidate = UpdatePrimeTracking(passedAllTests, lastWasComposite); bool primeCandidate = UpdatePrimeTracking(passedAllTests, lastWasComposite);
bool printToConsole = TryAcquireConsoleSlot(primeCandidate, out bool primeFlag); bool printToConsole = TryAcquireConsoleSlot(primeCandidate, out bool primeFlag);
StringBuilder? builderToFlush = AppendToOutputBuffer(recordSpan); StringBuilder? builderToFlush = AppendToOutputBuffer(recordSpan);
if (builderToFlush is not null) if (builderToFlush is not null)
{ {
FlushBuffer(builderToFlush); FlushBuffer(builderToFlush);
} }
if (printToConsole) if (printToConsole)
{ {
ReadOnlySpan<char> consoleSpan = recordSpan[..^1]; ReadOnlySpan<char> consoleSpan = recordSpan[..^1];
TextWriter consoleWriter = Console.Out; TextWriter consoleWriter = Console.Out;
if (primeFlag) if (primeFlag)
{ {
consoleWriter.Write(consoleSpan); consoleWriter.Write(consoleSpan);
consoleWriter.Write(PrimeFoundSuffix); consoleWriter.Write(PrimeFoundSuffix);
consoleWriter.WriteLine(); consoleWriter.WriteLine();
} }
else else
{ {
consoleWriter.WriteLine(consoleSpan); consoleWriter.WriteLine(consoleSpan);
} }
} }
if (rentedBuffer is not null) if (rentedBuffer is not null)
{ {
ThreadStaticPools.BytePool.Return(rentedBuffer); ThreadStaticPools.BytePool.Return(rentedBuffer);
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void FlushBuffer() internal void FlushBuffer()
{ {
// Program flow ensures the handler is initialized before this call. Do not uncomment the guard below. // Program flow ensures the handler is initialized before this call. Do not uncomment the guard below.
// if (!IsInitialized) // if (!IsInitialized)
// { // {
// return; // return;
// } // }
StringBuilder? builderToFlush; StringBuilder? builderToFlush;
lock (_sync) lock (_sync)
{ {
if (_writeIndex == 0 || _outputBuilder is null || _outputBuilder.Length == 0) if (_writeIndex == 0 || _outputBuilder is null || _outputBuilder.Length == 0)
{ {
return; return;
} }
builderToFlush = _outputBuilder; builderToFlush = _outputBuilder;
_outputBuilder = StringBuilderPool.Rent(); _outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0; _writeIndex = 0;
} }
FlushBuffer(builderToFlush!); FlushBuffer(builderToFlush!);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Dispose() internal void Dispose()
{ {
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below. // Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync) // if (!HasFileSync)
// { // {
// return; // return;
// } // }
lock (_fileWriteSync) lock (_fileWriteSync)
{ {
_resultsWriter?.Dispose(); _resultsWriter?.Dispose();
_resultsWriter = null; _resultsWriter = null;
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool TryAcquireConsoleSlot(bool primeCandidate, out bool primeFlag) private bool TryAcquireConsoleSlot(bool primeCandidate, out bool primeFlag)
{ {
int consoleInterval = PerfectNumberConstants.ConsoleInterval; int consoleInterval = PerfectNumberConstants.ConsoleInterval;
int counterValue = Interlocked.Increment(ref _consoleCounter); int counterValue = Interlocked.Increment(ref _consoleCounter);
if (counterValue >= consoleInterval) if (counterValue >= consoleInterval)
{ {
int previous = Interlocked.Exchange(ref _consoleCounter, 0); int previous = Interlocked.Exchange(ref _consoleCounter, 0);
if (previous >= consoleInterval) if (previous >= consoleInterval)
{ {
primeFlag = primeCandidate; primeFlag = primeCandidate;
return true; return true;
} }
} }
primeFlag = false; primeFlag = false;
return false; return false;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private StringBuilder? AppendToOutputBuffer(ReadOnlySpan<char> text) private StringBuilder? AppendToOutputBuffer(ReadOnlySpan<char> text)
{ {
StringBuilder? builderToFlush = null; StringBuilder? builderToFlush = null;
lock (_sync) lock (_sync)
{ {
StringBuilder outputBuilder = _outputBuilder!; StringBuilder outputBuilder = _outputBuilder!;
_ = outputBuilder.Append(text); _ = outputBuilder.Append(text);
int nextIndex = _writeIndex + 1; int nextIndex = _writeIndex + 1;
if (nextIndex >= _writeBatchSize) if (nextIndex >= _writeBatchSize)
{ {
builderToFlush = outputBuilder; builderToFlush = outputBuilder;
_outputBuilder = StringBuilderPool.Rent(); _outputBuilder = StringBuilderPool.Rent();
_writeIndex = 0; _writeIndex = 0;
} }
else else
{ {
_writeIndex = nextIndex; _writeIndex = nextIndex;
} }
} }
return builderToFlush; return builderToFlush;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int TryFormatResultLine(Span<byte> destination, ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests) private readonly int TryFormatResultLine(Span<byte> destination, ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
{ {
int offset = 0; int offset = 0;
if (!Utf8Formatter.TryFormat(currentP, destination[offset..], out int written)) if (!Utf8Formatter.TryFormat(currentP, destination[offset..], out int written))
{ {
return -1; return -1;
} }
offset += written; offset += written;
if (offset >= destination.Length) if (offset >= destination.Length)
{ {
return -1; return -1;
} }
destination[offset++] = (byte)','; destination[offset++] = (byte)',';
if (!Utf8Formatter.TryFormat(searchedMersenne, destination[offset..], out written)) if (!Utf8Formatter.TryFormat(searchedMersenne, destination[offset..], out written))
{ {
return -1; return -1;
} }
offset += written; offset += written;
if (offset >= destination.Length) if (offset >= destination.Length)
{ {
return -1; return -1;
} }
destination[offset++] = (byte)','; destination[offset++] = (byte)',';
if (!Utf8Formatter.TryFormat(detailedCheck, destination[offset..], out written)) if (!Utf8Formatter.TryFormat(detailedCheck, destination[offset..], out written))
{ {
return -1; return -1;
} }
offset += written; offset += written;
if (offset >= destination.Length) if (offset >= destination.Length)
{ {
return -1; return -1;
} }
destination[offset++] = (byte)','; destination[offset++] = (byte)',';
if (!Utf8Formatter.TryFormat(passedAllTests, destination[offset..], out written)) if (!Utf8Formatter.TryFormat(passedAllTests, destination[offset..], out written))
{ {
return -1; return -1;
} }
offset += written; offset += written;
if (offset >= destination.Length) if (offset >= destination.Length)
{ {
return -1; return -1;
} }
destination[offset++] = (byte)'\n'; destination[offset++] = (byte)'\n';
return offset; return offset;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FlushBuffer(StringBuilder builderToFlush) private void FlushBuffer(StringBuilder builderToFlush)
{ {
// Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below. // Program flow guarantees the file synchronization primitives are available here. Do not uncomment the guard below.
// if (!HasFileSync) // if (!HasFileSync)
// { // {
// builderToFlush.Clear(); // builderToFlush.Clear();
// StringBuilderPool.Return(builderToFlush); // StringBuilderPool.Return(builderToFlush);
// return; // return;
// } // }
try try
{ {
lock (_fileWriteSync) lock (_fileWriteSync)
{ {
TextFileWriter writer = EnsureResultsWriter(); TextFileWriter writer = EnsureResultsWriter();
writer.Write(builderToFlush); writer.Write(builderToFlush);
} }
} }
finally finally
{ {
builderToFlush.Clear(); builderToFlush.Clear();
StringBuilderPool.Return(builderToFlush); StringBuilderPool.Return(builderToFlush);
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private TextFileWriter EnsureResultsWriter() private TextFileWriter EnsureResultsWriter()
{ {
TextFileWriter? writer = _resultsWriter; TextFileWriter? writer = _resultsWriter;
if (writer is not null) if (writer is not null)
{ {
return writer; return writer;
} }
writer = new TextFileWriter(_resultsFileName, FileMode.Append); writer = new TextFileWriter(_resultsFileName, FileMode.Append);
_resultsWriter = writer; _resultsWriter = writer;
return writer; return writer;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool UpdatePrimeTracking(bool passedAllTests, bool lastWasComposite) private bool UpdatePrimeTracking(bool passedAllTests, bool lastWasComposite)
{ {
if (passedAllTests) if (passedAllTests)
{ {
int newCount = Interlocked.Increment(ref _primeCount); int newCount = Interlocked.Increment(ref _primeCount);
if (newCount >= 2) if (newCount >= 2)
{ {
Volatile.Write(ref _primeFoundAfterInit, true); Volatile.Write(ref _primeFoundAfterInit, true);
} }
} }
return passedAllTests && !lastWasComposite && Volatile.Read(ref _primeFoundAfterInit); return passedAllTests && !lastWasComposite && Volatile.Read(ref _primeFoundAfterInit);
} }
} }
+54 -58
View File
@@ -276,7 +276,7 @@ internal static class Program
orderWarmupLimitOverride ?? 5_000_000UL, orderWarmupLimitOverride ?? 5_000_000UL,
NttGpuMath.ReductionMode, NttGpuMath.ReductionMode,
mersenneOnGpu ? "gpu" : "cpu", mersenneOnGpu ? "gpu" : "cpu",
(GpuContextPool.ForceCpu ? "cpu" : "gpu"), GpuContextPool.ForceCpu ? "cpu" : "gpu",
orderOnGpu ? "gpu" : "cpu"); orderOnGpu ? "gpu" : "cpu");
if (!string.IsNullOrEmpty(_cliArguments.ResultsPrefix)) if (!string.IsNullOrEmpty(_cliArguments.ResultsPrefix))
@@ -418,6 +418,7 @@ internal static class Program
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads); GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
// Configure batch size for GPU primality sieve // Configure batch size for GPU primality sieve
PrimeTester.GpuBatchSize = gpuPrimeBatch; PrimeTester.GpuBatchSize = gpuPrimeBatch;
CalculationResultHandler resultHandler = _resultHandler;
Stopwatch? stopwatch = null; Stopwatch? stopwatch = null;
if (testMode) if (testMode)
@@ -436,7 +437,7 @@ internal static class Program
_byDivisorStartPrime, _byDivisorStartPrime,
static () => _lastCompositeP = true, static () => _lastCompositeP = true,
static () => _lastCompositeP = false, static () => _lastCompositeP = false,
PrintResult, static (candidate, searchedMersenne, detailedCheck, passedAllTests) => _resultHandler.HandleResult(candidate, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP),
threadCount); threadCount);
_resultHandler.FlushBuffer(); _resultHandler.FlushBuffer();
_resultHandler.ReleaseOutputBuffer(); _resultHandler.ReleaseOutputBuffer();
@@ -494,7 +495,7 @@ internal static class Program
{ {
p = buffer[j]; p = buffer[j];
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck); passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
PrintResult(p, searchedMersenne, detailedCheck, passedAllTests); resultHandler.HandleResult(p, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
} }
continue; continue;
@@ -517,7 +518,7 @@ internal static class Program
} }
passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck); passedAllTests = IsEvenPerfectCandidate(p, divisorCyclesSearchLimit, out searchedMersenne, out detailedCheck);
PrintResult(p, searchedMersenne, detailedCheck, passedAllTests); resultHandler.HandleResult(p, searchedMersenne, detailedCheck, passedAllTests, _lastCompositeP);
if (p == maxP) if (p == maxP)
{ {
@@ -715,47 +716,47 @@ internal static class Program
} }
private static void PrintHelp() // TODO: Move this to CliArguments
{ private static void PrintHelp()
Console.WriteLine("Usage: EvenPerfectBitScanner [options]"); {
Console.WriteLine(); Console.WriteLine("Usage: EvenPerfectBitScanner [options]");
Console.WriteLine("Options:"); Console.WriteLine();
Console.WriteLine(" --prime=<value> starting exponent (p)"); Console.WriteLine("Options:");
Console.WriteLine(" --max-prime=<value> inclusive upper bound for primes from filter files"); Console.WriteLine(" --prime=<value> starting exponent (p)");
Console.WriteLine(" --increment=bit|add exponent increment method"); Console.WriteLine(" --max-prime=<value> inclusive upper bound for primes from filter files");
Console.WriteLine(" --threads=<value> number of worker threads"); Console.WriteLine(" --increment=bit|add exponent increment method");
Console.WriteLine(" --block-size=<value> values processed per thread batch"); Console.WriteLine(" --threads=<value> number of worker threads");
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method"); Console.WriteLine(" --block-size=<value> values processed per thread batch");
Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)"); Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
Console.WriteLine(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)"); Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)");
Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)"); Console.WriteLine(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)");
Console.WriteLine(" --gpu-prime-batch=<n> Batch size for GPU primality sieve (default 262144)"); Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)");
Console.WriteLine(" --order-device=cpu|gpu Device for order computations (default gpu)"); Console.WriteLine(" --gpu-prime-batch=<n> Batch size for GPU primality sieve (default 262144)");
Console.WriteLine(" --ntt=reference|staged GPU NTT backend (default staged)"); Console.WriteLine(" --order-device=cpu|gpu Device for order computations (default gpu)");
Console.WriteLine(" --mod-reduction=auto|uint128|mont64|barrett128 staged NTT reduction (default auto)"); Console.WriteLine(" --ntt=reference|staged GPU NTT backend (default staged)");
Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)"); Console.WriteLine(" --mod-reduction=auto|uint128|mont64|barrett128 staged NTT reduction (default auto)");
Console.WriteLine(" --ll-slice=<value> Lucas–Lehmer iterations per slice (default 32)"); Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)");
Console.WriteLine(" --gpu-scan-batch=<value> GPU q-scan batch size (default 2_097_152)"); Console.WriteLine(" --ll-slice=<value> Lucas–Lehmer iterations per slice (default 32)");
Console.WriteLine(" --order-warmup-limit=<value> Warm-up order candidates (default 5_000_000)"); Console.WriteLine(" --gpu-scan-batch=<value> GPU q-scan batch size (default 2_097_152)");
Console.WriteLine(" --rle-blacklist=<path> enable RLE blacklist for p (hard filter up to --rle-hard-max)"); Console.WriteLine(" --order-warmup-limit=<value> Warm-up order candidates (default 5_000_000)");
Console.WriteLine(" --rle-hard-max=<p> apply RLE blacklist only for p <= this (default ulong.MaxValue = no limit)"); Console.WriteLine(" --rle-blacklist=<path> enable RLE blacklist for p (hard filter up to --rle-hard-max)");
Console.WriteLine(" --rle-only-last7=true|false apply RLE only when p % 10 == 7 (default true)"); Console.WriteLine(" --rle-hard-max=<p> apply RLE blacklist only for p <= this (default ulong.MaxValue = no limit)");
Console.WriteLine(" --zero-hard=<f> hard reject if zero_fraction(p) > f (default off)"); Console.WriteLine(" --rle-only-last7=true|false apply RLE only when p % 10 == 7 (default true)");
Console.WriteLine(" --zero-conj=<f>:<r> hard reject if zero_fraction(p) > f AND max_zero_block >= r (default off)"); Console.WriteLine(" --zero-hard=<f> hard reject if zero_fraction(p) > f (default off)");
Console.WriteLine(" --results-dir=<path> directory for results file"); Console.WriteLine(" --zero-conj=<f>:<r> hard reject if zero_fraction(p) > f AND max_zero_block >= r (default off)");
Console.WriteLine(" --results-prefix=<text> prefix to prepend to results filename"); Console.WriteLine(" --results-dir=<path> directory for results file");
Console.WriteLine(" --divisor-cycles=<path> divisor cycles data file"); Console.WriteLine(" --results-prefix=<text> prefix to prepend to results filename");
Console.WriteLine(" --divisor-cycles-device=cpu|gpu device for cycles generation (default gpu)"); Console.WriteLine(" --divisor-cycles=<path> divisor cycles data file");
Console.WriteLine(" --divisor-cycles-batch=<value> batch size for cycles generation (default 512)"); Console.WriteLine(" --divisor-cycles-device=cpu|gpu device for cycles generation (default gpu)");
Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation"); Console.WriteLine(" --divisor-cycles-batch=<value> batch size for cycles generation (default 512)");
Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor"); Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation");
Console.WriteLine(" --use-order test primality via q order"); Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor");
// mod-automaton removed Console.WriteLine(" --use-order test primality via q order");
Console.WriteLine(" --filter-p=<path> process only p from previous run results (required for --mersenne=bydivisor)"); 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");
} }
private static string BuildResultsFileName(bool bitInc, int threads, int block, GpuKernelType kernelType, bool useLucasFlag, bool useDivisorFlag, bool useByDivisorFlag, bool mersenneOnGpu, bool useOrder, bool useGcd, NttBackend nttBackend, int gpuPrimeThreads, int llSlice, int gpuScanBatch, ulong warmupLimit, ModReductionMode reduction, string mersenneDevice, string primesDevice, string orderDevice) private static string BuildResultsFileName(bool bitInc, int threads, int block, GpuKernelType kernelType, bool useLucasFlag, bool useDivisorFlag, bool useByDivisorFlag, bool mersenneOnGpu, bool useOrder, bool useGcd, NttBackend nttBackend, int gpuPrimeThreads, int llSlice, int gpuScanBatch, ulong warmupLimit, ModReductionMode reduction, string mersenneDevice, string primesDevice, string orderDevice)
{ {
@@ -895,16 +896,6 @@ internal static class Program
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void PrintResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
{
bool lastWasComposite = _lastCompositeP;
_resultHandler.HandleResult(currentP, searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
}
private static void ReportTestTime(TimeSpan elapsed) private static void ReportTestTime(TimeSpan elapsed)
{ {
double elapsedSeconds = elapsed.TotalSeconds; double elapsedSeconds = elapsed.TotalSeconds;
@@ -1242,6 +1233,7 @@ internal static class Program
return false; return false;
} }
// TODO: Convert this to extension method in ULongExtensions if one doesn't already exist. We may already calculate things like these
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ComputeBitStats(ulong value, out int bitLen, out int zeroCount, out int maxZeroBlock) private static void ComputeBitStats(ulong value, out int bitLen, out int zeroCount, out int maxZeroBlock)
{ {
@@ -1403,9 +1395,11 @@ internal static class Program
} }
} }
// TODO: Convert this to extension method in ULongExtensions if one doesn't already exist
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsCompositeByGcd(ulong p) internal static bool IsCompositeByGcd(ulong p)
{ {
// TODO: Can this ever be true on Program's execution path?
if (p < 2) if (p < 2)
{ {
return true; return true;
@@ -1415,9 +1409,11 @@ internal static class Program
return BinaryGcd(p, m) > 1UL; return BinaryGcd(p, m) > 1UL;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] // TODO: Convert this to extension method in ULongExtensions if one doesn't already exist
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong BinaryGcd(ulong a, ulong b) private static ulong BinaryGcd(ulong a, ulong b)
{ {
// TODO: Are these a & b conditions ever met on Program's execution path?
if (a == 0UL) if (a == 0UL)
{ {
return b; return b;