From d95195e0c6fe0aebe8554ade8d87a6768052aff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Thu, 25 Sep 2025 05:56:56 +0200 Subject: [PATCH] Move cycle filtering and residue prep to CPU --- ...MersenneNumberDivisorByDivisorGpuTester.cs | 191 +++++++----------- 1 file changed, 73 insertions(+), 118 deletions(-) diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index 2fbec3e..790af2f 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -26,15 +26,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private readonly ConcurrentDictionary, ArrayView>> _kernelCache = new(); private readonly ConcurrentDictionary, ArrayView>> _kernelExponentCache = new(); private readonly ConcurrentDictionary> _resourcePools = new(AcceleratorReferenceComparer.Instance); - private readonly ConcurrentDictionary, ArrayView>> _cycleKernelCache = new(); private readonly ConcurrentBag _sessionPool = new(); private Action, ArrayView> GetKernel(Accelerator accelerator) => _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView>(CheckKernel)); private Action, ArrayView> GetKernelByPrimeExponent(Accelerator accelerator) => _kernelExponentCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView>(ComputePrimeExponentKernel)); - private Action, ArrayView> GetCycleKernel(Accelerator accelerator) => - _cycleKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView>(CheckPrimeCycleKernel)); public int GpuBatchSize { @@ -333,34 +330,26 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private readonly MersenneNumberDivisorByDivisorGpuTester _owner; private readonly GpuContextPool.GpuContextLease _lease; private readonly Accelerator _accelerator; - private readonly Action, ArrayView> _kernel; - private readonly Action, ArrayView> _cycleKernel; - private MemoryBuffer1D _exponentsBuffer; - private MemoryBuffer1D _resultsBuffer; - private MemoryBuffer1D _cycleMaskBuffer; - private ulong[] _hostBuffer; - private ulong[] _primeBuffer; - private int[] _positionBuffer; - private byte[] _cycleMaskHost; - private int _capacity; - private bool _disposed; + private readonly Action, ArrayView> _kernel; + private MemoryBuffer1D _exponentsBuffer; + private MemoryBuffer1D _resultsBuffer; + private ulong[] _hostBuffer; + private int[] _positionBuffer; + private int _capacity; + private bool _disposed; - internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner) - { - _owner = owner; - _lease = GpuContextPool.RentPreferred(preferCpu: false); - _accelerator = _lease.Accelerator; - _kernel = owner.GetKernelByPrimeExponent(_accelerator); - _cycleKernel = owner.GetCycleKernel(_accelerator); - _capacity = Math.Max(1, owner._gpuBatchSize); - _exponentsBuffer = _accelerator.Allocate1D(_capacity); - _resultsBuffer = _accelerator.Allocate1D(_capacity); - _cycleMaskBuffer = _accelerator.Allocate1D(_capacity); - _hostBuffer = ArrayPool.Shared.Rent(_capacity); - _primeBuffer = ArrayPool.Shared.Rent(_capacity); - _positionBuffer = ArrayPool.Shared.Rent(_capacity); - _cycleMaskHost = ArrayPool.Shared.Rent(_capacity); - } + internal DivisorScanSession(MersenneNumberDivisorByDivisorGpuTester owner) + { + _owner = owner; + _lease = GpuContextPool.RentPreferred(preferCpu: false); + _accelerator = _lease.Accelerator; + _kernel = owner.GetKernelByPrimeExponent(_accelerator); + _capacity = Math.Max(1, owner._gpuBatchSize); + _exponentsBuffer = _accelerator.Allocate1D(_capacity); + _resultsBuffer = _accelerator.Allocate1D(_capacity); + _hostBuffer = ArrayPool.Shared.Rent(_capacity); + _positionBuffer = ArrayPool.Shared.Rent(_capacity); + } internal void Reset() { @@ -376,21 +365,15 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv _exponentsBuffer.Dispose(); _resultsBuffer.Dispose(); - _cycleMaskBuffer.Dispose(); - ArrayPool.Shared.Return(_hostBuffer, clearArray: false); - ArrayPool.Shared.Return(_primeBuffer, clearArray: false); - ArrayPool.Shared.Return(_positionBuffer, clearArray: false); - ArrayPool.Shared.Return(_cycleMaskHost, clearArray: false); + ArrayPool.Shared.Return(_hostBuffer, clearArray: false); + ArrayPool.Shared.Return(_positionBuffer, clearArray: false); - _capacity = requiredCapacity; - _exponentsBuffer = _accelerator.Allocate1D(_capacity); - _resultsBuffer = _accelerator.Allocate1D(_capacity); - _cycleMaskBuffer = _accelerator.Allocate1D(_capacity); - _hostBuffer = ArrayPool.Shared.Rent(_capacity); - _primeBuffer = ArrayPool.Shared.Rent(_capacity); - _positionBuffer = ArrayPool.Shared.Rent(_capacity); - _cycleMaskHost = ArrayPool.Shared.Rent(_capacity); - } + _capacity = requiredCapacity; + _exponentsBuffer = _accelerator.Allocate1D(_capacity); + _resultsBuffer = _accelerator.Allocate1D(_capacity); + _hostBuffer = ArrayPool.Shared.Rent(_capacity); + _positionBuffer = ArrayPool.Shared.Rent(_capacity); + } public void CheckDivisor(ulong divisor, ulong divisorCycle, ReadOnlySpan primes, Span hits) { @@ -414,73 +397,56 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv Accelerator accelerator = _accelerator; - Action, ArrayView> kernel = _kernel; - Action, ArrayView> cycleKernel = _cycleKernel; - ArrayView1D exponentsView = _exponentsBuffer.View; - ArrayView1D resultsView = _resultsBuffer.View; - ArrayView1D cycleMaskViewFull = _cycleMaskBuffer.View; + Action, ArrayView> kernel = _kernel; + ArrayView1D exponentsView = _exponentsBuffer.View; + ArrayView1D resultsView = _resultsBuffer.View; - Span hostSpan = _hostBuffer.AsSpan(); - Span primeSpan = _primeBuffer.AsSpan(); - Span positionSpan = _positionBuffer.AsSpan(); + Span hostSpan = _hostBuffer.AsSpan(); + Span positionSpan = _positionBuffer.AsSpan(); - int offset = 0; - bool cycleEnabled = owner._useDivisorCycles; + int offset = 0; + bool cycleEnabled = owner._useDivisorCycles; while (offset < primesLength) { int batchSize = Math.Min(gpuBatchSize, primesLength - offset); ReadOnlySpan primesSlice = primes.Slice(offset, batchSize); - Span hitsSlice = hits.Slice(offset, batchSize); + Span hitsSlice = hits.Slice(offset, batchSize); - int computeCount = 0; + int computeCount = 0; - if (cycleEnabled) - { - Span cycleMaskSpan = _cycleMaskHost.AsSpan(0, batchSize); - ArrayView1D primeInputView = _resultsBuffer.View.SubView(0, batchSize); - primeInputView.CopyFromCPU(ref MemoryMarshal.GetReference(primesSlice), batchSize); - ArrayView1D cycleMaskView = cycleMaskViewFull.SubView(0, batchSize); - // TODO: Move this back to CPU side and make sure that we only pass primes here which passed the cycle test, when cycles are in use. - cycleKernel(batchSize, divisorCycle, primeInputView, cycleMaskView); - accelerator.Synchronize(); - cycleMaskView.CopyToCPU(ref MemoryMarshal.GetReference(cycleMaskSpan), batchSize); + if (cycleEnabled && divisorCycle != 0UL) + { + for (int i = 0; i < batchSize; i++) + { + ulong residue = primesSlice[i] % divisorCycle; + if (residue != 0UL) + { + hitsSlice[i] = 0; + continue; + } - for (int i = 0; i < batchSize; i++) - { - if (cycleMaskSpan[i] == 0) - { - hitsSlice[i] = 0; - continue; - } + hostSpan[computeCount] = residue; + positionSpan[computeCount] = i; + computeCount++; + } + } + else + { + for (int i = 0; i < batchSize; i++) + { + hostSpan[computeCount] = primesSlice[i]; + positionSpan[computeCount] = i; + computeCount++; + } + } - primeSpan[computeCount] = primesSlice[i]; - positionSpan[computeCount] = i; - computeCount++; - } - } - else - { - for (int i = 0; i < batchSize; i++) - { - primeSpan[computeCount] = primesSlice[i]; - positionSpan[computeCount] = i; - computeCount++; - } - } + if (computeCount > 0) + { + Span exponentSlice = hostSpan[..computeCount]; - if (computeCount > 0) - { - Span exponentSlice = hostSpan[..computeCount]; - for (int i = 0; i < computeCount; i++) - { - ulong prime = primeSpan[i]; - // TODO: Let's calculate prime % divisorCycle on CPU side, too. When we check the cycle, we calculate the residue. Let's put them into an array and re-use here. - exponentSlice[i] = cycleEnabled ? prime % divisorCycle : prime; - } - - ArrayView1D exponentView = exponentsView.SubView(0, computeCount); - exponentView.CopyFromCPU(ref MemoryMarshal.GetReference(exponentSlice), computeCount); - ArrayView1D resultView = resultsView.SubView(0, computeCount); + ArrayView1D exponentView = exponentsView.SubView(0, computeCount); + exponentView.CopyFromCPU(ref MemoryMarshal.GetReference(exponentSlice), computeCount); + ArrayView1D resultView = resultsView.SubView(0, computeCount); kernel(computeCount, divisorData, exponentView, resultView); accelerator.Synchronize(); resultView.CopyToCPU(ref MemoryMarshal.GetReference(exponentSlice), computeCount); @@ -491,11 +457,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv ulong residue = exponentSlice[i]; hitsSlice[position] = residue == 1UL ? (byte)1 : (byte)0; } - } + } - offset += batchSize; - } - } + offset += batchSize; + } + } public void Dispose() { @@ -516,20 +482,9 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv _sessionPool.Add(session); } - private static void CheckPrimeCycleKernel(Index1D index, ulong cycle, ArrayView primes, ArrayView mask) - { - if (cycle == 0UL) - { - mask[index] = 1; - return; - } - - mask[index] = primes[index] % cycle == 0UL ? (byte)1 : (byte)0; - } - - private static void ComputePrimeExponentKernel(Index1D index, MontgomeryDivisorData divisor, ArrayView exponents, ArrayView results) - { - ulong modulus = divisor.Modulus; + private static void ComputePrimeExponentKernel(Index1D index, MontgomeryDivisorData divisor, ArrayView exponents, ArrayView results) + { + ulong modulus = divisor.Modulus; if (modulus <= 1UL || (modulus & 1UL) == 0UL) { results[index] = 0UL;