diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index d4bfc4e..dbef3b1 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -1,6 +1,7 @@ using System; using System.Buffers; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Numerics; @@ -122,6 +123,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv resources.CycleCandidates, resources.CycleLengths, resources.CycleCapacity, + resources, out lastProcessed, out coveredRange, out processedCount @@ -186,6 +188,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv ulong[] cycleCandidates, ulong[] cycleLengths, int cycleCapacity, + BatchResources resources, out ulong lastProcessed, out bool coveredRange, out ulong processedCount) @@ -340,10 +343,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv gapView.CopyFromCPU(ref MemoryMarshal.GetReference(gapSpan), chunkCount); - ComputeRemaindersOnGpu(chunkCount, remainder10, 10, gapView, deltaView, remainder10View, remainderDeltaKernel, remainderScanKernel); - ComputeRemaindersOnGpu(chunkCount, remainder8, 8, gapView, deltaView, remainder8View, remainderDeltaKernel, remainderScanKernel); - ComputeRemaindersOnGpu(chunkCount, remainder5, 5, gapView, deltaView, remainder5View, remainderDeltaKernel, remainderScanKernel); - ComputeRemaindersOnGpu(chunkCount, remainder3, 3, gapView, deltaView, remainder3View, remainderDeltaKernel, remainderScanKernel); + ComputeRemaindersOnGpu(chunkCount, remainder10, 10, gapView, deltaView, remainder10View, remainderDeltaKernel, remainderScanKernel, resources.GetRemainderScanConfig); + ComputeRemaindersOnGpu(chunkCount, remainder8, 8, gapView, deltaView, remainder8View, remainderDeltaKernel, remainderScanKernel, resources.GetRemainderScanConfig); + ComputeRemaindersOnGpu(chunkCount, remainder5, 5, gapView, deltaView, remainder5View, remainderDeltaKernel, remainderScanKernel, resources.GetRemainderScanConfig); + ComputeRemaindersOnGpu(chunkCount, remainder3, 3, gapView, deltaView, remainder3View, remainderDeltaKernel, remainderScanKernel, resources.GetRemainderScanConfig); BuildCandidateMaskOnGpu(chunkCount, remainder10View, remainder8View, remainder5View, remainder3View, lastIsSevenFlag, maskView, maskSpan, candidateMaskKernel); @@ -461,7 +464,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv ArrayView1D deltaView, ArrayView1D remainderView, Action, ArrayView, byte> deltaKernel, - Action, ArrayView, byte, byte> scanKernel) + Action, ArrayView, byte, byte> scanKernel, + Func configProvider) { // GPU batches always contain at least one divisor gap, so the empty-count guard remains commented // to eliminate redundant branching. @@ -471,10 +475,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv // } deltaKernel(count, gapView, deltaView, modulus); - SharedMemoryConfig sharedMemoryConfig = SharedMemoryConfig.RequestDynamic(count); - Index1D gridDim = new Index1D(1); - Index1D groupDim = new Index1D(count); - KernelConfig kernelConfig = new KernelConfig(gridDim, groupDim, sharedMemoryConfig); + KernelConfig kernelConfig = configProvider(count); scanKernel(kernelConfig, new Index1D(count), deltaView, remainderView, baseRemainder, modulus); } @@ -906,10 +907,24 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv internal readonly Action, ArrayView, byte, byte> RemainderScanKernel; internal readonly Action, ArrayView, ArrayView, ArrayView, byte, ArrayView> CandidateMaskKernel; + private readonly Dictionary _remainderScanConfigs = new(); + internal readonly int Capacity; internal readonly int CycleCapacity; + internal KernelConfig GetRemainderScanConfig(int count) + { + if (!_remainderScanConfigs.TryGetValue(count, out KernelConfig config)) + { + SharedMemoryConfig sharedMemoryConfig = SharedMemoryConfig.RequestDynamic(count); + config = new KernelConfig(new Index1D(1), new Index1D(count), sharedMemoryConfig); + _remainderScanConfigs[count] = config; + } + + return config; + } + public void Dispose() { DivisorsBuffer.Dispose(); diff --git a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs index 2e066c1..084473c 100644 --- a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs +++ b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs @@ -1611,7 +1611,7 @@ internal static partial class PrimeOrderGpuHeuristics private static ulong CalculateByDoublingKernel(ulong prime) { ulong order = 1UL; - ulong value = 2UL % prime; + ulong value = 2UL; // Candidate primes exceed two, so the modulo would always return two. while (value != 1UL) { diff --git a/PerfectNumbers.Core/ULongExtensions.cs b/PerfectNumbers.Core/ULongExtensions.cs index cf45857..3c84598 100644 --- a/PerfectNumbers.Core/ULongExtensions.cs +++ b/PerfectNumbers.Core/ULongExtensions.cs @@ -530,7 +530,9 @@ public static class ULongExtensions ulong modulus = divisor.Modulus; if (exponent == 0UL) { - return keepMontgomery ? divisor.MontgomeryOne : 1UL % modulus; + return keepMontgomery + ? divisor.MontgomeryOne + : 1UL; // Montgomery moduli stay above one on the scanning path, so the modulo would always yield one. } if (exponent <= Pow2WindowFallbackThreshold) @@ -622,7 +624,9 @@ public static class ULongExtensions ulong modulus = divisor.Modulus; if (exponent == 0UL) { - return keepMontgomery ? divisor.MontgomeryOne : 1UL % modulus; + return keepMontgomery + ? divisor.MontgomeryOne + : 1UL; // Montgomery moduli stay above one on the scanning path, so the modulo would always yield one. } if (exponent <= Pow2WindowFallbackThreshold) @@ -769,7 +773,7 @@ public static class ULongExtensions return; } - ulong baseValue = 2UL % modulus; + ulong baseValue = 2UL; // Moduli arrive as odd primes greater than two, so the modulo would always return two. destination[0] = baseValue; if (oddPowerCount == 1) {