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