Add GPU options for by-divisor Montgomery data

This commit is contained in:
Marek Lemańczyk committed 2025-10-13 01:35:46 +02:00
1 parent ea29035c55
commit 483584b5d4
5 files changed
+449 -77

No files matched your search

@@ -19,7 +19,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
private ulong _configuredMaxPrime;
private ByDivisorDeltasDevice _deltasDevice = ByDivisorDeltasDevice.Cpu;
private ByDivisorMontgomeryDevice _montgomeryDevice = ByDivisorMontgomeryDevice.Cpu;
public int BatchSize
{
@@ -43,6 +43,18 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
}
}
public ByDivisorMontgomeryDevice MontgomeryDevice
{
get => _montgomeryDevice;
set
{
lock (_sync)
{
_montgomeryDevice = value;
}
}
}
public void ConfigureFromMaxPrime(ulong maxPrime)
{
lock (_sync)
@@ -402,26 +414,51 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
MontgomeryDivisorData cachedDivisorData = default;
bool hasCachedDivisorData = false;
byte step10 = (byte)(step % 10UL);
byte step8 = (byte)(step % 8UL);
byte step5 = (byte)(step % 5UL);
byte step3 = (byte)(step % 3UL);
byte step7 = (byte)(step % 7UL);
byte step11 = (byte)(step % 11UL);
const int RemainderTableSize = 6;
byte[] remainderBuffer = ArrayPool<byte>.Shared.Rent(RemainderTableSize);
byte[] stepBuffer = ArrayPool<byte>.Shared.Rent(RemainderTableSize);
Span<byte> remainderSpan = remainderBuffer.AsSpan(0, RemainderTableSize);
Span<byte> stepSpan = stepBuffer.AsSpan(0, RemainderTableSize);
Span<byte> modulusSpan = stackalloc byte[RemainderTableSize]
{
10,
8,
5,
3,
7,
11,
};
ulong startDivisor64 = divisor128.Low;
byte remainder10 = (byte)(startDivisor64 % 10UL);
byte remainder8 = (byte)(startDivisor64 % 8UL);
byte remainder5 = (byte)(startDivisor64 % 5UL);
byte remainder3 = (byte)(startDivisor64 % 3UL);
byte remainder7 = (byte)(startDivisor64 % 7UL);
byte remainder11 = (byte)(startDivisor64 % 11UL);
remainderSpan[0] = (byte)(startDivisor64 % 10UL);
remainderSpan[1] = (byte)(startDivisor64 % 8UL);
remainderSpan[2] = (byte)(startDivisor64 % 5UL);
remainderSpan[3] = (byte)(startDivisor64 % 3UL);
remainderSpan[4] = (byte)(startDivisor64 % 7UL);
remainderSpan[5] = (byte)(startDivisor64 % 11UL);
stepSpan[0] = (byte)(step % 10UL);
stepSpan[1] = (byte)(step % 8UL);
stepSpan[2] = (byte)(step % 5UL);
stepSpan[3] = (byte)(step % 3UL);
stepSpan[4] = (byte)(step % 7UL);
stepSpan[5] = (byte)(step % 11UL);
bool lastIsSeven = (prime & 3UL) == 3UL;
MersenneNumberDivisorRemainderGpuStepper remainderStepper = new(stepSpan, modulusSpan);
MersenneNumberDivisorMontgomeryGpuBuilder? montgomeryBuilder = _montgomeryDevice == ByDivisorMontgomeryDevice.Gpu
? new MersenneNumberDivisorMontgomeryGpuBuilder(batchCapacity)
: null;
MontgomeryDivisorData[]? montgomeryBuffer = montgomeryBuilder != null
? ArrayPool<MontgomeryDivisorData>.Shared.Rent(batchCapacity)
: null;
ulong remainingCandidates = ComputeRemainingCandidateCount(startDivisor64, limit64, step);
try
{
while (divisor128.CompareTo(limit128) <= 0)
while (remainingCandidates > 0UL && divisor128.CompareTo(limit128) <= 0)
{
if (divisor128.High != 0UL)
{
@@ -429,7 +466,9 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
}
ulong currentDivisor = divisor128.Low;
int chunkCount = ComputeChunkCount(currentDivisor, limit64, step, batchCapacity);
int chunkCount = remainingCandidates > (ulong)batchCapacity
? batchCapacity
: (int)remainingCandidates;
if (chunkCount <= 0)
{
break;
@@ -442,22 +481,29 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
currentDivisor,
step,
limit64,
remainder10,
remainder8,
remainder5,
remainder3,
remainder7,
remainder11,
step10,
step8,
step5,
step3,
step7,
step11,
remainderSpan[0],
remainderSpan[1],
remainderSpan[2],
remainderSpan[3],
remainderSpan[4],
remainderSpan[5],
stepSpan[0],
stepSpan[1],
stepSpan[2],
stepSpan[3],
stepSpan[4],
stepSpan[5],
lastIsSeven,
candidateSpan,
maskSpan);
Span<MontgomeryDivisorData> montgomerySpan = default;
if (montgomeryBuilder != null && montgomeryBuffer != null)
{
montgomerySpan = montgomeryBuffer.AsSpan(0, chunkCount);
montgomeryBuilder.Build(candidateSpan, montgomerySpan);
}
for (int i = 0; i < chunkCount; i++)
{
if (enforceLimit && (iterationCounter == 0UL || (iterationCounter & 63UL) == 0UL) && limitGuard.HasExpired())
@@ -477,16 +523,28 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
continue;
}
if (!hasCachedDivisorData || cachedModulus != candidate)
MontgomeryDivisorData divisorData;
if (!montgomerySpan.IsEmpty)
{
cachedDivisorData = MontgomeryDivisorData.FromModulus(candidate);
cachedModulus = candidate;
hasCachedDivisorData = true;
divisorData = montgomerySpan[i];
if (divisorData.Modulus != candidate)
{
divisorData = MontgomeryDivisorData.FromModulus(candidate);
}
}
else
{
if (!hasCachedDivisorData || cachedModulus != candidate)
{
cachedDivisorData = MontgomeryDivisorData.FromModulus(candidate);
cachedModulus = candidate;
hasCachedDivisorData = true;
}
divisorData = cachedDivisorData;
}
MontgomeryDivisorData divisorData = cachedDivisorData;
ulong divisorCycle;
if (candidate <= PerfectNumberConstants.MaxQForDivisorCycles)
{
divisorCycle = cycleCache.GetCycleLength(candidate);
@@ -516,67 +574,40 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
increment.Mul((ulong)chunkCount);
divisor128.Add(increment);
remainder10 = AdvanceRemainder(remainder10, step10, 10, chunkCount);
remainder8 = AdvanceRemainder(remainder8, step8, 8, chunkCount);
remainder5 = AdvanceRemainder(remainder5, step5, 5, chunkCount);
remainder3 = AdvanceRemainder(remainder3, step3, 3, chunkCount);
remainder7 = AdvanceRemainder(remainder7, step7, 7, chunkCount);
remainder11 = AdvanceRemainder(remainder11, step11, 11, chunkCount);
remainderStepper.Advance(chunkCount, remainderSpan);
remainingCandidates -= (ulong)chunkCount;
}
}
finally
{
montgomeryBuilder?.Dispose();
if (montgomeryBuffer != null)
{
ArrayPool<MontgomeryDivisorData>.Shared.Return(montgomeryBuffer, clearArray: false);
}
remainderStepper.Dispose();
ArrayPool<byte>.Shared.Return(remainderBuffer, clearArray: false);
ArrayPool<byte>.Shared.Return(stepBuffer, clearArray: false);
ArrayPool<ulong>.Shared.Return(candidateBuffer, clearArray: false);
ArrayPool<byte>.Shared.Return(maskBuffer, clearArray: false);
candidateEvaluator.Dispose();
}
processedAll = divisor128.CompareTo(limit128) > 0;
processedAll = remainingCandidates == 0UL && divisor128.CompareTo(limit128) > 0;
return false;
}
private static int ComputeChunkCount(ulong currentDivisor, ulong limit, ulong step, int maxCount)
private static ulong ComputeRemainingCandidateCount(ulong currentDivisor, ulong limit, ulong step)
{
if (maxCount <= 0 || step == 0UL || currentDivisor > limit)
if (step == 0UL || currentDivisor > limit)
{
return 0;
return 0UL;
}
ulong distance = limit - currentDivisor;
ulong stepsAvailable = step == 0UL ? 0UL : distance / step;
ulong total = stepsAvailable + 1UL;
if (total > (ulong)maxCount)
{
total = (ulong)maxCount;
}
if (total > (ulong)int.MaxValue)
{
return int.MaxValue;
}
return (int)total;
}
private static byte AdvanceRemainder(byte remainder, byte step, byte modulus, int count)
{
if (modulus == 0 || count <= 0)
{
return remainder;
}
int modulusValue = modulus;
int remainderValue = remainder % modulusValue;
int stepValue = step % modulusValue;
long delta = (long)stepValue * count;
int adjusted = (int)((remainderValue + delta) % modulusValue);
if (adjusted < 0)
{
adjusted += modulusValue;
}
return (byte)adjusted;
ulong stepsAvailable = distance / step;
return stepsAvailable + 1UL;
}
private static byte CheckDivisor(ulong prime, ulong divisorCycle, in MontgomeryDivisorData divisorData)