Divisor-Cycle Cache Rework
This commit is contained in:
1 parent
f9e548279c
commit
ebe48d5f2f
11 files changed
+1836
-2660
No files matched your search
@@ -12,14 +12,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
private ulong _divisorLimit;
|
||||
private ulong _lastStatusDivisor;
|
||||
private bool _isConfigured;
|
||||
private bool _useDivisorCycles;
|
||||
private int _batchSize = 1_024;
|
||||
|
||||
public bool UseDivisorCycles
|
||||
{
|
||||
get => _useDivisorCycles;
|
||||
set => _useDivisorCycles = value; // TODO: Delete the toggle once divisor cycle data is always consulted so CPU scans never fall back to the slower pure-Montgomery path.
|
||||
}
|
||||
|
||||
public int BatchSize
|
||||
{
|
||||
@@ -69,8 +63,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
||||
{
|
||||
ulong allowedMax;
|
||||
bool useCycles;
|
||||
|
||||
lock (_sync)
|
||||
{
|
||||
if (!_isConfigured)
|
||||
@@ -79,7 +71,6 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
}
|
||||
|
||||
allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit);
|
||||
useCycles = _useDivisorCycles;
|
||||
}
|
||||
|
||||
if (allowedMax < 3UL)
|
||||
@@ -93,12 +84,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
bool processedAll = false;
|
||||
|
||||
bool composite = CheckDivisors(
|
||||
prime,
|
||||
allowedMax,
|
||||
useCycles,
|
||||
out lastProcessed,
|
||||
out processedAll,
|
||||
out processedCount);
|
||||
prime,
|
||||
allowedMax,
|
||||
out lastProcessed,
|
||||
out processedAll,
|
||||
out processedCount);
|
||||
|
||||
if (processedCount > 0UL)
|
||||
{
|
||||
@@ -168,12 +158,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
}
|
||||
|
||||
private bool CheckDivisors(
|
||||
ulong prime,
|
||||
ulong allowedMax,
|
||||
bool useCycles,
|
||||
out ulong lastProcessed,
|
||||
out bool processedAll,
|
||||
out ulong processedCount)
|
||||
ulong prime,
|
||||
ulong allowedMax,
|
||||
out ulong lastProcessed,
|
||||
out bool processedAll,
|
||||
out ulong processedCount)
|
||||
{
|
||||
lastProcessed = 0UL;
|
||||
processedCount = 0UL;
|
||||
@@ -194,35 +183,15 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
// TODO: Hoist MontgomeryDivisorData acquisition into the divisor-cycle cache so we reuse the
|
||||
// staged ProcessEightBitWindows-ready metadata instead of reloading the slower Montgomery
|
||||
// structs for every divisor.
|
||||
byte hit;
|
||||
ulong divisorCycle = 0UL;
|
||||
DivisorCycleCache.CycleBlock? cycleBlock = null;
|
||||
if (useCycles)
|
||||
ulong divisorCycle = DivisorCycleCache.Shared.GetCycleLength(divisor);
|
||||
if (divisorCycle != 0UL)
|
||||
{
|
||||
cycleBlock = DivisorCycleCache.Shared.Acquire(divisor);
|
||||
// TODO: Swap this block lease for the direct single-cycle lookup once the cache exposes it
|
||||
// so the CPU scanner mirrors the benchmarked single-block policy without retaining mutable
|
||||
// blocks beyond the startup snapshot.
|
||||
divisorCycle = cycleBlock.GetCycle(divisor);
|
||||
// TODO: When divisorCycle is zero (cache miss), compute only that single cycle on the device
|
||||
// selected by the current settings, skip inserting the result into the shared cache, and keep
|
||||
// operating with the single on-disk block without requesting additional blocks.
|
||||
hit = CheckDivisor(prime, divisorCycle != 0UL, divisorCycle, divisorData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Remove this no-cycle branch and require on-demand cycle computation (without
|
||||
// mutating the cache) so CPU scans never fall back to the slower Montgomery-only path
|
||||
// identified in the benchmarks.
|
||||
hit = CheckDivisor(prime, false, 0UL, divisorData);
|
||||
}
|
||||
|
||||
cycleBlock?.Dispose();
|
||||
|
||||
if (hit != 0)
|
||||
{
|
||||
processedAll = true;
|
||||
return true;
|
||||
byte hit = CheckDivisor(prime, divisorCycle, divisorData);
|
||||
if (hit != 0)
|
||||
{
|
||||
processedAll = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (divisor >= allowedMax - 1UL)
|
||||
@@ -249,22 +218,18 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
return false;
|
||||
}
|
||||
|
||||
private static byte CheckDivisor(ulong prime, bool useCycles, ulong divisorCycle, in MontgomeryDivisorData divisorData)
|
||||
private static byte CheckDivisor(ulong prime, ulong divisorCycle, in MontgomeryDivisorData divisorData)
|
||||
{
|
||||
ulong modulus = divisorData.Modulus;
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL || divisorCycle == 0UL)
|
||||
{
|
||||
ulong modulus = divisorData.Modulus;
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong residue = useCycles && divisorCycle != 0UL
|
||||
? prime.Pow2MontgomeryModWithCycle(divisorCycle, divisorData)
|
||||
: prime.Pow2MontgomeryMod(divisorData); // TODO: Switch to the upcoming ProcessEightBitWindows helper once the
|
||||
// scalar Pow2Minus1Mod adopts it so CPU scans match the GPU speedups.
|
||||
|
||||
return residue == 1UL ? (byte)1 : (byte)0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong residue = prime.Pow2MontgomeryModWithCycle(divisorCycle, divisorData);
|
||||
return residue == 1UL ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
private void UpdateStatusUnsafe(ulong lastProcessed, ulong processedCount)
|
||||
{
|
||||
if (processedCount == 0UL)
|
||||
@@ -343,23 +308,22 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
return;
|
||||
}
|
||||
|
||||
ulong modulus = divisor;
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
if (divisor <= 1UL || (divisor & 1UL) == 0UL)
|
||||
{
|
||||
hits.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
bool useDivisorCycles = _owner._useDivisorCycles;
|
||||
if (useDivisorCycles && divisorCycle == 0UL)
|
||||
if (divisorCycle == 0UL)
|
||||
{
|
||||
// TODO: Replace this guard with an on-demand CPU/GPU cycle computation path that skips
|
||||
// cache insertion and continues operating with the single cached block instead of
|
||||
// requesting additional cycle batches when a lookup misses.
|
||||
throw new InvalidOperationException($"Missing divisor cycle for divisor {divisor}.");
|
||||
divisorCycle = DivisorCycleCache.Shared.GetCycleLength(divisor);
|
||||
if (divisorCycle == 0UL)
|
||||
{
|
||||
hits.Clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool cycleEnabled = useDivisorCycles;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataCache.Get(divisor);
|
||||
|
||||
var exponentStepper = new ExponentRemainderStepper(divisorData);
|
||||
@@ -369,39 +333,25 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
return;
|
||||
}
|
||||
|
||||
if (cycleEnabled)
|
||||
var stepper = new CycleRemainderStepper(divisorCycle);
|
||||
|
||||
ulong remainder = stepper.Initialize(primes[0]);
|
||||
hits[0] = remainder == 0UL
|
||||
? (exponentStepper.ComputeNextIsUnity(primes[0]) ? (byte)1 : (byte)0)
|
||||
: (byte)0;
|
||||
|
||||
for (int i = 1; i < length; i++)
|
||||
{
|
||||
var stepper = new CycleRemainderStepper(divisorCycle);
|
||||
|
||||
ulong remainder = stepper.Initialize(primes[0]);
|
||||
hits[0] = remainder == 0UL
|
||||
? (exponentStepper.ComputeNextIsUnity(primes[0]) ? (byte)1 : (byte)0)
|
||||
: (byte)0;
|
||||
|
||||
for (int i = 1; i < length; i++)
|
||||
remainder = stepper.ComputeNext(primes[i]);
|
||||
if (remainder != 0UL)
|
||||
{
|
||||
remainder = stepper.ComputeNext(primes[i]);
|
||||
if (remainder != 0UL)
|
||||
{
|
||||
hits[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
hits[i] = exponentStepper.ComputeNextIsUnity(primes[i]) ? (byte)1 : (byte)0;
|
||||
hits[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Remove this no-cycle fallback once divisor cycles are mandatory; the per-prime pow2 ladder here
|
||||
// keeps the slower Montgomery stepping alive even though the benchmarks showed the cached cycle path is far
|
||||
// faster for large divisor sets.
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
hits[i] = exponentStepper.ComputeNextIsUnity(primes[i]) ? (byte)1 : (byte)0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -415,3 +365,4 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user