Document unreachable guards in GPU by-divisor pipeline
This commit is contained in:
1 parent
65e010ded5
commit
bccb2f43c9
7 files changed
+178
-122
No files matched your search
@@ -87,11 +87,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
batchCapacity = _gpuBatchSize;
|
||||
}
|
||||
|
||||
if (allowedMax < 3UL)
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
return true;
|
||||
}
|
||||
// The GPU path always receives an allowed maximum well above the minimal divisor, so the legacy
|
||||
// guard stays commented out to document the invariant without branching.
|
||||
// if (allowedMax < 3UL)
|
||||
// {
|
||||
// divisorsExhausted = true;
|
||||
// return true;
|
||||
// }
|
||||
|
||||
bool composite;
|
||||
bool coveredRange;
|
||||
@@ -222,7 +224,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
ArrayView1D<ulong, Stride1D.Dense> exponentView;
|
||||
ArrayView1D<byte, Stride1D.Dense> hitsView;
|
||||
|
||||
cycleCapacity = Math.Max(1, cycleCapacity);
|
||||
// Batch resources expose a positive cycle capacity, so skip the redundant clamp here.
|
||||
// cycleCapacity = Math.Max(1, cycleCapacity);
|
||||
|
||||
int chunkCapacity = Math.Min(batchCapacity, cycleCapacity);
|
||||
int maxThreadsPerGroup = (int)accelerator.MaxNumThreadsPerGroup;
|
||||
@@ -231,20 +234,22 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
chunkCapacity = Math.Min(chunkCapacity, maxThreadsPerGroup);
|
||||
}
|
||||
|
||||
if (chunkCapacity <= 0)
|
||||
{
|
||||
chunkCapacity = cycleCapacity;
|
||||
}
|
||||
// cycleCapacity already bounds chunkCapacity above zero, so the fallback would never execute.
|
||||
// if (chunkCapacity <= 0)
|
||||
// {
|
||||
// chunkCapacity = cycleCapacity;
|
||||
// }
|
||||
|
||||
ulong[] filteredDivisors = ArrayPool<ulong>.Shared.Rent(chunkCapacity);
|
||||
ulong[] divisorGaps = ArrayPool<ulong>.Shared.Rent(chunkCapacity);
|
||||
|
||||
UInt128 twoP128 = (UInt128)prime << 1;
|
||||
if (twoP128 == UInt128.Zero)
|
||||
{
|
||||
coveredRange = true;
|
||||
return false;
|
||||
}
|
||||
// Primes above one yield a non-zero 2p value, so the legacy zero guard remains commented out.
|
||||
// if (twoP128 == UInt128.Zero)
|
||||
// {
|
||||
// coveredRange = true;
|
||||
// return false;
|
||||
// }
|
||||
|
||||
UInt128 allowedMax128 = allowedMax;
|
||||
UInt128 firstDivisor128 = twoP128 + UInt128.One;
|
||||
@@ -255,11 +260,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
}
|
||||
|
||||
UInt128 maxK128 = (allowedMax128 - UInt128.One) / twoP128;
|
||||
if (maxK128 == UInt128.Zero)
|
||||
{
|
||||
coveredRange = true;
|
||||
return false;
|
||||
}
|
||||
// Once the first divisor falls within the allowed range, at least one multiplier k is admissible,
|
||||
// so keep the defensive zero check commented out.
|
||||
// if (maxK128 == UInt128.Zero)
|
||||
// {
|
||||
// coveredRange = true;
|
||||
// return false;
|
||||
// }
|
||||
|
||||
ulong maxK = maxK128 > ulong.MaxValue ? ulong.MaxValue : (ulong)maxK128;
|
||||
ulong currentK = 1UL;
|
||||
@@ -301,10 +308,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
chunkCount = (int)remainingK;
|
||||
}
|
||||
|
||||
if (chunkCount <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// chunkCapacity and the remaining multiplier range are strictly positive here, so the zero
|
||||
// chunk fallback never triggers on production scans.
|
||||
// if (chunkCount <= 0)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
|
||||
Span<ulong> candidateSpan = cycleCandidates.AsSpan(0, chunkCount);
|
||||
Span<ulong> gapSpan = divisorGaps.AsSpan(0, chunkCount);
|
||||
@@ -465,10 +474,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
Action<Index1D, ArrayView<ulong>, ArrayView<byte>, byte> deltaKernel,
|
||||
Action<KernelConfig, Index1D, ArrayView<byte>, ArrayView<byte>, byte, byte> scanKernel)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// GPU batches always contain at least one divisor gap, so the empty-count guard remains commented
|
||||
// to eliminate redundant branching.
|
||||
// if (count <= 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
deltaKernel(count, gapView, deltaView, modulus);
|
||||
SharedMemoryConfig sharedMemoryConfig = SharedMemoryConfig.RequestDynamic<int>(count);
|
||||
@@ -491,11 +502,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
Span<byte> destination,
|
||||
Action<Index1D, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>, byte, ArrayView<byte>> maskKernel)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
destination.Clear();
|
||||
return;
|
||||
}
|
||||
// Candidate batches always provide at least one entry, so the zero-count guard stays commented out
|
||||
// to avoid clearing spans unnecessarily.
|
||||
// if (count <= 0)
|
||||
// {
|
||||
// destination.Clear();
|
||||
// return;
|
||||
// }
|
||||
|
||||
maskKernel(count, remainder10View, remainder8View, remainder5View, remainder3View, lastIsSevenFlag, maskView);
|
||||
maskView.CopyToCPU(ref MemoryMarshal.GetReference(destination), count);
|
||||
@@ -503,10 +516,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
|
||||
private static byte FetchLastRemainder(ArrayView1D<byte, Stride1D.Dense> remainderView, int count)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// Kernels always process at least one remainder in this path, so the empty-span guard remains
|
||||
// commented to document the invariant.
|
||||
// if (count <= 0)
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
byte value = 0;
|
||||
remainderView.SubView(count - 1, 1).CopyToCPU(ref value, 1);
|
||||
@@ -519,10 +534,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
||||
if (result >= modulus)
|
||||
{
|
||||
result -= modulus;
|
||||
if (result >= modulus)
|
||||
{
|
||||
result %= modulus;
|
||||
}
|
||||
// The step deltas remain below the modulus, so a single subtraction suffices on this path.
|
||||
// if (result >= modulus)
|
||||
// {
|
||||
// result %= modulus;
|
||||
// }
|
||||
}
|
||||
|
||||
return (byte)result;
|
||||
|
||||
@@ -68,10 +68,12 @@ internal sealed class MersenneNumberDivisorCandidateGpuEvaluator : IDisposable
|
||||
}
|
||||
|
||||
int count = candidates.Length;
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Candidate batches always carry at least one divisor on the by-divisor path, so the empty-count
|
||||
// guard remains commented out to eliminate the branch.
|
||||
// if (count == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
EnsureCapacity(count);
|
||||
|
||||
@@ -115,7 +117,12 @@ internal sealed class MersenneNumberDivisorCandidateGpuEvaluator : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
int newCapacity = _capacity == 0 ? 1 : _capacity;
|
||||
int newCapacity = _capacity;
|
||||
// The evaluator always starts with a positive capacity, so the zero-capacity fallback remains commented.
|
||||
// if (newCapacity == 0)
|
||||
// {
|
||||
// newCapacity = 1;
|
||||
// }
|
||||
while (newCapacity < required)
|
||||
{
|
||||
newCapacity <<= 1;
|
||||
@@ -188,10 +195,11 @@ internal sealed class MersenneNumberDivisorCandidateGpuEvaluator : IDisposable
|
||||
|
||||
private static int ComputeRemainder(byte remainder, byte step, int index, int modulus)
|
||||
{
|
||||
if (modulus == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// Modulus lookups only cover {10, 8, 5, 3, 7, 11}, so the zero-modulus guard stays commented out.
|
||||
// if (modulus == 0)
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
long value = remainder;
|
||||
value += (long)step * index;
|
||||
|
||||
@@ -54,10 +54,12 @@ internal sealed class MersenneNumberDivisorMontgomeryGpuBuilder : IDisposable
|
||||
}
|
||||
|
||||
int length = moduli.Length;
|
||||
if (length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Montgomery batches always include at least one modulus on the by-divisor path, so the empty-length
|
||||
// guard stays commented out to avoid branching.
|
||||
// if (length == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
EnsureCapacity(length);
|
||||
|
||||
@@ -83,7 +85,12 @@ internal sealed class MersenneNumberDivisorMontgomeryGpuBuilder : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
int newCapacity = _capacity == 0 ? 1 : _capacity;
|
||||
int newCapacity = _capacity;
|
||||
// The builder always starts with a positive capacity, so the zero-capacity fallback remains commented.
|
||||
// if (newCapacity == 0)
|
||||
// {
|
||||
// newCapacity = 1;
|
||||
// }
|
||||
while (newCapacity < required)
|
||||
{
|
||||
newCapacity <<= 1;
|
||||
|
||||
@@ -71,10 +71,12 @@ internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable
|
||||
throw new ObjectDisposedException(nameof(MersenneNumberDivisorRemainderGpuStepper));
|
||||
}
|
||||
|
||||
if (advanceCount <= 0 || remainders.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// GPU remainder batches always advance by a positive chunk count and operate on fixed-size tables, so
|
||||
// the defensive early-return remains commented out.
|
||||
// if (advanceCount <= 0 || remainders.Length == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (remainders.Length != _length)
|
||||
{
|
||||
@@ -99,17 +101,20 @@ internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable
|
||||
private static void AdvanceRemaindersKernel(Index1D index, int advanceCount, ArrayView<byte> remainders, ArrayView<byte> steps, ArrayView<byte> results)
|
||||
{
|
||||
int idx = index;
|
||||
if (idx >= remainders.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Kernel launches match the remainder table length, so the bounds guard stays commented to avoid redundant branching.
|
||||
// if (idx >= remainders.Length)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
int modulus = GetModulus(idx);
|
||||
if (modulus == 0)
|
||||
{
|
||||
results[idx] = remainders[idx];
|
||||
return;
|
||||
}
|
||||
// The modulus selector only emits positive bases (10, 8, 5, 3, 7, 11) for the configured indices, so the zero-modulus
|
||||
// fallback remains commented out.
|
||||
// if (modulus == 0)
|
||||
// {
|
||||
// results[idx] = remainders[idx];
|
||||
// return;
|
||||
// }
|
||||
|
||||
int remainder = remainders[idx] % modulus;
|
||||
int step = steps[idx] % modulus;
|
||||
|
||||
@@ -51,10 +51,12 @@ internal sealed class MersenneNumberDivisorResidueGpuEvaluator : IDisposable
|
||||
}
|
||||
|
||||
int length = exponents.Length;
|
||||
if (length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Residue batches always include at least one exponent when scheduled from the by-divisor scanner, so
|
||||
// the empty-length guard remains commented out to avoid branching.
|
||||
// if (length == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
EnsureCapacity(length);
|
||||
|
||||
@@ -79,10 +81,11 @@ internal sealed class MersenneNumberDivisorResidueGpuEvaluator : IDisposable
|
||||
}
|
||||
|
||||
int newCapacity = _capacity;
|
||||
if (newCapacity == 0)
|
||||
{
|
||||
newCapacity = 1;
|
||||
}
|
||||
// The evaluator always starts with a positive capacity, so the zero-capacity fallback stays commented.
|
||||
// if (newCapacity == 0)
|
||||
// {
|
||||
// newCapacity = 1;
|
||||
// }
|
||||
|
||||
while (newCapacity < required)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user