Hoist prime order thresholds in CPU heuristics
This commit is contained in:
1 parent
1bd3d5c163
commit
94a4ed8328
12 files changed
+670
-351
No files matched your search
@@ -202,14 +202,15 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--prime=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--prime=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
startPrime = Utf8CliParser.ParseUInt64(argument.AsSpan(argument.IndexOf('=') + 1));
|
ReadOnlySpan<char> primeValue = argument.AsSpan("--prime=".Length);
|
||||||
|
startPrime = Utf8CliParser.ParseUInt64(primeValue);
|
||||||
startPrimeProvided = true;
|
startPrimeProvided = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--max-prime=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--max-prime=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
ReadOnlySpan<char> value = argument.AsSpan(argument.IndexOf('=') + 1);
|
ReadOnlySpan<char> value = argument.AsSpan("--max-prime=".Length);
|
||||||
if (UInt128.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out UInt128 parsedMaxPrime))
|
if (UInt128.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out UInt128 parsedMaxPrime))
|
||||||
{
|
{
|
||||||
maxPrimeLimit = parsedMaxPrime;
|
maxPrimeLimit = parsedMaxPrime;
|
||||||
@@ -238,13 +239,13 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--threads=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--threads=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
threadCount = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan(argument.IndexOf('=') + 1)));
|
threadCount = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--threads=".Length)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
ReadOnlySpan<char> value = argument.AsSpan(argument.IndexOf('=') + 1);
|
ReadOnlySpan<char> value = argument.AsSpan("--mersenne=".Length);
|
||||||
if (value.Equals("pow2mod", StringComparison.OrdinalIgnoreCase))
|
if (value.Equals("pow2mod", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
kernelType = GpuKernelType.Pow2Mod;
|
kernelType = GpuKernelType.Pow2Mod;
|
||||||
@@ -281,7 +282,7 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan(argument.IndexOf('=') + 1), out ulong parsedLimit))
|
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--divisor-cycles-limit=".Length), out ulong parsedLimit))
|
||||||
{
|
{
|
||||||
divisorCyclesSearchLimit = parsedLimit;
|
divisorCyclesSearchLimit = parsedLimit;
|
||||||
}
|
}
|
||||||
@@ -291,7 +292,7 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan(argument.IndexOf('=') + 1), out ulong parsedResidueMax))
|
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--residue-max-k=".Length), out ulong parsedResidueMax))
|
||||||
{
|
{
|
||||||
residueKMax = parsedResidueMax;
|
residueKMax = parsedResidueMax;
|
||||||
}
|
}
|
||||||
@@ -301,7 +302,7 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
useMersenneOnGpu = !argument.AsSpan(argument.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
useMersenneOnGpu = !argument.AsSpan("--mersenne-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +320,7 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan(argument.IndexOf('=') + 1), out ulong parsedLimit))
|
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--order-warmup-limit=".Length), out ulong parsedLimit))
|
||||||
{
|
{
|
||||||
orderWarmupLimitOverride = parsedLimit;
|
orderWarmupLimitOverride = parsedLimit;
|
||||||
}
|
}
|
||||||
@@ -335,14 +336,14 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
ReadOnlySpan<char> value = argument.AsSpan(argument.IndexOf('=') + 1);
|
ReadOnlySpan<char> value = argument.AsSpan("--ntt=".Length);
|
||||||
nttBackend = value.Equals("staged", StringComparison.OrdinalIgnoreCase) ? NttBackend.Staged : NttBackend.Reference;
|
nttBackend = value.Equals("staged", StringComparison.OrdinalIgnoreCase) ? NttBackend.Staged : NttBackend.Reference;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
ReadOnlySpan<char> value = argument.AsSpan(argument.IndexOf('=') + 1);
|
ReadOnlySpan<char> value = argument.AsSpan("--mod-reduction=".Length);
|
||||||
if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase))
|
if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
modReductionMode = ModReductionMode.Mont64;
|
modReductionMode = ModReductionMode.Mont64;
|
||||||
@@ -365,25 +366,25 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--primes-device=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--primes-device=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
forcePrimeKernelsOnCpu = argument.AsSpan(argument.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
forcePrimeKernelsOnCpu = argument.AsSpan("--primes-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
useOrderOnGpu = !argument.AsSpan(argument.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
useOrderOnGpu = !argument.AsSpan("--order-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--rle-blacklist=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--rle-blacklist=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
rleBlacklistPath = argument[(argument.IndexOf('=') + 1)..];
|
rleBlacklistPath = argument["--rle-blacklist=".Length..];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--rle-hard-max=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--rle-hard-max=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan(argument.IndexOf('=') + 1), out ulong parsedMax))
|
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--rle-hard-max=".Length), out ulong parsedMax))
|
||||||
{
|
{
|
||||||
rleHardMaxP = parsedMax;
|
rleHardMaxP = parsedMax;
|
||||||
}
|
}
|
||||||
@@ -393,14 +394,14 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--rle-only-last7=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--rle-only-last7=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
ReadOnlySpan<char> value = argument.AsSpan(argument.IndexOf('=') + 1);
|
ReadOnlySpan<char> value = argument.AsSpan("--rle-only-last7=".Length);
|
||||||
rleOnlyLast7 = !value.Equals("false", StringComparison.OrdinalIgnoreCase) && !value.Equals("0", StringComparison.OrdinalIgnoreCase);
|
rleOnlyLast7 = !value.Equals("false", StringComparison.OrdinalIgnoreCase) && !value.Equals("0", StringComparison.OrdinalIgnoreCase);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--zero-hard=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--zero-hard=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (Utf8CliParser.TryParseDouble(argument.AsSpan(argument.IndexOf('=') + 1), out double zeroFraction))
|
if (Utf8CliParser.TryParseDouble(argument.AsSpan("--zero-hard=".Length), out double zeroFraction))
|
||||||
{
|
{
|
||||||
zeroFractionHard = zeroFraction;
|
zeroFractionHard = zeroFraction;
|
||||||
}
|
}
|
||||||
@@ -410,7 +411,7 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--zero-conj=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--zero-conj=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
ReadOnlySpan<char> value = argument.AsSpan(argument.IndexOf('=') + 1);
|
ReadOnlySpan<char> value = argument.AsSpan("--zero-conj=".Length);
|
||||||
int colon = value.IndexOf(':');
|
int colon = value.IndexOf(':');
|
||||||
if (colon > 0)
|
if (colon > 0)
|
||||||
{
|
{
|
||||||
@@ -427,87 +428,97 @@ internal readonly struct CliArguments
|
|||||||
|
|
||||||
if (argument.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
useMersenneOnGpu = !argument.AsSpan(argument.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
useMersenneOnGpu = !argument.AsSpan("--lucas=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase) ||
|
if (argument.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase))
|
||||||
argument.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
argument.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
forcePrimeKernelsOnCpu = argument.AsSpan(argument.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
forcePrimeKernelsOnCpu = argument.AsSpan("--primes=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argument.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
forcePrimeKernelsOnCpu = argument.AsSpan("--gpu-kernels=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argument.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
forcePrimeKernelsOnCpu = argument.AsSpan("--accelerator=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--results-dir=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--results-dir=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
resultsDirectory = argument[(argument.IndexOf('=') + 1)..];
|
resultsDirectory = argument["--results-dir=".Length..];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--results-prefix=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--results-prefix=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
resultsPrefix = argument[(argument.IndexOf('=') + 1)..];
|
resultsPrefix = argument["--results-prefix=".Length..];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--gpu-prime-threads=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--gpu-prime-threads=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
gpuPrimeThreads = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan(argument.IndexOf('=') + 1)));
|
gpuPrimeThreads = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--gpu-prime-threads=".Length)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--gpu-prime-batch=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--gpu-prime-batch=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
gpuPrimeBatch = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan(argument.IndexOf('=') + 1)));
|
gpuPrimeBatch = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--gpu-prime-batch=".Length)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
sliceSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan(argument.IndexOf('=') + 1)));
|
sliceSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--ll-slice=".Length)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--gpu-scan-batch=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--gpu-scan-batch=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
scanBatchSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan(argument.IndexOf('=') + 1)));
|
scanBatchSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--gpu-scan-batch=".Length)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--block-size=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--block-size=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
blockSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan(argument.IndexOf('=') + 1)));
|
blockSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--block-size=".Length)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
filterFile = argument[(argument.IndexOf('=') + 1)..];
|
filterFile = argument["--filter-p=".Length..];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--write-batch-size=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--write-batch-size=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
writeBatchSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan(argument.IndexOf('=') + 1)));
|
writeBatchSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--write-batch-size=".Length)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--divisor-cycles=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--divisor-cycles=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
cyclesPath = argument[(argument.IndexOf('=') + 1)..];
|
cyclesPath = argument["--divisor-cycles=".Length..];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--divisor-cycles-device=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--divisor-cycles-device=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
useGpuCycles = !argument.AsSpan(argument.IndexOf('=') + 1).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
useGpuCycles = !argument.AsSpan("--divisor-cycles-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.StartsWith("--divisor-cycles-batch=", StringComparison.OrdinalIgnoreCase))
|
if (argument.StartsWith("--divisor-cycles-batch=", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
cyclesBatchSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan(argument.IndexOf('=') + 1)));
|
cyclesBatchSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--divisor-cycles-batch=".Length)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ internal static class CalculationResultsFile
|
|||||||
using FileStream readStream = new(candidateFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
using FileStream readStream = new(candidateFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
using StreamReader reader = new(readStream);
|
using StreamReader reader = new(readStream);
|
||||||
string? line;
|
string? line;
|
||||||
ReadOnlySpan<char> span;
|
ReadOnlySpan<char> trimmed;
|
||||||
while ((line = reader.ReadLine()) is not null)
|
while ((line = reader.ReadLine()) is not null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(line))
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
@@ -22,36 +22,67 @@ internal static class CalculationResultsFile
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
span = line.AsSpan().Trim();
|
trimmed = line.AsSpan().Trim();
|
||||||
if (span.IsEmpty || span[0] == '#')
|
if (trimmed.IsEmpty || trimmed[0] == '#')
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int length = trimmed.Length;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (index < span.Length)
|
if (!maxPrimeConfigured)
|
||||||
{
|
{
|
||||||
if (!char.IsDigit(span[index]))
|
while (index < length)
|
||||||
{
|
{
|
||||||
index++;
|
char current = trimmed[index];
|
||||||
continue;
|
uint digit = (uint)(current - '0');
|
||||||
}
|
if (digit > 9U)
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int start = index;
|
int start = index++;
|
||||||
index++;
|
while (index < length && (uint)(trimmed[index] - '0') <= 9U)
|
||||||
while (index < span.Length && char.IsDigit(span[index]))
|
{
|
||||||
{
|
index++;
|
||||||
index++;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (Utf8CliParser.TryParseUInt64(span[start..index], out ulong parsed))
|
if (Utf8CliParser.TryParseUInt64(trimmed[start..index], out ulong parsed))
|
||||||
{
|
|
||||||
if (!maxPrimeConfigured || (UInt128)parsed <= maxPrimeLimit)
|
|
||||||
{
|
{
|
||||||
candidates.Add(parsed);
|
candidates.Add(parsed);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UInt128 limit = maxPrimeLimit;
|
||||||
|
while (index < length)
|
||||||
|
{
|
||||||
|
char current = trimmed[index];
|
||||||
|
uint digit = (uint)(current - '0');
|
||||||
|
if (digit > 9U)
|
||||||
{
|
{
|
||||||
skippedByLimit++;
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = index++;
|
||||||
|
while (index < length && (uint)(trimmed[index] - '0') <= 9U)
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utf8CliParser.TryParseUInt64(trimmed[start..index], out ulong parsed))
|
||||||
|
{
|
||||||
|
if ((UInt128)parsed <= limit)
|
||||||
|
{
|
||||||
|
candidates.Add(parsed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
skippedByLimit++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ namespace PerfectNumbers.Core.Cpu;
|
|||||||
|
|
||||||
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
|
public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester
|
||||||
{
|
{
|
||||||
private readonly object _sync = new();
|
|
||||||
|
|
||||||
private ulong _divisorLimit;
|
private ulong _divisorLimit;
|
||||||
private ulong _lastStatusDivisor;
|
|
||||||
private int _batchSize = 1_024;
|
private int _batchSize = 1_024;
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
@@ -32,7 +29,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
public void ConfigureFromMaxPrime(ulong maxPrime)
|
public void ConfigureFromMaxPrime(ulong maxPrime)
|
||||||
{
|
{
|
||||||
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
|
_divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime);
|
||||||
_lastStatusDivisor = 0UL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong DivisorLimit
|
public ulong DivisorLimit
|
||||||
@@ -60,22 +56,12 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
// return true;
|
// return true;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ulong processedCount;
|
|
||||||
bool processedAll;
|
bool processedAll;
|
||||||
|
|
||||||
bool composite = CheckDivisors(
|
bool composite = CheckDivisors(
|
||||||
prime,
|
prime,
|
||||||
allowedMax,
|
allowedMax,
|
||||||
out processedAll,
|
out processedAll);
|
||||||
out processedCount);
|
|
||||||
|
|
||||||
if (processedCount > 0UL)
|
|
||||||
{
|
|
||||||
lock (_sync)
|
|
||||||
{
|
|
||||||
UpdateStatusUnsafe(processedCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (composite)
|
if (composite)
|
||||||
{
|
{
|
||||||
@@ -111,10 +97,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
private bool CheckDivisors(
|
private bool CheckDivisors(
|
||||||
ulong prime,
|
ulong prime,
|
||||||
ulong allowedMax,
|
ulong allowedMax,
|
||||||
out bool processedAll,
|
out bool processedAll)
|
||||||
out ulong processedCount)
|
|
||||||
{
|
{
|
||||||
processedCount = 0UL;
|
|
||||||
processedAll = false;
|
processedAll = false;
|
||||||
|
|
||||||
// The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs.
|
// The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs.
|
||||||
@@ -151,44 +135,86 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
ulong stepHigh = step.High;
|
ulong stepHigh = step.High;
|
||||||
ulong stepLow = step.Low;
|
ulong stepLow = step.Low;
|
||||||
byte step10 = (byte)((((stepHigh % 10UL) * 6UL) + (stepLow % 10UL)) % 10UL);
|
ulong limitHigh = limit.High;
|
||||||
byte step8 = (byte)(stepLow % 8UL);
|
|
||||||
byte step5 = (byte)(((stepHigh % 5UL) + (stepLow % 5UL)) % 5UL);
|
|
||||||
byte step3 = (byte)(((stepHigh % 3UL) + (stepLow % 3UL)) % 3UL);
|
|
||||||
byte step7 = (byte)((((stepHigh % 7UL) * 2UL) + (stepLow % 7UL)) % 7UL);
|
|
||||||
byte step11 = (byte)((((stepHigh % 11UL) * 5UL) + (stepLow % 11UL)) % 11UL);
|
|
||||||
|
|
||||||
ulong divisorHigh = divisor.High;
|
|
||||||
ulong divisorLow = divisor.Low;
|
ulong divisorLow = divisor.Low;
|
||||||
byte remainder10 = (byte)((((divisorHigh % 10UL) * 6UL) + (divisorLow % 10UL)) % 10UL);
|
|
||||||
byte remainder8 = (byte)(divisorLow % 8UL);
|
|
||||||
byte remainder5 = (byte)(((divisorHigh % 5UL) + (divisorLow % 5UL)) % 5UL);
|
|
||||||
byte remainder3 = (byte)(((divisorHigh % 3UL) + (divisorLow % 3UL)) % 3UL);
|
|
||||||
byte remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
|
|
||||||
byte remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
|
|
||||||
|
|
||||||
// Keep the divisibility filters aligned with the divisor-cycle generator so the
|
// Keep the divisibility filters aligned with the divisor-cycle generator so the
|
||||||
// CPU path never requests cycles that were skipped during cache creation.
|
// CPU path never requests cycles that were skipped during cache creation.
|
||||||
LastDigit lastDigit = (prime & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
LastDigit lastDigit = (prime & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||||
bool lastIsSeven = lastDigit == LastDigit.Seven;
|
ushort decimalMask = DivisorGenerator.GetDecimalMask(lastDigit);
|
||||||
const ushort DecimalMaskWhenLastIsSeven = (1 << 3) | (1 << 7) | (1 << 9);
|
|
||||||
const ushort DecimalMaskOtherwise = (1 << 1) | (1 << 3) | (1 << 9);
|
byte step10;
|
||||||
|
byte step8;
|
||||||
|
byte step5;
|
||||||
|
byte step3;
|
||||||
|
byte step7;
|
||||||
|
byte step11;
|
||||||
|
|
||||||
|
byte remainder10;
|
||||||
|
byte remainder8;
|
||||||
|
byte remainder5;
|
||||||
|
byte remainder3;
|
||||||
|
byte remainder7;
|
||||||
|
byte remainder11;
|
||||||
|
|
||||||
|
if (stepHigh == 0UL && limitHigh == 0UL)
|
||||||
|
{
|
||||||
|
step10 = (byte)(stepLow % 10UL);
|
||||||
|
step8 = (byte)(stepLow & 7UL);
|
||||||
|
step5 = (byte)(stepLow % 5UL);
|
||||||
|
step3 = (byte)(stepLow % 3UL);
|
||||||
|
step7 = (byte)(stepLow % 7UL);
|
||||||
|
step11 = (byte)(stepLow % 11UL);
|
||||||
|
|
||||||
|
remainder10 = (byte)(divisorLow % 10UL);
|
||||||
|
remainder8 = (byte)(divisorLow & 7UL);
|
||||||
|
remainder5 = (byte)(divisorLow % 5UL);
|
||||||
|
remainder3 = (byte)(divisorLow % 3UL);
|
||||||
|
remainder7 = (byte)(divisorLow % 7UL);
|
||||||
|
remainder11 = (byte)(divisorLow % 11UL);
|
||||||
|
|
||||||
|
return CheckDivisors64(
|
||||||
|
prime,
|
||||||
|
stepLow,
|
||||||
|
limit.Low,
|
||||||
|
divisorLow,
|
||||||
|
decimalMask,
|
||||||
|
step10,
|
||||||
|
step8,
|
||||||
|
step5,
|
||||||
|
step3,
|
||||||
|
step7,
|
||||||
|
step11,
|
||||||
|
remainder10,
|
||||||
|
remainder8,
|
||||||
|
remainder5,
|
||||||
|
remainder3,
|
||||||
|
remainder7,
|
||||||
|
remainder11,
|
||||||
|
out processedAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong divisorHigh = divisor.High;
|
||||||
|
step10 = (byte)((((stepHigh % 10UL) * 6UL) + (stepLow % 10UL)) % 10UL);
|
||||||
|
step8 = (byte)(stepLow % 8UL);
|
||||||
|
step5 = (byte)(((stepHigh % 5UL) + (stepLow % 5UL)) % 5UL);
|
||||||
|
step3 = (byte)(((stepHigh % 3UL) + (stepLow % 3UL)) % 3UL);
|
||||||
|
step7 = (byte)((((stepHigh % 7UL) * 2UL) + (stepLow % 7UL)) % 7UL);
|
||||||
|
step11 = (byte)((((stepHigh % 11UL) * 5UL) + (stepLow % 11UL)) % 11UL);
|
||||||
|
|
||||||
|
remainder10 = (byte)((((divisorHigh % 10UL) * 6UL) + (divisorLow % 10UL)) % 10UL);
|
||||||
|
remainder8 = (byte)(divisorLow % 8UL);
|
||||||
|
remainder5 = (byte)(((divisorHigh % 5UL) + (divisorLow % 5UL)) % 5UL);
|
||||||
|
remainder3 = (byte)(((divisorHigh % 3UL) + (divisorLow % 3UL)) % 3UL);
|
||||||
|
remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL);
|
||||||
|
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
|
||||||
|
|
||||||
while (divisor.CompareTo(limit) <= 0)
|
while (divisor.CompareTo(limit) <= 0)
|
||||||
{
|
{
|
||||||
ulong candidate = divisor.Low;
|
bool passesSmallModuli = remainder3 != 0 && remainder5 != 0 && remainder7 != 0 && remainder11 != 0;
|
||||||
processedCount++;
|
if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
|
||||||
|
|
||||||
ushort decimalMask = lastIsSeven ? DecimalMaskWhenLastIsSeven : DecimalMaskOtherwise;
|
|
||||||
bool admissible = ((decimalMask >> remainder10) & 1) != 0;
|
|
||||||
|
|
||||||
if (admissible
|
|
||||||
&& (remainder8 == 1 || remainder8 == 7)
|
|
||||||
&& remainder3 != 0
|
|
||||||
&& remainder5 != 0
|
|
||||||
&& remainder7 != 0
|
|
||||||
&& remainder11 != 0)
|
|
||||||
{
|
{
|
||||||
|
ulong candidate = divisor.Low;
|
||||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(candidate);
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(candidate);
|
||||||
ulong divisorCycle;
|
ulong divisorCycle;
|
||||||
// Divisors generated from 2 * k * p + 1 exceed the small-cycle snapshot when p >= 138,000,000, so the short path below never runs.
|
// Divisors generated from 2 * k * p + 1 exceed the small-cycle snapshot when p >= 138,000,000, so the short path below never runs.
|
||||||
@@ -233,16 +259,141 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
divisor.Add(step);
|
divisor.Add(step);
|
||||||
remainder10 = AddMod(remainder10, step10, (byte)10);
|
remainder10 = AddMod10(remainder10, step10);
|
||||||
remainder8 = AddMod(remainder8, step8, (byte)8);
|
remainder8 = AddMod8(remainder8, step8);
|
||||||
remainder5 = AddMod(remainder5, step5, (byte)5);
|
remainder5 = AddMod5(remainder5, step5);
|
||||||
remainder3 = AddMod(remainder3, step3, (byte)3);
|
remainder3 = AddMod3(remainder3, step3);
|
||||||
remainder7 = AddMod(remainder7, step7, (byte)7);
|
remainder7 = AddMod7(remainder7, step7);
|
||||||
remainder11 = AddMod(remainder11, step11, (byte)11);
|
remainder11 = AddMod11(remainder11, step11);
|
||||||
|
}
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CheckDivisors64(
|
||||||
|
ulong prime,
|
||||||
|
ulong step,
|
||||||
|
ulong limit,
|
||||||
|
ulong divisor,
|
||||||
|
ushort decimalMask,
|
||||||
|
byte step10,
|
||||||
|
byte step8,
|
||||||
|
byte step5,
|
||||||
|
byte step3,
|
||||||
|
byte step7,
|
||||||
|
byte step11,
|
||||||
|
byte remainder10,
|
||||||
|
byte remainder8,
|
||||||
|
byte remainder5,
|
||||||
|
byte remainder3,
|
||||||
|
byte remainder7,
|
||||||
|
byte remainder11,
|
||||||
|
out bool processedAll)
|
||||||
|
{
|
||||||
|
processedAll = false;
|
||||||
|
|
||||||
|
bool canAdvance = step <= limit;
|
||||||
|
if (!canAdvance)
|
||||||
|
{
|
||||||
|
if (divisor <= limit)
|
||||||
|
{
|
||||||
|
if (remainder3 != 0 && remainder5 != 0 && remainder7 != 0 && remainder11 != 0 && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
|
||||||
|
{
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||||
|
ulong divisorCycle;
|
||||||
|
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
||||||
|
divisor,
|
||||||
|
prime,
|
||||||
|
divisorData,
|
||||||
|
out ulong computedCycle,
|
||||||
|
out bool primeOrderFailed) || computedCycle == 0UL)
|
||||||
|
{
|
||||||
|
divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
|
||||||
|
divisor,
|
||||||
|
divisorData,
|
||||||
|
skipPrimeOrderHeuristic: primeOrderFailed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
divisorCycle = computedCycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (divisorCycle == prime)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (divisorCycle == 0UL)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Divisor cycle was not calculated for {prime}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
processedAll = divisor.CompareTo(limit) > 0;
|
if (divisor > limit)
|
||||||
return false;
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong remainingIterations = ((limit - divisor) / step) + 1UL;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
bool passesSmallModuli = remainder3 != 0 && remainder5 != 0 && remainder7 != 0 && remainder11 != 0;
|
||||||
|
if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
|
||||||
|
{
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||||
|
ulong divisorCycle;
|
||||||
|
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
|
||||||
|
divisor,
|
||||||
|
prime,
|
||||||
|
divisorData,
|
||||||
|
out ulong computedCycle,
|
||||||
|
out bool primeOrderFailed) || computedCycle == 0UL)
|
||||||
|
{
|
||||||
|
divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
|
||||||
|
divisor,
|
||||||
|
divisorData,
|
||||||
|
skipPrimeOrderHeuristic: primeOrderFailed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
divisorCycle = computedCycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (divisorCycle == prime)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (divisorCycle == 0UL)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Divisor cycle was not calculated for {prime}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingIterations--;
|
||||||
|
if (remainingIterations == 0UL)
|
||||||
|
{
|
||||||
|
processedAll = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
divisor += step;
|
||||||
|
remainder10 = AddMod10(remainder10, step10);
|
||||||
|
remainder8 = AddMod8(remainder8, step8);
|
||||||
|
remainder5 = AddMod5(remainder5, step5);
|
||||||
|
remainder3 = AddMod3(remainder3, step3);
|
||||||
|
remainder7 = AddMod7(remainder7, step7);
|
||||||
|
remainder11 = AddMod11(remainder11, step11);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte CheckDivisor(ulong prime, ulong divisorCycle, in MontgomeryDivisorData divisorData)
|
private static byte CheckDivisor(ulong prime, ulong divisorCycle, in MontgomeryDivisorData divisorData)
|
||||||
@@ -251,35 +402,80 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
return residue == 1UL ? (byte)1 : (byte)0;
|
return residue == 1UL ? (byte)1 : (byte)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte AddMod(byte value, byte delta, byte modulus)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod3(byte value, byte delta)
|
||||||
{
|
{
|
||||||
|
const int Modulus = 3;
|
||||||
int sum = value + delta;
|
int sum = value + delta;
|
||||||
if (sum >= modulus)
|
|
||||||
|
if (sum >= Modulus)
|
||||||
{
|
{
|
||||||
sum -= modulus;
|
sum -= Modulus;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte)sum;
|
return (byte)sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateStatusUnsafe(ulong processedCount)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod5(byte value, byte delta)
|
||||||
{
|
{
|
||||||
if (processedCount == 0UL)
|
const int Modulus = 5;
|
||||||
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
{
|
{
|
||||||
return;
|
sum -= Modulus;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong interval = PerfectNumberConstants.ConsoleInterval;
|
return (byte)sum;
|
||||||
if (interval == 0UL)
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod7(byte value, byte delta)
|
||||||
|
{
|
||||||
|
const int Modulus = 7;
|
||||||
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
{
|
{
|
||||||
_lastStatusDivisor = 0UL;
|
sum -= Modulus;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong total = _lastStatusDivisor + processedCount;
|
return (byte)sum;
|
||||||
// TODO: Replace this modulo with the ring-buffer style counter (subtract loop) used in the fast CLI
|
}
|
||||||
// status benchmarks so we avoid `%` in this hot loop while still wrapping progress correctly.
|
|
||||||
_lastStatusDivisor = total % interval;
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod10(byte value, byte delta)
|
||||||
|
{
|
||||||
|
const int Modulus = 10;
|
||||||
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
|
{
|
||||||
|
sum -= Modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod11(byte value, byte delta)
|
||||||
|
{
|
||||||
|
const int Modulus = 11;
|
||||||
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
|
{
|
||||||
|
sum -= Modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod8(byte value, byte delta)
|
||||||
|
{
|
||||||
|
return (byte)((value + delta) & 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime)
|
private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime)
|
||||||
|
|||||||
@@ -8,31 +8,9 @@ internal static class DivisorGenerator
|
|||||||
private const ushort DecimalMaskOtherwise = (1 << 1) | (1 << 3) | (1 << 9);
|
private const ushort DecimalMaskOtherwise = (1 << 1) | (1 << 3) | (1 << 9);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsValidDivisor(
|
public static ushort GetDecimalMask(LastDigit lastDigit)
|
||||||
byte remainder10,
|
|
||||||
byte remainder8,
|
|
||||||
byte remainder3,
|
|
||||||
byte remainder5,
|
|
||||||
byte remainder7,
|
|
||||||
byte remainder11,
|
|
||||||
LastDigit lastDigit)
|
|
||||||
{
|
{
|
||||||
ushort decimalMask = lastDigit == LastDigit.Seven ? DecimalMaskWhenLastIsSeven : DecimalMaskOtherwise;
|
return lastDigit == LastDigit.Seven ? DecimalMaskWhenLastIsSeven : DecimalMaskOtherwise;
|
||||||
if (((decimalMask >> remainder10) & 1) == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainder8 != 1 && remainder8 != 7)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainder3 == 0 || remainder5 == 0 || remainder7 == 0 || remainder11 == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -36,30 +36,45 @@ internal static class DivisorByDivisorKernels
|
|||||||
byte firstHitValue;
|
byte firstHitValue;
|
||||||
ulong previousExponent = firstExponent;
|
ulong previousExponent = firstExponent;
|
||||||
ulong cycleRemainder = 0UL;
|
ulong cycleRemainder = 0UL;
|
||||||
if (cycleLength == 0UL)
|
bool hasCycle = cycleLength != 0UL;
|
||||||
{
|
if (hasCycle)
|
||||||
firstHitValue = firstUnity ? (byte)1 : (byte)0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
cycleRemainder = firstExponent % cycleLength;
|
cycleRemainder = firstExponent % cycleLength;
|
||||||
firstHitValue = cycleRemainder == 0UL && firstUnity ? (byte)1 : (byte)0;
|
firstHitValue = cycleRemainder == 0UL && firstUnity ? (byte)1 : (byte)0;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firstHitValue = firstUnity ? (byte)1 : (byte)0;
|
||||||
|
}
|
||||||
|
|
||||||
hits[offset] = firstHitValue;
|
hits[offset] = firstHitValue;
|
||||||
byte anyHit = firstHitValue;
|
byte anyHit = firstHitValue;
|
||||||
|
|
||||||
for (int i = 1; i < count; i++)
|
int endIndex = offset + count;
|
||||||
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
ulong exponent = exponents[offset + i];
|
for (int exponentIndex = offset + 1; exponentIndex < endIndex; exponentIndex++)
|
||||||
ulong delta = exponent - previousExponent;
|
|
||||||
previousExponent = exponent;
|
|
||||||
|
|
||||||
if (cycleLength != 0UL)
|
|
||||||
{
|
{
|
||||||
var remainderValue = new GpuUInt128(cycleRemainder);
|
ulong exponent = exponents[exponentIndex];
|
||||||
remainderValue.AddMod(delta, cycleLength);
|
ulong delta = exponent - previousExponent;
|
||||||
cycleRemainder = remainderValue.Low;
|
previousExponent = exponent;
|
||||||
|
|
||||||
|
if (delta >= cycleLength)
|
||||||
|
{
|
||||||
|
delta -= cycleLength;
|
||||||
|
if (delta >= cycleLength)
|
||||||
|
{
|
||||||
|
delta %= cycleLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong nextRemainder = cycleRemainder + delta;
|
||||||
|
if (nextRemainder >= cycleLength)
|
||||||
|
{
|
||||||
|
nextRemainder -= cycleLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
cycleRemainder = nextRemainder;
|
||||||
|
|
||||||
byte hit = 0;
|
byte hit = 0;
|
||||||
if (cycleRemainder == 0UL)
|
if (cycleRemainder == 0UL)
|
||||||
@@ -67,13 +82,17 @@ internal static class DivisorByDivisorKernels
|
|||||||
hit = stepper.ComputeNextIsUnityGpu(exponent) ? (byte)1 : (byte)0;
|
hit = stepper.ComputeNextIsUnityGpu(exponent) ? (byte)1 : (byte)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
hits[offset + i] = hit;
|
hits[exponentIndex] = hit;
|
||||||
anyHit |= hit;
|
anyHit |= hit;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int exponentIndex = offset + 1; exponentIndex < endIndex; exponentIndex++)
|
||||||
{
|
{
|
||||||
|
ulong exponent = exponents[exponentIndex];
|
||||||
byte hit = stepper.ComputeNextIsUnityGpu(exponent) ? (byte)1 : (byte)0;
|
byte hit = stepper.ComputeNextIsUnityGpu(exponent) ? (byte)1 : (byte)0;
|
||||||
hits[offset + i] = hit;
|
hits[exponentIndex] = hit;
|
||||||
anyHit |= hit;
|
anyHit |= hit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
resources.HitIndexBuffer,
|
resources.HitIndexBuffer,
|
||||||
resources.Divisors,
|
resources.Divisors,
|
||||||
resources.Exponents,
|
resources.Exponents,
|
||||||
resources.FilteredDivisors,
|
|
||||||
resources.DivisorData,
|
resources.DivisorData,
|
||||||
resources.Offsets,
|
resources.Offsets,
|
||||||
resources.Counts,
|
resources.Counts,
|
||||||
@@ -177,7 +176,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
MemoryBuffer1D<int, Stride1D.Dense> hitIndexBuffer,
|
MemoryBuffer1D<int, Stride1D.Dense> hitIndexBuffer,
|
||||||
ulong[] divisors,
|
ulong[] divisors,
|
||||||
ulong[] exponents,
|
ulong[] exponents,
|
||||||
ulong[] filteredDivisors,
|
|
||||||
GpuDivisorPartialData[] divisorData,
|
GpuDivisorPartialData[] divisorData,
|
||||||
int[] offsets,
|
int[] offsets,
|
||||||
int[] counts,
|
int[] counts,
|
||||||
@@ -192,7 +190,15 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
processedCount = 0UL;
|
processedCount = 0UL;
|
||||||
lastProcessed = 0UL;
|
lastProcessed = 0UL;
|
||||||
|
|
||||||
int chunkCapacity = Math.Max(1, batchCapacity);
|
// EvenPerfectBitScanner always configures non-empty GPU divisor buffers, so the defensive guard stays
|
||||||
|
// commented out. Tests and benchmarks that rely on zero-capacity buffers must be updated instead of re-enabling
|
||||||
|
// this branch.
|
||||||
|
// if (batchCapacity <= 0)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException("Divisor buffers must be non-empty.");
|
||||||
|
// }
|
||||||
|
|
||||||
|
int chunkCountBaseline = batchCapacity;
|
||||||
|
|
||||||
UInt128 twoP128 = (UInt128)prime << 1;
|
UInt128 twoP128 = (UInt128)prime << 1;
|
||||||
UInt128 allowedMax128 = allowedMax;
|
UInt128 allowedMax128 = allowedMax;
|
||||||
@@ -209,48 +215,63 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong maxK = maxK128 > ulong.MaxValue ? ulong.MaxValue : (ulong)maxK128;
|
ulong maxK = maxK128 > ulong.MaxValue ? ulong.MaxValue : (ulong)maxK128;
|
||||||
ulong currentK = 1UL;
|
ulong remainingCount = maxK;
|
||||||
|
|
||||||
byte step10 = (byte)(twoP128 % 10UL);
|
ulong stepValue = (ulong)twoP128;
|
||||||
byte step8 = (byte)(twoP128 % 8UL);
|
byte step10 = (byte)(stepValue % 10UL);
|
||||||
byte step5 = (byte)(twoP128 % 5UL);
|
byte step8 = (byte)(stepValue & 7UL);
|
||||||
byte step3 = (byte)(twoP128 % 3UL);
|
byte step5 = (byte)(stepValue % 5UL);
|
||||||
byte step7 = (byte)(twoP128 % 7UL);
|
byte step3 = (byte)(stepValue % 3UL);
|
||||||
byte step11 = (byte)(twoP128 % 11UL);
|
byte step7 = (byte)(stepValue % 7UL);
|
||||||
|
byte step11 = (byte)(stepValue % 11UL);
|
||||||
|
|
||||||
UInt128 currentDivisor128 = firstDivisor128;
|
UInt128 currentDivisor128 = firstDivisor128;
|
||||||
byte remainder10 = (byte)((ulong)(currentDivisor128 % 10UL));
|
ulong currentDivisorValue = (ulong)currentDivisor128;
|
||||||
byte remainder8 = (byte)((ulong)(currentDivisor128 & 7UL));
|
byte remainder10 = (byte)(currentDivisorValue % 10UL);
|
||||||
byte remainder5 = (byte)((ulong)(currentDivisor128 % 5UL));
|
byte remainder8 = (byte)(currentDivisorValue & 7UL);
|
||||||
byte remainder3 = (byte)((ulong)(currentDivisor128 % 3UL));
|
byte remainder5 = (byte)(currentDivisorValue % 5UL);
|
||||||
byte remainder7 = (byte)((ulong)(currentDivisor128 % 7UL));
|
byte remainder3 = (byte)(currentDivisorValue % 3UL);
|
||||||
byte remainder11 = (byte)((ulong)(currentDivisor128 % 11UL));
|
byte remainder7 = (byte)(currentDivisorValue % 7UL);
|
||||||
|
byte remainder11 = (byte)(currentDivisorValue % 11UL);
|
||||||
LastDigit lastDigit = (prime & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
LastDigit lastDigit = (prime & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||||
|
ushort decimalMask = DivisorGenerator.GetDecimalMask(lastDigit);
|
||||||
|
|
||||||
Span<ulong> filteredStorage = filteredDivisors.AsSpan();
|
|
||||||
Span<ulong> divisorStorage = divisors.AsSpan();
|
Span<ulong> divisorStorage = divisors.AsSpan();
|
||||||
Span<ulong> exponentStorage = exponents.AsSpan();
|
Span<ulong> exponentStorage = exponents.AsSpan();
|
||||||
Span<GpuDivisorPartialData> divisorDataStorage = divisorData.AsSpan();
|
Span<GpuDivisorPartialData> divisorDataStorage = divisorData.AsSpan();
|
||||||
Span<int> offsetStorage = offsets.AsSpan();
|
Span<int> offsetStorage = offsets.AsSpan();
|
||||||
Span<int> countStorage = counts.AsSpan();
|
Span<int> countStorage = counts.AsSpan();
|
||||||
Span<ulong> cycleStorage = cycles.AsSpan();
|
Span<ulong> cycleStorage = cycles.AsSpan();
|
||||||
while (currentK <= maxK && !composite)
|
Span<ulong> divisorSpan = divisorStorage;
|
||||||
|
Span<ulong> exponentSpan = exponentStorage;
|
||||||
|
Span<GpuDivisorPartialData> divisorDataSpan = divisorDataStorage;
|
||||||
|
Span<int> offsetSpan = offsetStorage;
|
||||||
|
Span<int> countSpan = countStorage;
|
||||||
|
Span<ulong> cycleSpan = cycleStorage;
|
||||||
|
ref GpuDivisorPartialData divisorDataRef = ref MemoryMarshal.GetReference(divisorDataSpan);
|
||||||
|
ref int offsetRef = ref MemoryMarshal.GetReference(offsetSpan);
|
||||||
|
ref int countRef = ref MemoryMarshal.GetReference(countSpan);
|
||||||
|
ref ulong cycleRef = ref MemoryMarshal.GetReference(cycleSpan);
|
||||||
|
ref ulong exponentRef = ref MemoryMarshal.GetReference(exponentSpan);
|
||||||
|
|
||||||
|
ArrayView1D<GpuDivisorPartialData, Stride1D.Dense> divisorDataView = divisorDataBuffer.View;
|
||||||
|
ArrayView1D<int, Stride1D.Dense> offsetView = offsetBuffer.View;
|
||||||
|
ArrayView1D<int, Stride1D.Dense> countView = countBuffer.View;
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> cycleView = cycleBuffer.View;
|
||||||
|
ArrayView1D<ulong, Stride1D.Dense> exponentViewDevice = exponentBuffer.View;
|
||||||
|
ArrayView1D<byte, Stride1D.Dense> hitsView = hitsBuffer.View;
|
||||||
|
ArrayView1D<int, Stride1D.Dense> hitIndexView = hitIndexBuffer.View;
|
||||||
|
|
||||||
|
while (remainingCount > 0UL && !composite)
|
||||||
{
|
{
|
||||||
int chunkCount = Math.Min(chunkCapacity, batchCapacity);
|
int chunkCount = chunkCountBaseline;
|
||||||
ulong remainingK = maxK - currentK + 1UL;
|
if ((ulong)chunkCount > remainingCount)
|
||||||
if ((ulong)chunkCount > remainingK)
|
|
||||||
{
|
{
|
||||||
chunkCount = (int)remainingK;
|
chunkCount = (int)remainingCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunkCount <= 0)
|
|
||||||
{
|
|
||||||
processedAll = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int filteredCount = 0;
|
|
||||||
UInt128 nextDivisor128 = currentDivisor128;
|
UInt128 nextDivisor128 = currentDivisor128;
|
||||||
|
int admissibleCount = 0;
|
||||||
|
|
||||||
byte localRemainder10 = remainder10;
|
byte localRemainder10 = remainder10;
|
||||||
byte localRemainder8 = remainder8;
|
byte localRemainder8 = remainder8;
|
||||||
@@ -261,20 +282,32 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
for (int i = 0; i < chunkCount; i++)
|
for (int i = 0; i < chunkCount; i++)
|
||||||
{
|
{
|
||||||
ulong candidate = (ulong)nextDivisor128;
|
bool passesSmallModuli = localRemainder3 != 0 && localRemainder5 != 0 && localRemainder7 != 0 && localRemainder11 != 0;
|
||||||
|
if (passesSmallModuli && (localRemainder8 == 1 || localRemainder8 == 7) && ((decimalMask >> localRemainder10) & 1) != 0)
|
||||||
if (DivisorGenerator.IsValidDivisor(localRemainder10, localRemainder8, localRemainder3, localRemainder5, localRemainder7, localRemainder11, lastDigit))
|
|
||||||
{
|
{
|
||||||
filteredStorage[filteredCount++] = candidate;
|
ulong divisorValue = (ulong)nextDivisor128;
|
||||||
|
MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue);
|
||||||
|
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
|
||||||
|
if (divisorCycle == prime)
|
||||||
|
{
|
||||||
|
int targetIndex = admissibleCount;
|
||||||
|
divisorSpan[targetIndex] = divisorValue;
|
||||||
|
exponentSpan[targetIndex] = prime;
|
||||||
|
divisorDataSpan[targetIndex] = new GpuDivisorPartialData(divisorValue);
|
||||||
|
offsetSpan[targetIndex] = targetIndex;
|
||||||
|
countSpan[targetIndex] = 1;
|
||||||
|
cycleSpan[targetIndex] = divisorCycle;
|
||||||
|
admissibleCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nextDivisor128 += twoP128;
|
nextDivisor128 += twoP128;
|
||||||
localRemainder10 = AddMod(localRemainder10, step10, 10);
|
localRemainder10 = AddMod10(localRemainder10, step10);
|
||||||
localRemainder8 = AddMod(localRemainder8, step8, 8);
|
localRemainder8 = AddMod8(localRemainder8, step8);
|
||||||
localRemainder5 = AddMod(localRemainder5, step5, 5);
|
localRemainder5 = AddMod5(localRemainder5, step5);
|
||||||
localRemainder3 = AddMod(localRemainder3, step3, 3);
|
localRemainder3 = AddMod3(localRemainder3, step3);
|
||||||
localRemainder7 = AddMod(localRemainder7, step7, 7);
|
localRemainder7 = AddMod7(localRemainder7, step7);
|
||||||
localRemainder11 = AddMod(localRemainder11, step11, 11);
|
localRemainder11 = AddMod11(localRemainder11, step11);
|
||||||
}
|
}
|
||||||
|
|
||||||
remainder10 = localRemainder10;
|
remainder10 = localRemainder10;
|
||||||
@@ -289,69 +322,23 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
UInt128 lastDivisor128 = nextDivisor128 - twoP128;
|
UInt128 lastDivisor128 = nextDivisor128 - twoP128;
|
||||||
lastProcessed = (ulong)lastDivisor128;
|
lastProcessed = (ulong)lastDivisor128;
|
||||||
currentDivisor128 = nextDivisor128;
|
currentDivisor128 = nextDivisor128;
|
||||||
|
remainingCount -= (ulong)chunkCount;
|
||||||
currentK += (ulong)chunkCount;
|
|
||||||
processedAll = currentK > maxK;
|
|
||||||
|
|
||||||
if (filteredCount == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<ulong> filteredDivisorsSpan = filteredStorage[..filteredCount];
|
|
||||||
|
|
||||||
Span<ulong> divisorSpan = divisorStorage;
|
|
||||||
Span<ulong> exponentSpan = exponentStorage;
|
|
||||||
Span<GpuDivisorPartialData> divisorDataSpan = divisorDataStorage;
|
|
||||||
Span<int> offsetSpan = offsetStorage;
|
|
||||||
Span<int> countSpan = countStorage;
|
|
||||||
Span<ulong> cycleSpan = cycleStorage;
|
|
||||||
int admissibleCount = 0;
|
|
||||||
int exponentIndex = 0;
|
|
||||||
for (int i = 0; i < filteredCount; i++)
|
|
||||||
{
|
|
||||||
ulong divisorValue = filteredDivisorsSpan[i];
|
|
||||||
MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue);
|
|
||||||
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
|
|
||||||
if (divisorCycle != prime)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
divisorSpan[admissibleCount] = divisorValue;
|
|
||||||
exponentSpan[exponentIndex] = prime;
|
|
||||||
divisorDataSpan[admissibleCount] = new GpuDivisorPartialData(divisorValue);
|
|
||||||
offsetSpan[admissibleCount] = exponentIndex;
|
|
||||||
countSpan[admissibleCount] = 1;
|
|
||||||
cycleSpan[admissibleCount] = divisorCycle;
|
|
||||||
|
|
||||||
admissibleCount++;
|
|
||||||
exponentIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (admissibleCount == 0)
|
if (admissibleCount == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayView1D<GpuDivisorPartialData, Stride1D.Dense> divisorView = divisorDataBuffer.View.SubView(0, admissibleCount);
|
divisorDataView.CopyFromCPU(ref divisorDataRef, admissibleCount);
|
||||||
ArrayView1D<int, Stride1D.Dense> offsetView = offsetBuffer.View.SubView(0, admissibleCount);
|
offsetView.CopyFromCPU(ref offsetRef, admissibleCount);
|
||||||
ArrayView1D<int, Stride1D.Dense> countView = countBuffer.View.SubView(0, admissibleCount);
|
countView.CopyFromCPU(ref countRef, admissibleCount);
|
||||||
ArrayView1D<ulong, Stride1D.Dense> cycleView = cycleBuffer.View.SubView(0, admissibleCount);
|
cycleView.CopyFromCPU(ref cycleRef, admissibleCount);
|
||||||
ArrayView1D<ulong, Stride1D.Dense> exponentView = exponentBuffer.View.SubView(0, exponentIndex);
|
exponentViewDevice.CopyFromCPU(ref exponentRef, admissibleCount);
|
||||||
ArrayView1D<byte, Stride1D.Dense> hitsView = hitsBuffer.View.SubView(0, exponentIndex);
|
|
||||||
ArrayView1D<int, Stride1D.Dense> hitIndexView = hitIndexBuffer.View.SubView(0, 1);
|
|
||||||
|
|
||||||
divisorView.CopyFromCPU(ref MemoryMarshal.GetReference(divisorDataSpan), admissibleCount);
|
|
||||||
offsetView.CopyFromCPU(ref MemoryMarshal.GetReference(offsetSpan), admissibleCount);
|
|
||||||
countView.CopyFromCPU(ref MemoryMarshal.GetReference(countSpan), admissibleCount);
|
|
||||||
cycleView.CopyFromCPU(ref MemoryMarshal.GetReference(cycleSpan), admissibleCount);
|
|
||||||
exponentView.CopyFromCPU(ref MemoryMarshal.GetReference(exponentSpan), exponentIndex);
|
|
||||||
|
|
||||||
int sentinel = int.MaxValue;
|
int sentinel = int.MaxValue;
|
||||||
hitIndexView.CopyFromCPU(ref sentinel, 1);
|
hitIndexView.CopyFromCPU(ref sentinel, 1);
|
||||||
|
|
||||||
kernel(admissibleCount, divisorView, offsetView, countView, exponentView, cycleView, hitsView, hitIndexView);
|
kernel(admissibleCount, divisorDataView, offsetView, countView, exponentViewDevice, cycleView, hitsView, hitIndexView);
|
||||||
|
|
||||||
int firstHit = sentinel;
|
int firstHit = sentinel;
|
||||||
hitIndexView.CopyToCPU(ref firstHit, 1);
|
hitIndexView.CopyToCPU(ref firstHit, 1);
|
||||||
@@ -362,6 +349,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
lastProcessed = hitFound ? divisorSpan[hitIndex] : divisorSpan[lastIndex];
|
lastProcessed = hitFound ? divisorSpan[hitIndex] : divisorSpan[lastIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processedAll = remainingCount == 0UL;
|
||||||
coveredRange = composite || processedAll || (currentDivisor128 > allowedMax128);
|
coveredRange = composite || processedAll || (currentDivisor128 > allowedMax128);
|
||||||
return composite;
|
return composite;
|
||||||
}
|
}
|
||||||
@@ -379,23 +367,80 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static byte AddMod(byte value, byte delta, byte modulus)
|
private static byte AddMod3(byte value, byte delta)
|
||||||
{
|
{
|
||||||
int result = value + delta;
|
const int Modulus = 3;
|
||||||
if (result >= modulus)
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
{
|
{
|
||||||
result -= modulus;
|
sum -= Modulus;
|
||||||
if (result >= modulus)
|
|
||||||
{
|
|
||||||
result -= modulus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte)result;
|
return (byte)sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod5(byte value, byte delta)
|
||||||
|
{
|
||||||
|
const int Modulus = 5;
|
||||||
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
|
{
|
||||||
|
sum -= Modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod7(byte value, byte delta)
|
||||||
|
{
|
||||||
|
const int Modulus = 7;
|
||||||
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
|
{
|
||||||
|
sum -= Modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod10(byte value, byte delta)
|
||||||
|
{
|
||||||
|
const int Modulus = 10;
|
||||||
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
|
{
|
||||||
|
sum -= Modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod11(byte value, byte delta)
|
||||||
|
{
|
||||||
|
const int Modulus = 11;
|
||||||
|
int sum = value + delta;
|
||||||
|
|
||||||
|
if (sum >= Modulus)
|
||||||
|
{
|
||||||
|
sum -= Modulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod8(byte value, byte delta)
|
||||||
|
{
|
||||||
|
return (byte)((value + delta) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static ulong ComputeDivisorLimitFromMaxPrimeGpu(ulong maxPrime)
|
private static ulong ComputeDivisorLimitFromMaxPrimeGpu(ulong maxPrime)
|
||||||
@@ -557,27 +602,25 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
ArrayView1D<byte, Stride1D.Dense> hitView = _hitBuffer.View;
|
ArrayView1D<byte, Stride1D.Dense> hitView = _hitBuffer.View;
|
||||||
|
|
||||||
GpuDivisorPartialData partialData = new GpuDivisorPartialData(divisor);
|
GpuDivisorPartialData partialData = new GpuDivisorPartialData(divisor);
|
||||||
divisorView.SubView(0, 1).CopyFromCPU(ref partialData, 1);
|
divisorView.CopyFromCPU(ref partialData, 1);
|
||||||
|
|
||||||
int offsetValue = 0;
|
int offsetValue = 0;
|
||||||
offsetView.SubView(0, 1).CopyFromCPU(ref offsetValue, 1);
|
offsetView.CopyFromCPU(ref offsetValue, 1);
|
||||||
|
|
||||||
int countValue = length;
|
int countValue = length;
|
||||||
countView.SubView(0, 1).CopyFromCPU(ref countValue, 1);
|
countView.CopyFromCPU(ref countValue, 1);
|
||||||
|
|
||||||
ulong cycleValue = cycle;
|
ulong cycleValue = cycle;
|
||||||
cycleView.SubView(0, 1).CopyFromCPU(ref cycleValue, 1);
|
cycleView.CopyFromCPU(ref cycleValue, 1);
|
||||||
|
|
||||||
Span<ulong> hostSpan = _hostBuffer.AsSpan(0, length);
|
Span<ulong> hostSpan = _hostBuffer.AsSpan(0, length);
|
||||||
primes.CopyTo(hostSpan);
|
primes.CopyTo(hostSpan);
|
||||||
|
ref ulong hostRef = ref MemoryMarshal.GetReference(hostSpan);
|
||||||
|
|
||||||
ArrayView1D<ulong, Stride1D.Dense> exponentSlice = exponentView.SubView(0, length);
|
exponentView.CopyFromCPU(ref hostRef, length);
|
||||||
exponentSlice.CopyFromCPU(ref MemoryMarshal.GetReference(hostSpan), length);
|
|
||||||
|
|
||||||
ArrayView1D<byte, Stride1D.Dense> hitSliceView = hitView.SubView(0, length);
|
|
||||||
|
|
||||||
int sentinel = int.MaxValue;
|
int sentinel = int.MaxValue;
|
||||||
firstHitView.SubView(0, 1).CopyFromCPU(ref sentinel, 1);
|
firstHitView.CopyFromCPU(ref sentinel, 1);
|
||||||
|
|
||||||
var kernel = _kernel;
|
var kernel = _kernel;
|
||||||
if (kernel is null)
|
if (kernel is null)
|
||||||
@@ -586,10 +629,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
throw new InvalidOperationException("GPU divisor kernel was not initialized.");
|
throw new InvalidOperationException("GPU divisor kernel was not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel(1, divisorView, offsetView, countView, exponentSlice, cycleView, hitSliceView, firstHitView);
|
kernel(1, divisorView, offsetView, countView, exponentView, cycleView, hitView, firstHitView);
|
||||||
|
|
||||||
Span<byte> hitSlice = hits.Slice(0, length);
|
Span<byte> hitSlice = hits.Slice(0, length);
|
||||||
hitSliceView.CopyToCPU(ref MemoryMarshal.GetReference(hitSlice), length);
|
ref byte hitRef = ref MemoryMarshal.GetReference(hitSlice);
|
||||||
|
hitView.CopyToCPU(ref hitRef, length);
|
||||||
|
|
||||||
Monitor.Exit(_lease.ExecutionLock);
|
Monitor.Exit(_lease.ExecutionLock);
|
||||||
}
|
}
|
||||||
@@ -688,8 +732,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
internal ulong[] Exponents { get; private set; } = null!;
|
internal ulong[] Exponents { get; private set; } = null!;
|
||||||
|
|
||||||
internal ulong[] FilteredDivisors { get; private set; } = null!;
|
|
||||||
|
|
||||||
internal GpuDivisorPartialData[] DivisorData { get; private set; } = null!;
|
internal GpuDivisorPartialData[] DivisorData { get; private set; } = null!;
|
||||||
|
|
||||||
internal int[] Offsets { get; private set; } = null!;
|
internal int[] Offsets { get; private set; } = null!;
|
||||||
@@ -764,7 +806,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
Divisors = ulongPool.Rent(capacity);
|
Divisors = ulongPool.Rent(capacity);
|
||||||
Exponents = ulongPool.Rent(capacity);
|
Exponents = ulongPool.Rent(capacity);
|
||||||
FilteredDivisors = ulongPool.Rent(capacity);
|
|
||||||
DivisorData = partialPool.Rent(capacity);
|
DivisorData = partialPool.Rent(capacity);
|
||||||
Offsets = intPool.Rent(capacity);
|
Offsets = intPool.Rent(capacity);
|
||||||
Counts = intPool.Rent(capacity);
|
Counts = intPool.Rent(capacity);
|
||||||
@@ -803,14 +844,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
ArrayPool<GpuDivisorPartialData> partialPool = ThreadStaticPools.GpuDivisorPartialDataPool;
|
ArrayPool<GpuDivisorPartialData> partialPool = ThreadStaticPools.GpuDivisorPartialDataPool;
|
||||||
ulongPool.Return(Divisors, clearArray: false);
|
ulongPool.Return(Divisors, clearArray: false);
|
||||||
ulongPool.Return(Exponents, clearArray: false);
|
ulongPool.Return(Exponents, clearArray: false);
|
||||||
ulongPool.Return(FilteredDivisors, clearArray: false);
|
|
||||||
partialPool.Return(DivisorData, clearArray: false);
|
partialPool.Return(DivisorData, clearArray: false);
|
||||||
intPool.Return(Offsets, clearArray: false);
|
intPool.Return(Offsets, clearArray: false);
|
||||||
intPool.Return(Counts, clearArray: false);
|
intPool.Return(Counts, clearArray: false);
|
||||||
ulongPool.Return(Cycles, clearArray: false);
|
ulongPool.Return(Cycles, clearArray: false);
|
||||||
Divisors = Array.Empty<ulong>();
|
Divisors = Array.Empty<ulong>();
|
||||||
Exponents = Array.Empty<ulong>();
|
Exponents = Array.Empty<ulong>();
|
||||||
FilteredDivisors = Array.Empty<ulong>();
|
|
||||||
DivisorData = Array.Empty<GpuDivisorPartialData>();
|
DivisorData = Array.Empty<GpuDivisorPartialData>();
|
||||||
Offsets = Array.Empty<int>();
|
Offsets = Array.Empty<int>();
|
||||||
Counts = Array.Empty<int>();
|
Counts = Array.Empty<int>();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Numerics;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
using ILGPU;
|
using ILGPU;
|
||||||
using ILGPU.Runtime;
|
using ILGPU.Runtime;
|
||||||
@@ -379,7 +380,8 @@ public class MersenneDivisorCycles
|
|||||||
for (int i = 0; i < smallLength; i++)
|
for (int i = 0; i < smallLength; i++)
|
||||||
{
|
{
|
||||||
ulong prime = smallPrimes[i];
|
ulong prime = smallPrimes[i];
|
||||||
if (smallPrimesSquared[i] > remaining)
|
ulong primeSquared = smallPrimesSquared[i];
|
||||||
|
if (primeSquared > remaining)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -438,26 +440,14 @@ public class MersenneDivisorCycles
|
|||||||
|
|
||||||
private static void AddFactor(Dictionary<ulong, int> counts, ulong factor)
|
private static void AddFactor(Dictionary<ulong, int> counts, ulong factor)
|
||||||
{
|
{
|
||||||
if (counts.TryGetValue(factor, out int existing))
|
ref int entry = ref CollectionsMarshal.GetValueRefOrAddDefault(counts, factor, out bool exists);
|
||||||
{
|
entry = exists ? entry + 1 : 1;
|
||||||
counts[factor] = existing + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
counts[factor] = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddFactor(Dictionary<ulong, int> counts, ulong factor, int multiplicity)
|
private static void AddFactor(Dictionary<ulong, int> counts, ulong factor, int multiplicity)
|
||||||
{
|
{
|
||||||
if (counts.TryGetValue(factor, out int existing))
|
ref int entry = ref CollectionsMarshal.GetValueRefOrAddDefault(counts, factor, out bool exists);
|
||||||
{
|
entry = exists ? entry + multiplicity : multiplicity;
|
||||||
counts[factor] = existing + multiplicity;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
counts[factor] = multiplicity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong ReduceOrder(in MontgomeryDivisorData divisorData, ulong initialOrder, Dictionary<ulong, int> factorCounts)
|
private static ulong ReduceOrder(in MontgomeryDivisorData divisorData, ulong initialOrder, Dictionary<ulong, int> factorCounts)
|
||||||
@@ -535,8 +525,8 @@ public class MersenneDivisorCycles
|
|||||||
private static void ProcessReduceOrderPrime(Span<ulong> candidateBuffer, Span<bool> evaluationBuffer, ref ulong order, ulong prime, int multiplicity, ref ExponentRemainderStepperCpu stepper)
|
private static void ProcessReduceOrderPrime(Span<ulong> candidateBuffer, Span<bool> evaluationBuffer, ref ulong order, ulong prime, int multiplicity, ref ExponentRemainderStepperCpu stepper)
|
||||||
{
|
{
|
||||||
ulong working = order;
|
ulong working = order;
|
||||||
int actual = 0;
|
int writeIndex = multiplicity;
|
||||||
while (actual < multiplicity)
|
while (writeIndex > 0)
|
||||||
{
|
{
|
||||||
if (working < prime)
|
if (working < prime)
|
||||||
{
|
{
|
||||||
@@ -549,26 +539,20 @@ public class MersenneDivisorCycles
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
candidateBuffer[actual] = reduced;
|
writeIndex--;
|
||||||
|
candidateBuffer[writeIndex] = reduced;
|
||||||
working = reduced;
|
working = reduced;
|
||||||
actual++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int actual = multiplicity - writeIndex;
|
||||||
if (actual == 0)
|
if (actual == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<ulong> candidates = candidateBuffer.Slice(0, actual);
|
Span<ulong> candidates = candidateBuffer.Slice(writeIndex, actual);
|
||||||
Span<bool> evaluations = evaluationBuffer.Slice(0, actual);
|
Span<bool> evaluations = evaluationBuffer.Slice(0, actual);
|
||||||
|
|
||||||
for (int left = 0, right = actual - 1; left < right; left++, right--)
|
|
||||||
{
|
|
||||||
ulong tmp = candidates[left];
|
|
||||||
candidates[left] = candidates[right];
|
|
||||||
candidates[right] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
stepper.Reset();
|
stepper.Reset();
|
||||||
|
|
||||||
evaluations[0] = stepper.InitializeCpuIsUnity(candidates[0]);
|
evaluations[0] = stepper.InitializeCpuIsUnity(candidates[0]);
|
||||||
@@ -598,24 +582,28 @@ public class MersenneDivisorCycles
|
|||||||
// return 2UL;
|
// return 2UL;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
ulong modulus = n;
|
||||||
|
ulong nMinus1 = modulus - 1UL;
|
||||||
|
ulong nMinus2 = modulus - 2UL;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
ulong c = (NextRandomUInt64() % (n - 1UL)) + 1UL;
|
ulong c = (NextRandomUInt64() % nMinus1) + 1UL;
|
||||||
ulong x = (NextRandomUInt64() % (n - 2UL)) + 2UL;
|
ulong x = (NextRandomUInt64() % nMinus2) + 2UL;
|
||||||
ulong y = x;
|
ulong y = x;
|
||||||
ulong d = 1UL;
|
ulong d = 1UL;
|
||||||
|
|
||||||
while (d == 1UL)
|
while (d == 1UL)
|
||||||
{
|
{
|
||||||
x = AdvancePolynomial(x, c, n);
|
x = AdvancePolynomial(x, c, modulus);
|
||||||
y = AdvancePolynomial(y, c, n);
|
y = AdvancePolynomial(y, c, modulus);
|
||||||
y = AdvancePolynomial(y, c, n);
|
y = AdvancePolynomial(y, c, modulus);
|
||||||
|
|
||||||
ulong diff = x > y ? x - y : y - x;
|
ulong diff = x > y ? x - y : y - x;
|
||||||
d = BinaryGcd(diff, n);
|
d = BinaryGcd(diff, modulus);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d != n)
|
if (d != modulus)
|
||||||
{
|
{
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,9 +93,15 @@ public static class MersenneNumberDivisorByDivisorTester
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while (startIndex > 0 && candidates[startIndex - 1] >= startPrime)
|
while (startIndex > 0)
|
||||||
{
|
{
|
||||||
startIndex--;
|
int previousIndex = startIndex - 1;
|
||||||
|
if (candidates[previousIndex] < startPrime)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex = previousIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,22 +299,16 @@ internal static partial class PrimeOrderCalculator
|
|||||||
Span<ulong> candidates = candidateBuffer[..actual];
|
Span<ulong> candidates = candidateBuffer[..actual];
|
||||||
Span<bool> evaluations = evaluationBuffer[..actual];
|
Span<bool> evaluations = evaluationBuffer[..actual];
|
||||||
|
|
||||||
for (int left = 0, right = actual - 1; left < right; left++, right--)
|
|
||||||
{
|
|
||||||
ulong tmp = candidates[left];
|
|
||||||
candidates[left] = candidates[right];
|
|
||||||
candidates[right] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
stepper.Reset();
|
stepper.Reset();
|
||||||
|
|
||||||
evaluations[0] = stepper.InitializeCpuIsUnity(candidates[0]);
|
int last = actual - 1;
|
||||||
for (int j = 1; j < actual; j++)
|
evaluations[last] = stepper.InitializeCpuIsUnity(candidates[last]);
|
||||||
|
for (int j = last - 1; j >= 0; j--)
|
||||||
{
|
{
|
||||||
evaluations[j] = stepper.ComputeNextIsUnity(candidates[j]);
|
evaluations[j] = stepper.ComputeNextIsUnity(candidates[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = actual - 1; j >= 0; j--)
|
for (int j = 0; j < actual; j++)
|
||||||
{
|
{
|
||||||
if (!evaluations[j])
|
if (!evaluations[j])
|
||||||
{
|
{
|
||||||
@@ -464,16 +458,16 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
Span<ulong> candidates = buffer[..actual];
|
Span<ulong> candidates = buffer[..actual];
|
||||||
candidates.Reverse();
|
|
||||||
|
|
||||||
stepper.Reset();
|
stepper.Reset();
|
||||||
|
|
||||||
if (stepper.InitializeCpuIsUnity(candidates[0]))
|
int last = actual - 1;
|
||||||
|
if (stepper.InitializeCpuIsUnity(candidates[last]))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 1; j < actual; j++)
|
for (int j = last - 1; j >= 0; j--)
|
||||||
{
|
{
|
||||||
if (stepper.ComputeNextIsUnity(candidates[j]))
|
if (stepper.ComputeNextIsUnity(candidates[j]))
|
||||||
{
|
{
|
||||||
@@ -694,13 +688,16 @@ internal static partial class PrimeOrderCalculator
|
|||||||
private static void SortCandidates(ulong prime, ulong? previousOrder, List<ulong> candidates)
|
private static void SortCandidates(ulong prime, ulong? previousOrder, List<ulong> candidates)
|
||||||
{
|
{
|
||||||
ulong previous = previousOrder ?? 0UL;
|
ulong previous = previousOrder ?? 0UL;
|
||||||
int previousGroup = previousOrder.HasValue ? GetGroup(previousOrder.Value, prime) : 1;
|
|
||||||
bool hasPrevious = previousOrder.HasValue;
|
bool hasPrevious = previousOrder.HasValue;
|
||||||
|
ulong threshold1 = prime >> 3;
|
||||||
|
ulong threshold2 = prime >> 2;
|
||||||
|
ulong threshold3 = (prime * 3UL) >> 3;
|
||||||
|
int previousGroup = hasPrevious ? GetGroup(previous, threshold1, threshold2, threshold3) : 1;
|
||||||
|
|
||||||
candidates.Sort((x, y) =>
|
candidates.Sort((x, y) =>
|
||||||
{
|
{
|
||||||
CandidateKey keyX = BuildKey(x, prime, previous, previousGroup, hasPrevious);
|
CandidateKey keyX = BuildKey(x, previous, previousGroup, hasPrevious, threshold1, threshold2, threshold3);
|
||||||
CandidateKey keyY = BuildKey(y, prime, previous, previousGroup, hasPrevious);
|
CandidateKey keyY = BuildKey(y, previous, previousGroup, hasPrevious, threshold1, threshold2, threshold3);
|
||||||
int compare = keyX.Primary.CompareTo(keyY.Primary);
|
int compare = keyX.Primary.CompareTo(keyY.Primary);
|
||||||
if (compare != 0)
|
if (compare != 0)
|
||||||
{
|
{
|
||||||
@@ -717,9 +714,9 @@ internal static partial class PrimeOrderCalculator
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CandidateKey BuildKey(ulong value, ulong prime, ulong previous, int previousGroup, bool hasPrevious)
|
private static CandidateKey BuildKey(ulong value, ulong previous, int previousGroup, bool hasPrevious, ulong threshold1, ulong threshold2, ulong threshold3)
|
||||||
{
|
{
|
||||||
int group = GetGroup(value, prime);
|
int group = GetGroup(value, threshold1, threshold2, threshold3);
|
||||||
if (group == 0)
|
if (group == 0)
|
||||||
{
|
{
|
||||||
return new CandidateKey(int.MaxValue, long.MaxValue, long.MaxValue);
|
return new CandidateKey(int.MaxValue, long.MaxValue, long.MaxValue);
|
||||||
@@ -769,21 +766,18 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return groupOffset + (isGe ? 0 : 1);
|
return groupOffset + (isGe ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetGroup(ulong value, ulong prime)
|
private static int GetGroup(ulong value, ulong threshold1, ulong threshold2, ulong threshold3)
|
||||||
{
|
{
|
||||||
ulong threshold1 = prime >> 3;
|
|
||||||
if (value <= threshold1)
|
if (value <= threshold1)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong threshold2 = prime >> 2;
|
|
||||||
if (value <= threshold2)
|
if (value <= threshold2)
|
||||||
{
|
{
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong threshold3 = (prime * 3UL) >> 3;
|
|
||||||
if (value <= threshold3)
|
if (value <= threshold3)
|
||||||
{
|
{
|
||||||
return 3;
|
return 3;
|
||||||
@@ -828,9 +822,11 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
FactorEntry factor = factors[index];
|
FactorEntry factor = factors[index];
|
||||||
|
int factorExponent = factor.Exponent;
|
||||||
ulong primeFactor = factor.Value;
|
ulong primeFactor = factor.Value;
|
||||||
ulong contribution = 1UL;
|
ulong contribution = 1UL;
|
||||||
for (int exponent = 0; exponent <= factor.Exponent; exponent++)
|
ulong contributionLimit = factorExponent == 0 ? 0UL : order / primeFactor;
|
||||||
|
for (int exponent = 0; exponent <= factorExponent; exponent++)
|
||||||
{
|
{
|
||||||
ulong nextDivisor = divisorProduct * contribution;
|
ulong nextDivisor = divisorProduct * contribution;
|
||||||
if (nextDivisor > order)
|
if (nextDivisor > order)
|
||||||
@@ -844,12 +840,12 @@ internal static partial class PrimeOrderCalculator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exponent == factor.Exponent)
|
if (exponent == factorExponent)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contribution > order / primeFactor)
|
if (contribution > contributionLimit)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -973,24 +969,45 @@ internal static partial class PrimeOrderCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
Span<ulong> candidates = buffer[..actual];
|
Span<ulong> candidates = buffer[..actual];
|
||||||
candidates.Reverse();
|
|
||||||
|
|
||||||
stepper.Reset();
|
stepper.Reset();
|
||||||
|
int last = actual - 1;
|
||||||
|
|
||||||
if (powUsed >= powBudget && powBudget > 0)
|
bool enforceBudget = powBudget > 0;
|
||||||
|
if (!enforceBudget)
|
||||||
|
{
|
||||||
|
powUsed++;
|
||||||
|
if (stepper.InitializeCpuIsUnity(candidates[last]))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = last - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
powUsed++;
|
||||||
|
if (stepper.ComputeNextIsUnity(candidates[j]))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (powUsed >= powBudget)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
powUsed++;
|
powUsed++;
|
||||||
if (stepper.InitializeCpuIsUnity(candidates[0]))
|
if (stepper.InitializeCpuIsUnity(candidates[last]))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 1; j < actual; j++)
|
for (int j = last - 1; j >= 0; j--)
|
||||||
{
|
{
|
||||||
if (powUsed >= powBudget && powBudget > 0)
|
if (powUsed >= powBudget)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# --mersenne=bydivisor Path Map
|
||||||
|
|
||||||
|
This temporary map tracks the code paths exercised by the `--mersenne=bydivisor` mode and notes the latest review status for each branch. Entries move from _Queued_ to _Reviewed_ once the surrounding loops have been audited for invariant work.
|
||||||
|
|
||||||
|
## High-level Flow
|
||||||
|
- `EvenPerfectBitScanner` CLI parsing → _Reviewed_
|
||||||
|
- `MersenneNumberDivisorByDivisorTester.Run` → _Reviewed_
|
||||||
|
- `EvenPerfectBitScanner.IO.CalculationResultsFile`
|
||||||
|
- `LoadCandidatesWithinRange` → _Reviewed_
|
||||||
|
- `EnumerateCandidates` → _Reviewed_
|
||||||
|
- `IMersenneNumberDivisorByDivisorTester` implementations
|
||||||
|
- `MersenneNumberDivisorByDivisorCpuTester`
|
||||||
|
- `IsPrime` → _Reviewed_
|
||||||
|
- `CheckDivisors` (128-bit path) → _Reviewed_
|
||||||
|
- `CheckDivisors64` → _Reviewed_
|
||||||
|
- `MersenneNumberDivisorByDivisorGpuTester`
|
||||||
|
- `IsPrime` → _Reviewed_
|
||||||
|
- `CheckDivisors` → _Reviewed_
|
||||||
|
- `DivisorByDivisorKernels.CheckKernel` → _Reviewed_
|
||||||
|
|
||||||
|
- `MersenneDivisorCycles`
|
||||||
|
- `TryCalculateCycleLengthForExponentCpu` → _Reviewed_
|
||||||
|
- `TryFactorIntoCountsInternal` → _Reviewed_
|
||||||
|
- `PollardRho64` → _Reviewed_
|
||||||
|
- `ReduceOrder` (includes `ProcessReduceOrderPrime`) → _Reviewed_
|
||||||
|
- `PrimeOrderCalculator`
|
||||||
|
- `InitializeStartingOrderCpu` → _Reviewed_
|
||||||
|
- `ExponentLoweringCpu` / `ProcessExponentLoweringPrime` → _Reviewed_
|
||||||
|
- `ValidateOrderAgainstFactors` / `ValidateOrderForFactor` → _Reviewed_
|
||||||
|
- `TryConfirmCandidateCpu` / `CheckCandidateViolation` → _Reviewed_
|
||||||
|
- `TryHeuristicFinishCpu` / `BuildCandidates` / `SortCandidates` → _Reviewed_
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
- Hold for additional guidance on further --mersenne=bydivisor audits; computation branches documented above are fully reviewed.
|
||||||
|
- Defer result aggregation and reporting reviews until instructed otherwise, keeping the focus on computation-only branches.
|
||||||
@@ -136,7 +136,7 @@ The plan incorporates the updated divisor-class heuristics (Groups A/B, wheels,
|
|||||||
* [done] Kept GPU divisibility in standard modular arithmetic while reserving Montgomery transforms for CPU confirmation logic.
|
* [done] Kept GPU divisibility in standard modular arithmetic while reserving Montgomery transforms for CPU confirmation logic.
|
||||||
* [done] When the temporary Group B gate is disabled, delegate GPU calls to the CPU fallback so Group A coverage remains in place while the accelerator path stays available for future re-enablement.
|
* [done] When the temporary Group B gate is disabled, delegate GPU calls to the CPU fallback so Group A coverage remains in place while the accelerator path stays available for future re-enablement.
|
||||||
8. [done] **Refactor by-divisor CPU/GPU testers to reuse heuristics**
|
8. [done] **Refactor by-divisor CPU/GPU testers to reuse heuristics**
|
||||||
* [done] Drove `MersenneNumberDivisorByDivisorCpuTester.CheckDivisors` from `HeuristicPrimeTester.CreateMersenneDivisorEnumerator`, eliminating the bespoke residue loop while preserving the existing status counters.
|
* [done] Drove `MersenneNumberDivisorByDivisorCpuTester.CheckDivisors` from `HeuristicPrimeTester.CreateMersenneDivisorEnumerator`, eliminating the bespoke residue loop and retiring the legacy status counter.
|
||||||
* [done] Streamed GPU batches from the same enumerator and Montgomery preparation data, removing the filtered-divisor scratch arrays while continuing to feed the existing kernel and hit bookkeeping.
|
* [done] Streamed GPU batches from the same enumerator and Montgomery preparation data, removing the filtered-divisor scratch arrays while continuing to feed the existing kernel and hit bookkeeping.
|
||||||
9. [done] **Route legacy prime checks through heuristics**
|
9. [done] **Route legacy prime checks through heuristics**
|
||||||
* [done] Introduce `HeuristicPrimeTester` so heuristic CPU/GPU checks live alongside the legacy `PrimeTester` implementations while callers opt in explicitly.
|
* [done] Introduce `HeuristicPrimeTester` so heuristic CPU/GPU checks live alongside the legacy `PrimeTester` implementations while callers opt in explicitly.
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ Montgomery Primitives
|
|||||||
Cycle-Aware Modular Arithmetic
|
Cycle-Aware Modular Arithmetic
|
||||||
|
|
||||||
Status & Progress Reporting
|
Status & Progress Reporting
|
||||||
- PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs:296: Replace this modulo with the ring-buffer style counter (subtract loop) used in the fast CLI status benchmarks so we avoid `%` in this hot loop while still wrapping progress correctly.
|
|
||||||
|
|
||||||
Cycle Remainder Steppers
|
Cycle Remainder Steppers
|
||||||
- PerfectNumbers.Core/CycleRemainderStepper.cs:28: Inline this reset at the call sites so the hot loops reuse struct reinitialization measured fastest in MersenneDivisorCycleLengthGpuBenchmarks, avoiding the extra method call when scanners need to restart stepping.
|
- PerfectNumbers.Core/CycleRemainderStepper.cs:28: Inline this reset at the call sites so the hot loops reuse struct reinitialization measured fastest in MersenneDivisorCycleLengthGpuBenchmarks, avoiding the extra method call when scanners need to restart stepping.
|
||||||
|
|||||||
Reference in new issue
Block a user