Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0466d98434 |
No files matched your search
@@ -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.
|
||||||
@@ -170,24 +154,20 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
// 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);
|
|
||||||
|
|
||||||
while (divisor.CompareTo(limit) <= 0)
|
while (divisor.CompareTo(limit) <= 0)
|
||||||
{
|
{
|
||||||
ulong candidate = divisor.Low;
|
ulong candidate = divisor.Low;
|
||||||
processedCount++;
|
|
||||||
|
|
||||||
ushort decimalMask = lastIsSeven ? DecimalMaskWhenLastIsSeven : DecimalMaskOtherwise;
|
bool admissible = ((decimalMask >> remainder10) & 1) != 0
|
||||||
bool admissible = ((decimalMask >> remainder10) & 1) != 0;
|
|
||||||
|
|
||||||
if (admissible
|
|
||||||
&& (remainder8 == 1 || remainder8 == 7)
|
&& (remainder8 == 1 || remainder8 == 7)
|
||||||
&& remainder3 != 0
|
&& remainder3 != 0
|
||||||
&& remainder5 != 0
|
&& remainder5 != 0
|
||||||
&& remainder7 != 0
|
&& remainder7 != 0
|
||||||
&& remainder11 != 0)
|
&& remainder11 != 0;
|
||||||
|
|
||||||
|
if (admissible)
|
||||||
{
|
{
|
||||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(candidate);
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(candidate);
|
||||||
ulong divisorCycle;
|
ulong divisorCycle;
|
||||||
@@ -234,7 +214,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
|
|
||||||
divisor.Add(step);
|
divisor.Add(step);
|
||||||
remainder10 = AddMod(remainder10, step10, (byte)10);
|
remainder10 = AddMod(remainder10, step10, (byte)10);
|
||||||
remainder8 = AddMod(remainder8, step8, (byte)8);
|
remainder8 = AddMod8(remainder8, step8);
|
||||||
remainder5 = AddMod(remainder5, step5, (byte)5);
|
remainder5 = AddMod(remainder5, step5, (byte)5);
|
||||||
remainder3 = AddMod(remainder3, step3, (byte)3);
|
remainder3 = AddMod(remainder3, step3, (byte)3);
|
||||||
remainder7 = AddMod(remainder7, step7, (byte)7);
|
remainder7 = AddMod(remainder7, step7, (byte)7);
|
||||||
@@ -251,9 +231,11 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
return residue == 1UL ? (byte)1 : (byte)0;
|
return residue == 1UL ? (byte)1 : (byte)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static byte AddMod(byte value, byte delta, byte modulus)
|
private static byte AddMod(byte value, byte delta, byte modulus)
|
||||||
{
|
{
|
||||||
int sum = value + delta;
|
int sum = value + delta;
|
||||||
|
|
||||||
if (sum >= modulus)
|
if (sum >= modulus)
|
||||||
{
|
{
|
||||||
sum -= modulus;
|
sum -= modulus;
|
||||||
@@ -262,24 +244,10 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
|
|||||||
return (byte)sum;
|
return (byte)sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateStatusUnsafe(ulong processedCount)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod8(byte value, byte delta)
|
||||||
{
|
{
|
||||||
if (processedCount == 0UL)
|
return (byte)((value + delta) & 7);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong interval = PerfectNumberConstants.ConsoleInterval;
|
|
||||||
if (interval == 0UL)
|
|
||||||
{
|
|
||||||
_lastStatusDivisor = 0UL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong total = _lastStatusDivisor + processedCount;
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime)
|
private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime)
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ internal static class DivisorGenerator
|
|||||||
private const ushort DecimalMaskWhenLastIsSeven = (1 << 3) | (1 << 7) | (1 << 9);
|
private const ushort DecimalMaskWhenLastIsSeven = (1 << 3) | (1 << 7) | (1 << 9);
|
||||||
private const ushort DecimalMaskOtherwise = (1 << 1) | (1 << 3) | (1 << 9);
|
private const ushort DecimalMaskOtherwise = (1 << 1) | (1 << 3) | (1 << 9);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ushort GetDecimalMask(LastDigit lastDigit)
|
||||||
|
{
|
||||||
|
return lastDigit == LastDigit.Seven ? DecimalMaskWhenLastIsSeven : DecimalMaskOtherwise;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsValidDivisor(
|
public static bool IsValidDivisor(
|
||||||
byte remainder10,
|
byte remainder10,
|
||||||
@@ -15,9 +21,8 @@ internal static class DivisorGenerator
|
|||||||
byte remainder5,
|
byte remainder5,
|
||||||
byte remainder7,
|
byte remainder7,
|
||||||
byte remainder11,
|
byte remainder11,
|
||||||
LastDigit lastDigit)
|
ushort decimalMask)
|
||||||
{
|
{
|
||||||
ushort decimalMask = lastDigit == LastDigit.Seven ? DecimalMaskWhenLastIsSeven : DecimalMaskOtherwise;
|
|
||||||
if (((decimalMask >> remainder10) & 1) == 0)
|
if (((decimalMask >> remainder10) & 1) == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -36,28 +36,30 @@ 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];
|
GpuUInt128 remainderValue = new(cycleRemainder);
|
||||||
ulong delta = exponent - previousExponent;
|
for (int exponentIndex = offset + 1; exponentIndex < endIndex; exponentIndex++)
|
||||||
previousExponent = exponent;
|
|
||||||
|
|
||||||
if (cycleLength != 0UL)
|
|
||||||
{
|
{
|
||||||
var remainderValue = new GpuUInt128(cycleRemainder);
|
ulong exponent = exponents[exponentIndex];
|
||||||
|
ulong delta = exponent - previousExponent;
|
||||||
|
previousExponent = exponent;
|
||||||
|
|
||||||
remainderValue.AddMod(delta, cycleLength);
|
remainderValue.AddMod(delta, cycleLength);
|
||||||
cycleRemainder = remainderValue.Low;
|
cycleRemainder = remainderValue.Low;
|
||||||
|
|
||||||
@@ -67,13 +69,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,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,7 +217,7 @@ 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);
|
byte step10 = (byte)(twoP128 % 10UL);
|
||||||
byte step8 = (byte)(twoP128 % 8UL);
|
byte step8 = (byte)(twoP128 % 8UL);
|
||||||
@@ -226,6 +234,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
byte remainder7 = (byte)((ulong)(currentDivisor128 % 7UL));
|
byte remainder7 = (byte)((ulong)(currentDivisor128 % 7UL));
|
||||||
byte remainder11 = (byte)((ulong)(currentDivisor128 % 11UL));
|
byte remainder11 = (byte)((ulong)(currentDivisor128 % 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> filteredStorage = filteredDivisors.AsSpan();
|
||||||
Span<ulong> divisorStorage = divisors.AsSpan();
|
Span<ulong> divisorStorage = divisors.AsSpan();
|
||||||
@@ -234,19 +243,32 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
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;
|
||||||
int chunkCount = Math.Min(chunkCapacity, batchCapacity);
|
Span<GpuDivisorPartialData> divisorDataSpan = divisorDataStorage;
|
||||||
ulong remainingK = maxK - currentK + 1UL;
|
Span<int> offsetSpan = offsetStorage;
|
||||||
if ((ulong)chunkCount > remainingK)
|
Span<int> countSpan = countStorage;
|
||||||
{
|
Span<ulong> cycleSpan = cycleStorage;
|
||||||
chunkCount = (int)remainingK;
|
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);
|
||||||
|
|
||||||
if (chunkCount <= 0)
|
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 = chunkCountBaseline;
|
||||||
|
if ((ulong)chunkCount > remainingCount)
|
||||||
{
|
{
|
||||||
processedAll = true;
|
chunkCount = (int)remainingCount;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int filteredCount = 0;
|
int filteredCount = 0;
|
||||||
@@ -263,14 +285,14 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
{
|
{
|
||||||
ulong candidate = (ulong)nextDivisor128;
|
ulong candidate = (ulong)nextDivisor128;
|
||||||
|
|
||||||
if (DivisorGenerator.IsValidDivisor(localRemainder10, localRemainder8, localRemainder3, localRemainder5, localRemainder7, localRemainder11, lastDigit))
|
if (DivisorGenerator.IsValidDivisor(localRemainder10, localRemainder8, localRemainder3, localRemainder5, localRemainder7, localRemainder11, decimalMask))
|
||||||
{
|
{
|
||||||
filteredStorage[filteredCount++] = candidate;
|
filteredStorage[filteredCount++] = candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextDivisor128 += twoP128;
|
nextDivisor128 += twoP128;
|
||||||
localRemainder10 = AddMod(localRemainder10, step10, 10);
|
localRemainder10 = AddMod(localRemainder10, step10, 10);
|
||||||
localRemainder8 = AddMod(localRemainder8, step8, 8);
|
localRemainder8 = AddMod8(localRemainder8, step8);
|
||||||
localRemainder5 = AddMod(localRemainder5, step5, 5);
|
localRemainder5 = AddMod(localRemainder5, step5, 5);
|
||||||
localRemainder3 = AddMod(localRemainder3, step3, 3);
|
localRemainder3 = AddMod(localRemainder3, step3, 3);
|
||||||
localRemainder7 = AddMod(localRemainder7, step7, 7);
|
localRemainder7 = AddMod(localRemainder7, step7, 7);
|
||||||
@@ -289,28 +311,17 @@ 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)
|
if (filteredCount == 0)
|
||||||
{
|
{
|
||||||
continue;
|
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 admissibleCount = 0;
|
||||||
int exponentIndex = 0;
|
|
||||||
for (int i = 0; i < filteredCount; i++)
|
for (int i = 0; i < filteredCount; i++)
|
||||||
{
|
{
|
||||||
ulong divisorValue = filteredDivisorsSpan[i];
|
ulong divisorValue = filteredStorage[i];
|
||||||
MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue);
|
MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue);
|
||||||
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
|
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData);
|
||||||
if (divisorCycle != prime)
|
if (divisorCycle != prime)
|
||||||
@@ -319,14 +330,13 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
divisorSpan[admissibleCount] = divisorValue;
|
divisorSpan[admissibleCount] = divisorValue;
|
||||||
exponentSpan[exponentIndex] = prime;
|
exponentSpan[admissibleCount] = prime;
|
||||||
divisorDataSpan[admissibleCount] = new GpuDivisorPartialData(divisorValue);
|
divisorDataSpan[admissibleCount] = new GpuDivisorPartialData(divisorValue);
|
||||||
offsetSpan[admissibleCount] = exponentIndex;
|
offsetSpan[admissibleCount] = admissibleCount;
|
||||||
countSpan[admissibleCount] = 1;
|
countSpan[admissibleCount] = 1;
|
||||||
cycleSpan[admissibleCount] = divisorCycle;
|
cycleSpan[admissibleCount] = divisorCycle;
|
||||||
|
|
||||||
admissibleCount++;
|
admissibleCount++;
|
||||||
exponentIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (admissibleCount == 0)
|
if (admissibleCount == 0)
|
||||||
@@ -334,24 +344,16 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
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 +364,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;
|
||||||
}
|
}
|
||||||
@@ -382,19 +385,23 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
|
|||||||
private static byte AddMod(byte value, byte delta, byte modulus)
|
private static byte AddMod(byte value, byte delta, byte modulus)
|
||||||
{
|
{
|
||||||
int result = value + delta;
|
int result = value + delta;
|
||||||
|
|
||||||
if (result >= modulus)
|
if (result >= modulus)
|
||||||
{
|
{
|
||||||
result -= modulus;
|
result -= modulus;
|
||||||
if (result >= modulus)
|
|
||||||
{
|
|
||||||
result -= modulus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte)result;
|
return (byte)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static byte AddMod8(byte value, byte delta)
|
||||||
|
{
|
||||||
|
return (byte)((value + delta) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -557,27 +564,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 +591,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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