Streamline GPU remainder batching tables

This commit is contained in:
Marek Lemańczyk committed 2025-10-13 01:35:50 +02:00
1 parent 483584b5d4
commit 9e06a21718
3 files changed
+49 -65

No files matched your search

@@ -419,15 +419,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
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,
};
// The remainder and step tables follow the fixed modulus order: 10, 8, 5, 3, 7, 11.
ulong startDivisor64 = divisor128.Low;
remainderSpan[0] = (byte)(startDivisor64 % 10UL);
@@ -446,7 +438,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
bool lastIsSeven = (prime & 3UL) == 3UL;
MersenneNumberDivisorRemainderGpuStepper remainderStepper = new(stepSpan, modulusSpan);
MersenneNumberDivisorRemainderGpuStepper remainderStepper = new(stepSpan);
MersenneNumberDivisorMontgomeryGpuBuilder? montgomeryBuilder = _montgomeryDevice == ByDivisorMontgomeryDevice.Gpu
? new MersenneNumberDivisorMontgomeryGpuBuilder(batchCapacity)
: null;
@@ -481,18 +473,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
currentDivisor,
step,
limit64,
remainderSpan[0],
remainderSpan[1],
remainderSpan[2],
remainderSpan[3],
remainderSpan[4],
remainderSpan[5],
stepSpan[0],
stepSpan[1],
stepSpan[2],
stepSpan[3],
stepSpan[4],
stepSpan[5],
remainderSpan,
stepSpan,
lastIsSeven,
candidateSpan,
maskSpan);
@@ -8,6 +8,8 @@ namespace PerfectNumbers.Core.Gpu;
internal sealed class MersenneNumberDivisorCandidateGpuEvaluator : IDisposable
{
private const int RemainderCount = 6;
private readonly GpuContextPool.GpuContextLease _lease;
private readonly Accelerator _accelerator;
private readonly Action<Index1D, CandidateFilterArgs, ArrayView<ulong>, ArrayView<byte>> _kernel;
@@ -49,18 +51,8 @@ internal sealed class MersenneNumberDivisorCandidateGpuEvaluator : IDisposable
ulong startDivisor,
ulong step,
ulong limit,
byte remainder10,
byte remainder8,
byte remainder5,
byte remainder3,
byte remainder7,
byte remainder11,
byte step10,
byte step8,
byte step5,
byte step3,
byte step7,
byte step11,
ReadOnlySpan<byte> remainders,
ReadOnlySpan<byte> steps,
bool lastIsSeven,
Span<ulong> candidates,
Span<byte> mask)
@@ -70,6 +62,11 @@ internal sealed class MersenneNumberDivisorCandidateGpuEvaluator : IDisposable
throw new ObjectDisposedException(nameof(MersenneNumberDivisorCandidateGpuEvaluator));
}
if (remainders.Length != RemainderCount || steps.Length != RemainderCount)
{
throw new ArgumentException("Remainder and step tables must contain exactly six entries.");
}
int count = candidates.Length;
if (count == 0)
{
@@ -88,18 +85,18 @@ internal sealed class MersenneNumberDivisorCandidateGpuEvaluator : IDisposable
startDivisor,
step,
limit,
remainder10,
remainder8,
remainder5,
remainder3,
remainder7,
remainder11,
step10,
step8,
step5,
step3,
step7,
step11,
remainders[0],
remainders[1],
remainders[2],
remainders[3],
remainders[4],
remainders[5],
steps[0],
steps[1],
steps[2],
steps[3],
steps[4],
steps[5],
lastIsSeven ? (byte)1 : (byte)0);
_kernel(count, args, candidateView, maskView);
@@ -8,50 +8,44 @@ namespace PerfectNumbers.Core.Gpu;
internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable
{
private const int RemainderCount = 6;
private readonly GpuContextPool.GpuContextLease _lease;
private readonly Accelerator _accelerator;
private readonly Action<Index1D, int, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>> _kernel;
private readonly Action<Index1D, int, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>> _kernel;
private MemoryBuffer1D<byte, Stride1D.Dense> _remainderBuffer;
private MemoryBuffer1D<byte, Stride1D.Dense> _stepBuffer;
private MemoryBuffer1D<byte, Stride1D.Dense> _modulusBuffer;
private MemoryBuffer1D<byte, Stride1D.Dense> _resultBuffer;
private byte[] _hostRemainders;
private byte[] _hostSteps;
private byte[] _hostModuli;
private readonly int _length;
private int _capacity;
private bool _disposed;
internal MersenneNumberDivisorRemainderGpuStepper(ReadOnlySpan<byte> steps, ReadOnlySpan<byte> moduli)
internal MersenneNumberDivisorRemainderGpuStepper(ReadOnlySpan<byte> steps)
{
if (steps.Length != moduli.Length)
if (steps.Length != RemainderCount)
{
throw new ArgumentException("Steps and moduli must have the same length.");
throw new ArgumentException("Step table must contain exactly six entries.");
}
_lease = GpuContextPool.RentPreferred(preferCpu: false);
_accelerator = _lease.Accelerator;
_kernel = _accelerator.LoadAutoGroupedStreamKernel<Index1D, int, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>>(AdvanceRemaindersKernel);
_kernel = _accelerator.LoadAutoGroupedStreamKernel<Index1D, int, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>>(AdvanceRemaindersKernel);
_length = steps.Length;
_capacity = Math.Max(1, _length);
_remainderBuffer = _accelerator.Allocate1D<byte>(_capacity);
_stepBuffer = _accelerator.Allocate1D<byte>(_capacity);
_modulusBuffer = _accelerator.Allocate1D<byte>(_capacity);
_resultBuffer = _accelerator.Allocate1D<byte>(_capacity);
_hostRemainders = ArrayPool<byte>.Shared.Rent(_capacity);
_hostSteps = ArrayPool<byte>.Shared.Rent(_capacity);
_hostModuli = ArrayPool<byte>.Shared.Rent(_capacity);
steps.CopyTo(_hostSteps.AsSpan(0, _length));
moduli.CopyTo(_hostModuli.AsSpan(0, _length));
ArrayView1D<byte, Stride1D.Dense> stepView = _stepBuffer.View.SubView(0, _length);
stepView.CopyFromCPU(ref MemoryMarshal.GetReference(_hostSteps.AsSpan(0, _length)), _length);
ArrayView1D<byte, Stride1D.Dense> modulusView = _modulusBuffer.View.SubView(0, _length);
modulusView.CopyFromCPU(ref MemoryMarshal.GetReference(_hostModuli.AsSpan(0, _length)), _length);
}
public void Dispose()
@@ -64,11 +58,9 @@ internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable
_disposed = true;
_remainderBuffer.Dispose();
_stepBuffer.Dispose();
_modulusBuffer.Dispose();
_resultBuffer.Dispose();
ArrayPool<byte>.Shared.Return(_hostRemainders, clearArray: false);
ArrayPool<byte>.Shared.Return(_hostSteps, clearArray: false);
ArrayPool<byte>.Shared.Return(_hostModuli, clearArray: false);
_lease.Dispose();
}
@@ -96,16 +88,15 @@ internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable
remainderView.CopyFromCPU(ref MemoryMarshal.GetReference(hostRemainderSpan), _length);
ArrayView1D<byte, Stride1D.Dense> stepView = _stepBuffer.View.SubView(0, _length);
ArrayView1D<byte, Stride1D.Dense> modulusView = _modulusBuffer.View.SubView(0, _length);
ArrayView1D<byte, Stride1D.Dense> resultView = _resultBuffer.View.SubView(0, _length);
_kernel(_length, advanceCount, remainderView, stepView, modulusView, resultView);
_kernel(_length, advanceCount, remainderView, stepView, resultView);
resultView.CopyToCPU(ref MemoryMarshal.GetReference(hostRemainderSpan), _length);
hostRemainderSpan.CopyTo(remainders);
}
private static void AdvanceRemaindersKernel(Index1D index, int advanceCount, ArrayView<byte> remainders, ArrayView<byte> steps, ArrayView<byte> moduli, ArrayView<byte> results)
private static void AdvanceRemaindersKernel(Index1D index, int advanceCount, ArrayView<byte> remainders, ArrayView<byte> steps, ArrayView<byte> results)
{
int idx = index;
if (idx >= remainders.Length)
@@ -113,7 +104,7 @@ internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable
return;
}
int modulus = moduli[idx];
int modulus = GetModulus(idx);
if (modulus == 0)
{
results[idx] = remainders[idx];
@@ -132,4 +123,18 @@ internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable
results[idx] = (byte)adjusted;
}
private static int GetModulus(int index)
{
return index switch
{
0 => 10,
1 => 8,
2 => 5,
3 => 3,
4 => 7,
5 => 11,
_ => 0,
};
}
}