Revert "Merge pull request #24 from mlemanczyk/codex/modify-evenperfectbitscanner-for-residue-mode"
This reverts commit2d024cc708, reversing changes made tob1c29ad16e.
This commit is contained in:
1 parent
deb25796a8
commit
8a6fc0c6a5
7 files changed
+48
-334
No files matched your search
@@ -165,95 +165,6 @@ public class ProgramTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Divisor_mode_sets_detailedCheck_when_divisor_is_found()
|
|
||||||
{
|
|
||||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var divisorField = typeof(Program).GetField("_divisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var candidatesField = typeof(MersenneNumberDivisorGpuTester).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var residueField = typeof(Program).GetField("PResidue", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
useDivisorField.SetValue(null, true);
|
|
||||||
divisorField.SetValue(null, UInt128.Zero);
|
|
||||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
|
||||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
|
||||||
candidatesField.SetValue(null, new (ulong, uint)[] { (23UL, 11U) });
|
|
||||||
forceCpuProp!.SetValue(null, true);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Program.IsEvenPerfectCandidate(11UL, 32UL, out bool searched, out bool detailedCheck).Should().BeFalse();
|
|
||||||
searched.Should().BeTrue();
|
|
||||||
detailedCheck.Should().BeTrue();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
useDivisorField.SetValue(null, false);
|
|
||||||
testerField.SetValue(null, null);
|
|
||||||
candidatesField.SetValue(null, null);
|
|
||||||
primeField.SetValue(null, null);
|
|
||||||
residueField.SetValue(null, null);
|
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Residue_mode_sets_detailedCheck_based_on_exhaustion()
|
|
||||||
{
|
|
||||||
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var residueModeField = typeof(Program).GetField("_useResidueMode", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var residueField = typeof(Program).GetField("PResidue", BindingFlags.NonPublic | BindingFlags.Static)!;
|
|
||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
|
||||||
|
|
||||||
const ulong exponent = 7UL;
|
|
||||||
|
|
||||||
useDivisorField.SetValue(null, false);
|
|
||||||
residueModeField.SetValue(null, true);
|
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
|
||||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
|
||||||
forceCpuProp!.SetValue(null, true);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
|
||||||
useGpuScan: false,
|
|
||||||
useGpuOrder: false,
|
|
||||||
useResidue: true,
|
|
||||||
maxK: 0UL,
|
|
||||||
residueDivisorSets: 0UL), trackAllValues: true));
|
|
||||||
|
|
||||||
Program.IsEvenPerfectCandidate(exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
|
|
||||||
searched.Should().BeTrue();
|
|
||||||
detailedCheck.Should().BeFalse();
|
|
||||||
|
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
|
||||||
useGpuScan: false,
|
|
||||||
useGpuOrder: false,
|
|
||||||
useResidue: true,
|
|
||||||
maxK: 4UL,
|
|
||||||
residueDivisorSets: 2UL), trackAllValues: true));
|
|
||||||
|
|
||||||
Program.IsEvenPerfectCandidate(exponent, 0UL, out searched, out detailedCheck).Should().BeTrue();
|
|
||||||
searched.Should().BeTrue();
|
|
||||||
detailedCheck.Should().BeTrue();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
mersenneField.SetValue(null, null);
|
|
||||||
primeField.SetValue(null, null);
|
|
||||||
residueField.SetValue(null, null);
|
|
||||||
residueModeField.SetValue(null, false);
|
|
||||||
forceCpuProp!.SetValue(null, false);
|
|
||||||
useDivisorField.SetValue(null, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Divisor_mode_accepts_large_search_limit()
|
public void Divisor_mode_accepts_large_search_limit()
|
||||||
{
|
{
|
||||||
@@ -335,7 +246,7 @@ public class ProgramTests
|
|||||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||||
|
|
||||||
useDivisorField.SetValue(null, false);
|
useDivisorField.SetValue(null, false);
|
||||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: true, maxK: 1_000UL, residueDivisorSets: 1UL), trackAllValues: true));
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: true, maxK: 1_000UL), trackAllValues: true));
|
||||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
forceCpuProp!.SetValue(null, false);
|
forceCpuProp!.SetValue(null, false);
|
||||||
@@ -344,7 +255,7 @@ public class ProgramTests
|
|||||||
{
|
{
|
||||||
Program.IsEvenPerfectCandidate(29UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
|
Program.IsEvenPerfectCandidate(29UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||||
searched.Should().BeTrue();
|
searched.Should().BeTrue();
|
||||||
detailed.Should().BeTrue();
|
detailed.Should().BeFalse();
|
||||||
|
|
||||||
Program.IsEvenPerfectCandidate(31UL, 0UL, out searched, out detailed).Should().BeTrue();
|
Program.IsEvenPerfectCandidate(31UL, 0UL, out searched, out detailed).Should().BeTrue();
|
||||||
searched.Should().BeTrue();
|
searched.Should().BeTrue();
|
||||||
|
|||||||
@@ -460,8 +460,7 @@ internal static class Program
|
|||||||
useGpuScan: mersenneOnGpu,
|
useGpuScan: mersenneOnGpu,
|
||||||
useGpuOrder: orderOnGpu,
|
useGpuOrder: orderOnGpu,
|
||||||
useResidue: useResidue,
|
useResidue: useResidue,
|
||||||
maxK: residueKMax,
|
maxK: residueKMax);
|
||||||
residueDivisorSets: divisorCyclesSearchLimit128);
|
|
||||||
if (!useLucas)
|
if (!useLucas)
|
||||||
{
|
{
|
||||||
tester.WarmUpOrders(currentP, _orderWarmupLimitOverride ?? 5_000_000UL);
|
tester.WarmUpOrders(currentP, _orderWarmupLimitOverride ?? 5_000_000UL);
|
||||||
@@ -767,7 +766,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 (UInt128 supported; residue path honors full range)");
|
Console.WriteLine(" --divisor-cycles-limit=<value> cycle search iterations when --mersenne=divisor");
|
||||||
|
|
||||||
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");
|
||||||
@@ -1127,30 +1126,8 @@ internal static class Program
|
|||||||
return _divisorTester!.IsPrime(p, _divisor, divisorCyclesSearchLimit, out detailedCheck);
|
return _divisorTester!.IsPrime(p, _divisor, divisorCyclesSearchLimit, out detailedCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mersennePrime = MersenneTesters.Value!.IsMersennePrime(p, out bool residueExhausted);
|
detailedCheck = MersenneTesters.Value!.IsMersennePrime(p);
|
||||||
detailedCheck = residueExhausted;
|
return detailedCheck;
|
||||||
return mersennePrime;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static UInt128 ResolveResidueMaxK(
|
|
||||||
bool useResidue,
|
|
||||||
bool residueMaxExplicit,
|
|
||||||
bool divisorLimitExplicit,
|
|
||||||
UInt128 residueMaxK,
|
|
||||||
UInt128 divisorLimit)
|
|
||||||
{
|
|
||||||
if (!useResidue)
|
|
||||||
{
|
|
||||||
return residueMaxK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (residueMaxExplicit || !divisorLimitExplicit)
|
|
||||||
{
|
|
||||||
return residueMaxK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return divisorLimit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
|
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
|
||||||
|
|||||||
@@ -69,18 +69,8 @@ public class MersenneNumberResidueCpuTesterTests
|
|||||||
bool expectedPrime)
|
bool expectedPrime)
|
||||||
{
|
{
|
||||||
bool isPrime = true;
|
bool isPrime = true;
|
||||||
bool exhausted = false;
|
tester.Scan(exponent, (UInt128)exponent << 1, LastDigitIsSeven(exponent), (UInt128)maxK, ref isPrime);
|
||||||
tester.Scan(
|
|
||||||
exponent,
|
|
||||||
(UInt128)exponent << 1,
|
|
||||||
LastDigitIsSeven(exponent),
|
|
||||||
(UInt128)perSetLimit,
|
|
||||||
(UInt128)setCount,
|
|
||||||
(UInt128)overallLimit,
|
|
||||||
ref isPrime,
|
|
||||||
ref exhausted);
|
|
||||||
isPrime.Should().Be(expectedPrime);
|
isPrime.Should().Be(expectedPrime);
|
||||||
exhausted.Should().BeTrue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,18 +288,8 @@ public class MersenneNumberResidueGpuTesterTests
|
|||||||
bool expectedPrime)
|
bool expectedPrime)
|
||||||
{
|
{
|
||||||
bool isPrime = true;
|
bool isPrime = true;
|
||||||
bool exhausted = false;
|
tester.Scan(exponent, (UInt128)exponent << 1, LastDigitIsSeven(exponent), (UInt128)maxK, ref isPrime);
|
||||||
tester.Scan(
|
|
||||||
exponent,
|
|
||||||
(UInt128)exponent << 1,
|
|
||||||
LastDigitIsSeven(exponent),
|
|
||||||
(UInt128)perSetLimit,
|
|
||||||
(UInt128)setCount,
|
|
||||||
(UInt128)overallLimit,
|
|
||||||
ref isPrime,
|
|
||||||
ref exhausted);
|
|
||||||
isPrime.Should().Be(expectedPrime);
|
isPrime.Should().Be(expectedPrime);
|
||||||
exhausted.Should().BeTrue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5,21 +5,8 @@ public class MersenneNumberResidueCpuTester
|
|||||||
private ModResidueTracker? _mersenneResidueTracker;
|
private ModResidueTracker? _mersenneResidueTracker;
|
||||||
|
|
||||||
// CPU residue variant using tracker + unrolled residue updates (no method calls in the loop).
|
// CPU residue variant using tracker + unrolled residue updates (no method calls in the loop).
|
||||||
public void Scan(
|
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
|
||||||
ulong exponent,
|
|
||||||
UInt128 twoP,
|
|
||||||
bool lastIsSeven,
|
|
||||||
UInt128 perSetLimit,
|
|
||||||
UInt128 setCount,
|
|
||||||
UInt128 overallLimit,
|
|
||||||
ref bool isPrime,
|
|
||||||
ref bool divisorsExhausted)
|
|
||||||
{
|
{
|
||||||
if (setCount == UInt128.Zero || perSetLimit == UInt128.Zero || overallLimit == UInt128.Zero)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize/update Mersenne residue tracker and start a merge walk for ascending q divisors
|
// Initialize/update Mersenne residue tracker and start a merge walk for ascending q divisors
|
||||||
|
|
||||||
_mersenneResidueTracker ??= new(ResidueModel.Mersenne);
|
_mersenneResidueTracker ??= new(ResidueModel.Mersenne);
|
||||||
@@ -27,6 +14,12 @@ public class MersenneNumberResidueCpuTester
|
|||||||
// and avoid per-q powmods when possible when scanning lanes.
|
// and avoid per-q powmods when possible when scanning lanes.
|
||||||
_mersenneResidueTracker.BeginMerge(exponent);
|
_mersenneResidueTracker.BeginMerge(exponent);
|
||||||
|
|
||||||
|
// Start at k = 1: q0 = 2*p*1 + 1
|
||||||
|
UInt128 k = UInt128.One;
|
||||||
|
UInt128 q = twoP + UInt128.One;
|
||||||
|
// Current residues
|
||||||
|
q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3);
|
||||||
|
// Step residues for q += 2*p
|
||||||
ulong step10 = (ulong)(twoP % 10UL) % 10UL;
|
ulong step10 = (ulong)(twoP % 10UL) % 10UL;
|
||||||
ulong step8 = (ulong)(twoP % 8UL);
|
ulong step8 = (ulong)(twoP % 8UL);
|
||||||
ulong step3 = (ulong)(twoP % 3UL);
|
ulong step3 = (ulong)(twoP % 3UL);
|
||||||
@@ -50,39 +43,18 @@ public class MersenneNumberResidueCpuTester
|
|||||||
|
|
||||||
bool localIsPrime, isDivider;
|
bool localIsPrime, isDivider;
|
||||||
|
|
||||||
|
maxK++;
|
||||||
|
|
||||||
ModResidueTracker tracker = _mersenneResidueTracker!;
|
ModResidueTracker tracker = _mersenneResidueTracker!;
|
||||||
UInt128 limitInclusive = overallLimit + UInt128.One;
|
while (k < maxK && (localIsPrime = Volatile.Read(ref isPrime)))
|
||||||
for (UInt128 setIndex = UInt128.Zero; setIndex < setCount && Volatile.Read(ref isPrime); setIndex++)
|
|
||||||
{
|
{
|
||||||
UInt128 setOffset = perSetLimit * setIndex;
|
remaining = maxK - k;
|
||||||
UInt128 k = setOffset + UInt128.One;
|
|
||||||
if (k >= limitInclusive)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt128 setLimitExclusive = setOffset + perSetLimit + UInt128.One;
|
|
||||||
if (setLimitExclusive > limitInclusive)
|
|
||||||
{
|
|
||||||
setLimitExclusive = limitInclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt128 q = twoP * k + UInt128.One;
|
|
||||||
q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3);
|
|
||||||
|
|
||||||
while (k < setLimitExclusive && (localIsPrime = Volatile.Read(ref isPrime)))
|
|
||||||
{
|
|
||||||
remaining = setLimitExclusive - k;
|
|
||||||
if (remaining == UInt128.Zero)
|
if (remaining == UInt128.Zero)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
lanes = remaining > 4UL ? 4 : (int)remaining;
|
lanes = remaining > 4UL ? 4 : (int)remaining;
|
||||||
if (lanes <= 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute lane residues incrementally (avoid multiplications, minimize reductions)
|
// Compute lane residues incrementally (avoid multiplications, minimize reductions)
|
||||||
// r10
|
// r10
|
||||||
@@ -113,6 +85,7 @@ public class MersenneNumberResidueCpuTester
|
|||||||
if (lanes >= 1 &&
|
if (lanes >= 1 &&
|
||||||
(((allowMask >> (int)r10_0) & 1) != 0 && (r8_0 == 1UL || r8_0 == 7UL) && r3_0 != 0UL && r5_0 != 0UL))
|
(((allowMask >> (int)r10_0) & 1) != 0 && (r8_0 == 1UL || r8_0 == 7UL) && r3_0 != 0UL && r5_0 != 0UL))
|
||||||
{
|
{
|
||||||
|
// cycle-based quick check for small q
|
||||||
if (localIsPrime && tracker.MergeOrAppend(exponent, q, out isDivider) && isDivider &&
|
if (localIsPrime && tracker.MergeOrAppend(exponent, q, out isDivider) && isDivider &&
|
||||||
(q <= ulong.MaxValue ? ((ulong)q).IsPrimeCandidate() : q.IsPrimeCandidate()))
|
(q <= ulong.MaxValue ? ((ulong)q).IsPrimeCandidate() : q.IsPrimeCandidate()))
|
||||||
{
|
{
|
||||||
@@ -162,12 +135,10 @@ public class MersenneNumberResidueCpuTester
|
|||||||
if (!localIsPrime)
|
if (!localIsPrime)
|
||||||
{
|
{
|
||||||
Volatile.Write(ref isPrime, false);
|
Volatile.Write(ref isPrime, false);
|
||||||
divisorsExhausted = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!Volatile.Read(ref isPrime))
|
else if (!Volatile.Read(ref isPrime))
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,9 +162,8 @@ public class MersenneNumberResidueCpuTester
|
|||||||
r3 += step3; if (r3 >= 3UL) r3 -= 3UL;
|
r3 += step3; if (r3 >= 3UL) r3 -= 3UL;
|
||||||
r5 += step5; if (r5 >= 10UL) r5 -= 10UL; if (r5 >= 5UL) r5 -= 5UL;
|
r5 += step5; if (r5 >= 10UL) r5 -= 10UL; if (r5 >= 5UL) r5 -= 5UL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
divisorsExhausted = true;
|
return;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,29 +12,15 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
// via GpuKernelPool to avoid host round-trips.
|
// via GpuKernelPool to avoid host round-trips.
|
||||||
|
|
||||||
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
|
// GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1.
|
||||||
public void Scan(
|
public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime)
|
||||||
ulong exponent,
|
|
||||||
UInt128 twoP,
|
|
||||||
bool lastIsSeven,
|
|
||||||
UInt128 perSetLimit,
|
|
||||||
UInt128 setCount,
|
|
||||||
UInt128 overallLimit,
|
|
||||||
ref bool isPrime,
|
|
||||||
ref bool divisorsExhausted)
|
|
||||||
{
|
{
|
||||||
if (setCount == UInt128.Zero || perSetLimit == UInt128.Zero || overallLimit == UInt128.Zero)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder);
|
||||||
var execution = gpuLease.EnterExecutionScope();
|
|
||||||
var accelerator = gpuLease.Accelerator;
|
var accelerator = gpuLease.Accelerator;
|
||||||
var stream = gpuLease.Stream;
|
|
||||||
// Ensure device has small cycles and primes tables for in-kernel lookup
|
// Ensure device has small cycles and primes tables for in-kernel lookup
|
||||||
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator);
|
||||||
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
|
ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator);
|
||||||
int batchSize = GpuConstants.ScanBatchSize;
|
int batchSize = GpuConstants.ScanBatchSize;
|
||||||
|
UInt128 kStart = 1UL;
|
||||||
byte last = lastIsSeven ? (byte)1 : (byte)0;
|
byte last = lastIsSeven ? (byte)1 : (byte)0;
|
||||||
var kernel = gpuLease.Pow2ModKernel;
|
var kernel = gpuLease.Pow2ModKernel;
|
||||||
ulong step10 = ((exponent % 10UL) << 1) % 10UL;
|
ulong step10 = ((exponent % 10UL) << 1) % 10UL;
|
||||||
@@ -52,59 +38,41 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
// testers such as the order kernels.
|
// testers such as the order kernels.
|
||||||
orderBuffer.MemSetToZero();
|
orderBuffer.MemSetToZero();
|
||||||
ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize);
|
ulong[] orderArray = ArrayPool<ulong>.Shared.Rent(batchSize);
|
||||||
UInt128 batchSize128 = (UInt128)batchSize;
|
UInt128 remaining;
|
||||||
UInt128 limitInclusive = overallLimit == UInt128.MaxValue
|
int currentSize;
|
||||||
? overallLimit
|
int i;
|
||||||
: checked(overallLimit + UInt128.One);
|
UInt128 batchSize128 = (UInt128)batchSize, q;
|
||||||
Span<ulong> orders = orderArray.AsSpan(0, batchSize);
|
Span<ulong> orders = orderArray.AsSpan(0, batchSize);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (UInt128 setIndex = UInt128.Zero; setIndex < setCount && Volatile.Read(ref isPrime); setIndex++)
|
while (kStart < maxK && Volatile.Read(ref isPrime))
|
||||||
{
|
{
|
||||||
UInt128 setOffset = checked(perSetLimit * setIndex);
|
remaining = maxK - kStart;
|
||||||
UInt128 setStart = checked(setOffset + UInt128.One);
|
if (remaining > batchSize128)
|
||||||
if (setStart >= limitInclusive)
|
|
||||||
{
|
{
|
||||||
break;
|
currentSize = batchSize;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
UInt128 setLimitExclusive = checked(setOffset + perSetLimit);
|
|
||||||
setLimitExclusive = setLimitExclusive >= limitInclusive
|
|
||||||
? limitInclusive
|
|
||||||
: checked(setLimitExclusive + UInt128.One);
|
|
||||||
if (setLimitExclusive > limitInclusive)
|
|
||||||
{
|
{
|
||||||
setLimitExclusive = limitInclusive;
|
currentSize = (int)remaining;
|
||||||
}
|
|
||||||
|
|
||||||
UInt128 kStart = setStart;
|
|
||||||
while (kStart < setLimitExclusive && Volatile.Read(ref isPrime))
|
|
||||||
{
|
|
||||||
UInt128 remaining = checked(setLimitExclusive - kStart);
|
|
||||||
int currentSize = remaining > batchSize128 ? batchSize : (int)remaining;
|
|
||||||
if (currentSize <= 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
orders = orderArray.AsSpan(0, currentSize);
|
orders = orderArray.AsSpan(0, currentSize);
|
||||||
|
}
|
||||||
|
|
||||||
UInt128 q = checked(twoP * kStart);
|
q = twoP * kStart + UInt128.One;
|
||||||
q = checked(q + UInt128.One);
|
|
||||||
q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3);
|
||||||
var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5);
|
||||||
|
|
||||||
kernel(stream, currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
|
kernel(currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL,
|
||||||
ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
|
ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2);
|
||||||
|
|
||||||
stream.Synchronize();
|
accelerator.Synchronize();
|
||||||
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize);
|
||||||
stream.Synchronize();
|
|
||||||
if (!Volatile.Read(ref isPrime))
|
if (!Volatile.Read(ref isPrime))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < currentSize; i++)
|
for (i = 0; i < currentSize; i++)
|
||||||
{
|
{
|
||||||
if (orders[i] != 0UL)
|
if (orders[i] != 0UL)
|
||||||
{
|
{
|
||||||
@@ -113,18 +81,14 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kStart = checked(kStart + (UInt128)currentSize);
|
kStart += batchSize128;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
ArrayPool<ulong>.Shared.Return(orderArray);
|
ArrayPool<ulong>.Shared.Return(orderArray);
|
||||||
orderBuffer.Dispose();
|
orderBuffer.Dispose();
|
||||||
execution.Dispose();
|
|
||||||
gpuLease.Dispose();
|
gpuLease.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
divisorsExhausted = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,13 +25,11 @@ public sealed class MersenneNumberTester(
|
|||||||
bool useGpuScan = true,
|
bool useGpuScan = true,
|
||||||
bool useGpuOrder = true,
|
bool useGpuOrder = true,
|
||||||
bool useResidue = true,
|
bool useResidue = true,
|
||||||
UInt128? maxK = null,
|
ulong maxK = 5_000_000UL)
|
||||||
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 UInt128 _maxK = maxK ?? (UInt128)5_000_000UL;
|
private readonly ulong _maxK = maxK;
|
||||||
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;
|
||||||
@@ -50,23 +48,6 @@ public sealed class MersenneNumberTester(
|
|||||||
// TODO: Ensure Program passes useResidue correctly and that residue-vs-incremental-vs-LL selection respects CLI flags.
|
// TODO: Ensure Program passes useResidue correctly and that residue-vs-incremental-vs-LL selection respects CLI flags.
|
||||||
// Also consider injecting a shared cycles cache (MersenneDivisorCycles.Shared) to both CPU and GPU testers.
|
// Also consider injecting a shared cycles cache (MersenneDivisorCycles.Shared) to both CPU and GPU testers.
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static UInt128 DivideRoundUp(UInt128 value, UInt128 divisor)
|
|
||||||
{
|
|
||||||
if (divisor == UInt128.Zero)
|
|
||||||
{
|
|
||||||
return UInt128.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt128 result = value / divisor;
|
|
||||||
if (result * divisor == value)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result + UInt128.One;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WarmUpOrders(ulong exponent, ulong limit = 5_000_000UL)
|
public void WarmUpOrders(ulong exponent, ulong limit = 5_000_000UL)
|
||||||
{
|
{
|
||||||
bool cacheEnabled = useOrderCache;
|
bool cacheEnabled = useOrderCache;
|
||||||
@@ -218,30 +199,23 @@ public sealed class MersenneNumberTester(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsMersennePrime(ulong exponent) => IsMersennePrime(exponent, out _);
|
public bool IsMersennePrime(ulong exponent)
|
||||||
|
|
||||||
public bool IsMersennePrime(ulong exponent, out bool divisorsExhausted)
|
|
||||||
{
|
{
|
||||||
divisorsExhausted = false;
|
|
||||||
|
|
||||||
// Safe early rejections using known orders for tiny primes:
|
// Safe early rejections using known orders for tiny primes:
|
||||||
// 7 | M_p iff 3 | p. Avoid rejecting p == 3 where M_3 == 7 is prime.
|
// 7 | M_p iff 3 | p. Avoid rejecting p == 3 where M_3 == 7 is prime.
|
||||||
if ((exponent % 3UL) == 0UL && exponent != 3UL)
|
if ((exponent % 3UL) == 0UL && exponent != 3UL)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne())
|
if ((exponent & 3UL) == 1UL && exponent.SharesFactorWithExponentMinusOne())
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip even exponents: for even p > 2, M_p is composite; here p is large.
|
// Skip even exponents: for even p > 2, M_p is composite; here p is large.
|
||||||
if ((exponent & 1UL) == 0UL)
|
if ((exponent & 1UL) == 0UL)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,79 +228,21 @@ 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 (_maxK < maxK)
|
if ((UInt128)_maxK < maxK)
|
||||||
{
|
{
|
||||||
maxK = _maxK;
|
maxK = _maxK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_maxK == UInt128.Zero || _residueDivisorSetCount == UInt128.Zero)
|
|
||||||
{
|
|
||||||
return prePrime;
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt128 totalLimit = _maxK;
|
|
||||||
if (totalLimit > maxK)
|
|
||||||
{
|
|
||||||
totalLimit = maxK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalLimit == UInt128.Zero)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt128 requestedSets = _residueDivisorSetCount;
|
|
||||||
if (_useGpuScan)
|
if (_useGpuScan)
|
||||||
{
|
{
|
||||||
UInt128 maxUsefulSets = DivideRoundUp(totalLimit, (UInt128)GpuConstants.ScanBatchSize);
|
_residueGpuTester!.Scan(exponent, twoP, lastIsSeven, maxK, ref prePrime);
|
||||||
if (maxUsefulSets == UInt128.Zero)
|
|
||||||
{
|
|
||||||
maxUsefulSets = UInt128.One;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestedSets == UInt128.Zero || requestedSets > maxUsefulSets)
|
|
||||||
{
|
|
||||||
requestedSets = maxUsefulSets;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestedSets == UInt128.Zero)
|
|
||||||
{
|
|
||||||
requestedSets = UInt128.One;
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt128 perSetLimit = DivideRoundUp(totalLimit, requestedSets);
|
|
||||||
if (perSetLimit == UInt128.Zero)
|
|
||||||
{
|
|
||||||
perSetLimit = UInt128.One;
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt128 setsNeeded = DivideRoundUp(totalLimit, perSetLimit);
|
|
||||||
UInt128 effectiveSets = requestedSets;
|
|
||||||
if (setsNeeded < effectiveSets)
|
|
||||||
{
|
|
||||||
effectiveSets = setsNeeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool scanCompleted = false;
|
|
||||||
|
|
||||||
if (_useGpuScan)
|
|
||||||
{
|
|
||||||
_residueGpuTester!.Scan(exponent, twoP, lastIsSeven, perSetLimit, effectiveSets, totalLimit, ref prePrime, ref scanCompleted);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_residueCpuTester!.Scan(exponent, twoP, lastIsSeven, perSetLimit, effectiveSets, totalLimit, ref prePrime, ref scanCompleted);
|
_residueCpuTester!.Scan(exponent, twoP, lastIsSeven, maxK, ref prePrime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prePrime)
|
return prePrime;
|
||||||
{
|
|
||||||
divisorsExhausted = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
divisorsExhausted = scanCompleted;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_useIncremental)
|
if (!_useIncremental)
|
||||||
@@ -347,15 +263,12 @@ public sealed class MersenneNumberTester(
|
|||||||
|
|
||||||
if (!prePrime)
|
if (!prePrime)
|
||||||
{
|
{
|
||||||
divisorsExhausted = true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lucasPrime = _useGpuLucas
|
return _useGpuLucas
|
||||||
? _lucasLehmerGpuTester.IsPrime(exponent, runOnGpu: true)
|
? _lucasLehmerGpuTester.IsPrime(exponent, runOnGpu: true)
|
||||||
: _lucasLehmerCpuTester.IsPrime(exponent);
|
: _lucasLehmerCpuTester.IsPrime(exponent);
|
||||||
divisorsExhausted = true;
|
|
||||||
return lucasPrime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isPrime = true;
|
bool isPrime = true;
|
||||||
@@ -383,7 +296,6 @@ public sealed class MersenneNumberTester(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
divisorsExhausted = true;
|
|
||||||
return isPrime;
|
return isPrime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user