From 9e06a21718cc707f63f720cc66bbd4040510faa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 13 Oct 2025 01:35:50 +0200 Subject: [PATCH] Streamline GPU remainder batching tables --- ...MersenneNumberDivisorByDivisorCpuTester.cs | 26 ++--------- ...senneNumberDivisorCandidateGpuEvaluator.cs | 45 +++++++++---------- ...ersenneNumberDivisorRemainderGpuStepper.cs | 43 ++++++++++-------- 3 files changed, 49 insertions(+), 65 deletions(-) diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs index 53242d9..0dfcc24 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs @@ -419,15 +419,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv byte[] stepBuffer = ArrayPool.Shared.Rent(RemainderTableSize); Span remainderSpan = remainderBuffer.AsSpan(0, RemainderTableSize); Span stepSpan = stepBuffer.AsSpan(0, RemainderTableSize); - Span 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); diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorCandidateGpuEvaluator.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorCandidateGpuEvaluator.cs index 9a75acb..e4e0fe1 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorCandidateGpuEvaluator.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorCandidateGpuEvaluator.cs @@ -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, ArrayView> _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 remainders, + ReadOnlySpan steps, bool lastIsSeven, Span candidates, Span 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); diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorRemainderGpuStepper.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorRemainderGpuStepper.cs index e110be9..e293430 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorRemainderGpuStepper.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorRemainderGpuStepper.cs @@ -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, ArrayView, ArrayView, ArrayView> _kernel; + private readonly Action, ArrayView, ArrayView> _kernel; private MemoryBuffer1D _remainderBuffer; private MemoryBuffer1D _stepBuffer; - private MemoryBuffer1D _modulusBuffer; private MemoryBuffer1D _resultBuffer; private byte[] _hostRemainders; private byte[] _hostSteps; - private byte[] _hostModuli; private readonly int _length; private int _capacity; private bool _disposed; - internal MersenneNumberDivisorRemainderGpuStepper(ReadOnlySpan steps, ReadOnlySpan moduli) + internal MersenneNumberDivisorRemainderGpuStepper(ReadOnlySpan 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, ArrayView, ArrayView, ArrayView>(AdvanceRemaindersKernel); + _kernel = _accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView>(AdvanceRemaindersKernel); _length = steps.Length; _capacity = Math.Max(1, _length); _remainderBuffer = _accelerator.Allocate1D(_capacity); _stepBuffer = _accelerator.Allocate1D(_capacity); - _modulusBuffer = _accelerator.Allocate1D(_capacity); _resultBuffer = _accelerator.Allocate1D(_capacity); _hostRemainders = ArrayPool.Shared.Rent(_capacity); _hostSteps = ArrayPool.Shared.Rent(_capacity); - _hostModuli = ArrayPool.Shared.Rent(_capacity); steps.CopyTo(_hostSteps.AsSpan(0, _length)); - moduli.CopyTo(_hostModuli.AsSpan(0, _length)); ArrayView1D stepView = _stepBuffer.View.SubView(0, _length); stepView.CopyFromCPU(ref MemoryMarshal.GetReference(_hostSteps.AsSpan(0, _length)), _length); - - ArrayView1D 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.Shared.Return(_hostRemainders, clearArray: false); ArrayPool.Shared.Return(_hostSteps, clearArray: false); - ArrayPool.Shared.Return(_hostModuli, clearArray: false); _lease.Dispose(); } @@ -96,16 +88,15 @@ internal sealed class MersenneNumberDivisorRemainderGpuStepper : IDisposable remainderView.CopyFromCPU(ref MemoryMarshal.GetReference(hostRemainderSpan), _length); ArrayView1D stepView = _stepBuffer.View.SubView(0, _length); - ArrayView1D modulusView = _modulusBuffer.View.SubView(0, _length); ArrayView1D 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 remainders, ArrayView steps, ArrayView moduli, ArrayView results) + private static void AdvanceRemaindersKernel(Index1D index, int advanceCount, ArrayView remainders, ArrayView steps, ArrayView 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, + }; + } }