Merge pull request #31 from mlemanczyk/codex/modify-evenperfectbitscanner-for-uint128-divisors

Allow residue GPU scanning to cover UInt128 divisors
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-09-20 02:49:36 +02:00
commit ff2c516a0e
6 files changed
+35 -29

No files matched your search

+11 -5
View File
@@ -1,3 +1,4 @@
using System.Globalization;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
@@ -73,11 +74,12 @@ internal static class Program
bool mersenneOnGpu = true; // controls Lucas/incremental/pow2mod device bool mersenneOnGpu = true; // controls Lucas/incremental/pow2mod device
bool orderOnGpu = true; // controls order computations device bool orderOnGpu = true; // controls order computations device
int scanBatchSize = 2_097_152, sliceSize = 32; int scanBatchSize = 2_097_152, sliceSize = 32;
ulong residueKMax = 5_000_000UL; UInt128 residueKMax = 5_000_000UL;
string filterFile = string.Empty; string filterFile = string.Empty;
string cyclesPath = DefaultCyclesPath; string cyclesPath = DefaultCyclesPath;
int cyclesBatchSize = 512; int cyclesBatchSize = 512;
bool continueCyclesGeneration = false; bool continueCyclesGeneration = false;
UInt128 divisorCyclesSearchLimit128 = PerfectNumberConstants.ExtraDivisorCycleSearchLimit;
ulong divisorCyclesSearchLimit = PerfectNumberConstants.ExtraDivisorCycleSearchLimit; ulong divisorCyclesSearchLimit = PerfectNumberConstants.ExtraDivisorCycleSearchLimit;
// NTT backend selection (GPU): reference vs staged // NTT backend selection (GPU): reference vs staged
@@ -143,12 +145,16 @@ internal static class Program
else if (arg.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); int eq = arg.IndexOf('=');
divisorCyclesSearchLimit = ulong.Parse(arg.AsSpan(eq + 1)); if (UInt128.TryParse(arg.AsSpan(eq + 1), NumberStyles.Integer, provider: null, out var limit))
{
divisorCyclesSearchLimit128 = limit;
divisorCyclesSearchLimit = limit > (UInt128)ulong.MaxValue ? ulong.MaxValue : (ulong)limit;
}
} }
else if (arg.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase)) else if (arg.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase))
{ {
int eq = arg.IndexOf('='); int eq = arg.IndexOf('=');
if (ulong.TryParse(arg.AsSpan(eq + 1), out var kmax)) if (UInt128.TryParse(arg.AsSpan(eq + 1), NumberStyles.Integer, provider: null, out var kmax))
{ {
residueKMax = kmax; residueKMax = kmax;
} }
@@ -449,7 +455,7 @@ internal static class Program
useGpuOrder: orderOnGpu, useGpuOrder: orderOnGpu,
useResidue: useResidue, useResidue: useResidue,
maxK: residueKMax, maxK: residueKMax,
residueDivisorSets: divisorCyclesSearchLimit); residueDivisorSets: divisorCyclesSearchLimit128);
if (!useLucas) if (!useLucas)
{ {
tester.WarmUpOrders(currentP, _orderWarmupLimitOverride ?? 5_000_000UL); tester.WarmUpOrders(currentP, _orderWarmupLimitOverride ?? 5_000_000UL);
@@ -741,7 +747,7 @@ internal static class Program
Console.WriteLine(" --divisor-cycles-device=cpu|gpu device for cycles generation (default gpu)"); Console.WriteLine(" --divisor-cycles-device=cpu|gpu device for cycles generation (default gpu)");
Console.WriteLine(" --divisor-cycles-batch-size=<value> batch size for cycles generation (default 512)"); Console.WriteLine(" --divisor-cycles-batch-size=<value> batch size for cycles generation (default 512)");
Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation"); Console.WriteLine(" --divisor-cycles-continue continue divisor cycles generation");
Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor or residue"); Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor or residue (UInt128 supported; residue path honors full range)");
Console.WriteLine(" --use-order test primality via q order"); Console.WriteLine(" --use-order test primality via q order");
Console.WriteLine(" --workaround-mod avoid '%' operator on the GPU"); Console.WriteLine(" --workaround-mod avoid '%' operator on the GPU");
@@ -30,7 +30,7 @@ public class MersenneNumberResidueCpuTesterTests
(UInt128)exponent << 1, (UInt128)exponent << 1,
LastDigitIsSeven(exponent), LastDigitIsSeven(exponent),
(UInt128)maxK, (UInt128)maxK,
1UL, UInt128.One,
(UInt128)maxK, (UInt128)maxK,
ref isPrime, ref isPrime,
ref exhausted); ref exhausted);
@@ -33,7 +33,7 @@ public class MersenneNumberResidueGpuTesterTests
(UInt128)exponent << 1, (UInt128)exponent << 1,
LastDigitIsSeven(exponent), LastDigitIsSeven(exponent),
(UInt128)maxK, (UInt128)maxK,
1UL, UInt128.One,
(UInt128)maxK, (UInt128)maxK,
ref isPrime, ref isPrime,
ref exhausted); ref exhausted);
@@ -10,12 +10,12 @@ public class MersenneNumberResidueCpuTester
UInt128 twoP, UInt128 twoP,
bool lastIsSeven, bool lastIsSeven,
UInt128 perSetLimit, UInt128 perSetLimit,
ulong setCount, UInt128 setCount,
UInt128 overallLimit, UInt128 overallLimit,
ref bool isPrime, ref bool isPrime,
ref bool divisorsExhausted) ref bool divisorsExhausted)
{ {
if (setCount == 0UL || perSetLimit == UInt128.Zero || overallLimit == UInt128.Zero) if (setCount == UInt128.Zero || perSetLimit == UInt128.Zero || overallLimit == UInt128.Zero)
{ {
return; return;
} }
@@ -52,9 +52,9 @@ public class MersenneNumberResidueCpuTester
ModResidueTracker tracker = _mersenneResidueTracker!; ModResidueTracker tracker = _mersenneResidueTracker!;
UInt128 limitInclusive = overallLimit + UInt128.One; UInt128 limitInclusive = overallLimit + UInt128.One;
for (ulong setIndex = 0; setIndex < setCount && Volatile.Read(ref isPrime); setIndex++) for (UInt128 setIndex = UInt128.Zero; setIndex < setCount && Volatile.Read(ref isPrime); setIndex++)
{ {
UInt128 setOffset = perSetLimit * (UInt128)setIndex; UInt128 setOffset = perSetLimit * setIndex;
UInt128 k = setOffset + UInt128.One; UInt128 k = setOffset + UInt128.One;
if (k >= limitInclusive) if (k >= limitInclusive)
{ {
@@ -17,12 +17,12 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
UInt128 twoP, UInt128 twoP,
bool lastIsSeven, bool lastIsSeven,
UInt128 perSetLimit, UInt128 perSetLimit,
ulong setCount, UInt128 setCount,
UInt128 overallLimit, UInt128 overallLimit,
ref bool isPrime, ref bool isPrime,
ref bool divisorsExhausted) ref bool divisorsExhausted)
{ {
if (setCount == 0UL || perSetLimit == UInt128.Zero || overallLimit == UInt128.Zero) if (setCount == UInt128.Zero || perSetLimit == UInt128.Zero || overallLimit == UInt128.Zero)
{ {
return; return;
} }
@@ -48,9 +48,9 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
Span<ulong> orders = orderArray.AsSpan(0, batchSize); Span<ulong> orders = orderArray.AsSpan(0, batchSize);
try try
{ {
for (ulong setIndex = 0; setIndex < setCount && Volatile.Read(ref isPrime); setIndex++) for (UInt128 setIndex = UInt128.Zero; setIndex < setCount && Volatile.Read(ref isPrime); setIndex++)
{ {
UInt128 setOffset = perSetLimit * (UInt128)setIndex; UInt128 setOffset = perSetLimit * setIndex;
UInt128 setStart = setOffset + UInt128.One; UInt128 setStart = setOffset + UInt128.One;
if (setStart >= limitInclusive) if (setStart >= limitInclusive)
{ {
+14 -14
View File
@@ -25,13 +25,13 @@ public sealed class MersenneNumberTester(
bool useGpuScan = true, bool useGpuScan = true,
bool useGpuOrder = true, bool useGpuOrder = true,
bool useResidue = true, bool useResidue = true,
ulong maxK = 5_000_000UL, UInt128? maxK = null,
ulong residueDivisorSets = PerfectNumberConstants.ExtraDivisorCycleSearchLimit) UInt128? residueDivisorSets = null)
{ {
private readonly bool _useResidue = useResidue; private readonly bool _useResidue = useResidue;
private readonly bool _useIncremental = useIncremental && !useResidue; private readonly bool _useIncremental = useIncremental && !useResidue;
private readonly ulong _maxK = maxK; private readonly UInt128 _maxK = maxK ?? (UInt128)5_000_000UL;
private readonly ulong _residueDivisorSetCount = residueDivisorSets; private readonly UInt128 _residueDivisorSetCount = residueDivisorSets ?? (UInt128)PerfectNumberConstants.ExtraDivisorCycleSearchLimit;
private readonly GpuKernelType _kernelType = kernelType; private readonly GpuKernelType _kernelType = kernelType;
private readonly bool _useModuloWorkaround = useModuloWorkaround; private readonly bool _useModuloWorkaround = useModuloWorkaround;
private readonly bool _useGpuLucas = useGpuLucas; private readonly bool _useGpuLucas = useGpuLucas;
@@ -246,17 +246,17 @@ public sealed class MersenneNumberTester(
// In residue mode the residue scan is the final primality check. // In residue mode the residue scan is the final primality check.
if (_useResidue) if (_useResidue)
{ {
if ((UInt128)_maxK < maxK) if (_maxK < maxK)
{ {
maxK = _maxK; maxK = _maxK;
} }
if (_maxK == 0UL || _residueDivisorSetCount == 0UL) if (_maxK == UInt128.Zero || _residueDivisorSetCount == UInt128.Zero)
{ {
return prePrime; return prePrime;
} }
UInt128 totalLimit = (UInt128)_maxK; UInt128 totalLimit = _maxK;
if (totalLimit > maxK) if (totalLimit > maxK)
{ {
totalLimit = maxK; totalLimit = maxK;
@@ -267,23 +267,23 @@ public sealed class MersenneNumberTester(
return true; return true;
} }
ulong requestedSets = _residueDivisorSetCount; UInt128 requestedSets = _residueDivisorSetCount;
if (requestedSets == 0UL) if (requestedSets == UInt128.Zero)
{ {
requestedSets = 1UL; requestedSets = UInt128.One;
} }
UInt128 perSetLimit = DivideRoundUp(totalLimit, (UInt128)requestedSets); UInt128 perSetLimit = DivideRoundUp(totalLimit, requestedSets);
if (perSetLimit == UInt128.Zero) if (perSetLimit == UInt128.Zero)
{ {
perSetLimit = UInt128.One; perSetLimit = UInt128.One;
} }
UInt128 setsNeeded = DivideRoundUp(totalLimit, perSetLimit); UInt128 setsNeeded = DivideRoundUp(totalLimit, perSetLimit);
ulong effectiveSets = requestedSets; UInt128 effectiveSets = requestedSets;
if (setsNeeded < (UInt128)effectiveSets) if (setsNeeded < effectiveSets)
{ {
effectiveSets = (ulong)setsNeeded; effectiveSets = setsNeeded;
} }
bool scanCompleted = false; bool scanCompleted = false;