diff --git a/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs index 3f026c0..abeb8da 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs @@ -17,7 +17,12 @@ internal static class DivisorByDivisorKernels hits[index] = montgomeryResult == 1UL ? (byte)1 : (byte)0; } - public static void GenerateCandidatesAndGapsKernel(Index1D index, ArrayView candidates, ArrayView gaps, ulong startValue, ulong stride) + public static void GenerateCandidatesAndGapsKernel( + Index1D index, + ArrayView candidates, + ArrayView gaps, + GpuUInt128 startValue, + GpuUInt128 stride) { int globalIndex = index; int candidateLength = (int)candidates.Length; @@ -27,14 +32,44 @@ internal static class DivisorByDivisorKernels } ulong offset = (ulong)globalIndex; - ulong candidate = startValue + offset * stride; - candidates[globalIndex] = candidate; + GpuUInt128 candidateValue; + if (offset == 0UL) + { + candidateValue = startValue; + } + else + { + candidateValue = stride; + candidateValue.Mul(offset); + candidateValue.Add(startValue); + } + + candidates[globalIndex] = candidateValue.Low; int gapLength = (int)gaps.Length; - if (globalIndex < gapLength) + if (globalIndex >= gapLength) { - gaps[globalIndex] = offset == 0UL ? 0UL : stride; + return; } + + if (offset == 0UL) + { + gaps[globalIndex] = 0UL; + return; + } + + GpuUInt128 previousValue = startValue; + ulong previousOffset = offset - 1UL; + if (previousOffset != 0UL) + { + GpuUInt128 scaledStride = stride; + scaledStride.Mul(previousOffset); + previousValue.Add(scaledStride); + } + + GpuUInt128 gapValue = candidateValue; + gapValue.Sub(previousValue); + gaps[globalIndex] = gapValue.Low; } public static void ComputeRemainderDeltasKernel(Index1D index, ArrayView gaps, ArrayView deltas, byte modulus) diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index a0d6219..5a988bf 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -28,7 +28,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private readonly ConcurrentDictionary, ArrayView, byte>> _remainderDeltaKernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary, ArrayView, byte, byte>> _remainderScanKernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary, ArrayView, ArrayView, ArrayView, byte, ArrayView>> _candidateMaskKernelCache = new(AcceleratorReferenceComparer.Instance); - private readonly ConcurrentDictionary, ArrayView, ulong, ulong>> _candidateGenerationKernelCache = new(AcceleratorReferenceComparer.Instance); + private readonly ConcurrentDictionary, ArrayView, GpuUInt128, GpuUInt128>> _candidateGenerationKernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary> _resourcePools = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentBag _acceleratorPool = new(); private readonly ConcurrentBag _sessionPool = new(); @@ -52,10 +52,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, byte, ArrayView>(DivisorByDivisorKernels.EvaluateCandidateMaskKernel)); - private Action, ArrayView, ulong, ulong> GetCandidateGenerationKernel(Accelerator accelerator) => + private Action, ArrayView, GpuUInt128, GpuUInt128> GetCandidateGenerationKernel(Accelerator accelerator) => _candidateGenerationKernelCache.GetOrAdd( accelerator, - acc => acc.LoadAutoGroupedStreamKernel, ArrayView, ulong, ulong>(DivisorByDivisorKernels.GenerateCandidatesAndGapsKernel)); + acc => acc.LoadAutoGroupedStreamKernel, ArrayView, GpuUInt128, GpuUInt128>(DivisorByDivisorKernels.GenerateCandidatesAndGapsKernel)); public int GpuBatchSize { @@ -208,7 +208,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv Action, ArrayView, byte> remainderDeltaKernel, Action, ArrayView, byte, byte> remainderScanKernel, Action, ArrayView, ArrayView, ArrayView, byte, ArrayView> candidateMaskKernel, - Action, ArrayView, ulong, ulong> candidateGenerationKernel, + Action, ArrayView, GpuUInt128, GpuUInt128> candidateGenerationKernel, in ulong[] divisors, ulong[] exponents, byte[] hits, @@ -268,7 +268,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv ArrayPool ulongPool = ThreadStaticPools.UlongPool; ulong[] filteredDivisors = ulongPool.Rent(chunkCapacity); - ulong[]? fallbackDivisorGaps = null; var viewCache = new BufferSliceCache(divisorDeltaBuffer, remainderDeltaBuffer, remainderBuffer, hitsBuffer, remainderStride); Span candidateStorage = cycleCandidates.AsSpan(); Span hitStorage = hits.AsSpan(); @@ -306,58 +305,17 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv UInt128 localDivisor = currentDivisor128; UInt128 nextDivisor; - bool strideFits = twoP128 <= (UInt128)ulong.MaxValue; - bool baseFits = localDivisor <= (UInt128)ulong.MaxValue; - bool useGpuGeneration = strideFits && baseFits; - if (useGpuGeneration) + ArrayView1D candidateView = candidateBuffer.View.SubView(0, effectiveCount); + GpuUInt128 gpuStartValue = new GpuUInt128(localDivisor); + GpuUInt128 gpuStride = new GpuUInt128(twoP128); + GenerateCandidatesOnGpu(effectiveCount, gpuStartValue, gpuStride, candidateView, gapView, candidateGenerationKernel); + if (effectiveCount > 0) { - ulong startValue = (ulong)localDivisor; - ulong stride = (ulong)twoP128; - ArrayView1D candidateView = candidateBuffer.View.SubView(0, effectiveCount); - GenerateCandidatesOnGpu(effectiveCount, startValue, stride, candidateView, gapView, candidateGenerationKernel); - if (effectiveCount > 0) - { - candidateView.CopyToCPU(ref MemoryMarshal.GetReference(candidateSpan), effectiveCount); - } - - nextDivisor = localDivisor + twoP128 * (UInt128)(ulong)effectiveCount; + candidateView.CopyToCPU(ref MemoryMarshal.GetReference(candidateSpan), effectiveCount); } - else - { - if (fallbackDivisorGaps is null || fallbackDivisorGaps.Length < chunkCapacity) - { - if (fallbackDivisorGaps is not null) - { - ulongPool.Return(fallbackDivisorGaps, clearArray: false); - } - fallbackDivisorGaps = ulongPool.Rent(chunkCapacity); - } - - Span gapSpan = fallbackDivisorGaps.AsSpan(0, effectiveCount); - UInt128 cursor = localDivisor; - - for (int i = 0; i < effectiveCount; i++) - { - ulong divisorValue = (ulong)cursor; - candidateSpan[i] = divisorValue; - cursor += twoP128; - } - - if (effectiveCount > 0) - { - gapSpan[0] = 0UL; - for (int i = 1; i < effectiveCount; i++) - { - gapSpan[i] = candidateSpan[i] - candidateSpan[i - 1]; - } - - gapView.CopyFromCPU(ref MemoryMarshal.GetReference(gapSpan), effectiveCount); - } - - nextDivisor = cursor; - } + nextDivisor = localDivisor + twoP128 * (UInt128)effectiveCount; processedCount += (ulong)effectiveCount; if (effectiveCount > 0) @@ -447,10 +405,6 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv } ulongPool.Return(filteredDivisors, clearArray: false); - if (fallbackDivisorGaps is not null) - { - ulongPool.Return(fallbackDivisorGaps, clearArray: false); - } coveredRange = composite || processedAll || (currentDivisor128 > allowedMax128); return composite; @@ -530,11 +484,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void GenerateCandidatesOnGpu( int count, - ulong startValue, - ulong stride, + GpuUInt128 startValue, + GpuUInt128 stride, ArrayView1D candidateView, ArrayView1D gapView, - Action, ArrayView, ulong, ulong> kernel) + Action, ArrayView, GpuUInt128, GpuUInt128> kernel) { if (count <= 0) { @@ -854,7 +808,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv internal readonly Action, ArrayView, byte, byte> RemainderScanKernel; internal readonly Action, ArrayView, ArrayView, ArrayView, byte, ArrayView> CandidateMaskKernel; - internal readonly Action, ArrayView, ulong, ulong> CandidateGenerationKernel; + internal readonly Action, ArrayView, GpuUInt128, GpuUInt128> CandidateGenerationKernel; internal readonly int Capacity;