From 23e8d306b1b08699c620ad9d22e6dae8cb68ffda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Sun, 21 Sep 2025 02:48:51 +0200 Subject: [PATCH] Breaking change partially reverted --- EvenPerfectBitScanner/Program.cs | 2 - .../Gpu/MersenneNumberResidueGpuTester.cs | 116 +++++++++--------- PerfectNumbers.Core/MersenneNumberTester.cs | 11 +- 3 files changed, 62 insertions(+), 67 deletions(-) diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index dbd683e..0ac8140 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -372,8 +372,6 @@ internal static class Program } } - residueKMax = ResolveResidueMaxK(useResidue, residueKMaxExplicit, divisorCyclesLimitExplicit, residueKMax, divisorCyclesSearchLimit128); - if (showHelp) { PrintHelp(); diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs index a9ee58d..a3dc886 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs @@ -15,39 +15,40 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) public void Scan(ulong exponent, UInt128 twoP, bool lastIsSeven, UInt128 maxK, ref bool isPrime) { var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); - var accelerator = gpuLease.Accelerator; - // Ensure device has small cycles and primes tables for in-kernel lookup - var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator); - ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator); - int batchSize = GpuConstants.ScanBatchSize; - UInt128 kStart = 1UL; - byte last = lastIsSeven ? (byte)1 : (byte)0; - var kernel = gpuLease.Pow2ModKernel; - ulong step10 = ((exponent % 10UL) << 1) % 10UL; - ulong step8 = ((exponent & 7UL) << 1) & 7UL; - ulong step3 = ((exponent % 3UL) << 1) % 3UL; - ulong step5 = ((exponent % 5UL) << 1) % 5UL; - GpuUInt128 twoPGpu = (GpuUInt128)twoP; + var accelerator = gpuLease.Accelerator; + var stream = gpuLease.Stream; + // Ensure device has small cycles and primes tables for in-kernel lookup + var smallCyclesView = GpuKernelPool.EnsureSmallCyclesOnDevice(accelerator); + ResiduePrimeViews primeViews = GpuKernelPool.EnsureSmallPrimesOnDevice(accelerator); + int batchSize = GpuConstants.ScanBatchSize; + UInt128 kStart = 1UL; + byte last = lastIsSeven ? (byte)1 : (byte)0; + var kernel = gpuLease.Pow2ModKernel; + ulong step10 = ((exponent % 10UL) << 1) % 10UL; + ulong step8 = ((exponent & 7UL) << 1) & 7UL; + ulong step3 = ((exponent % 3UL) << 1) % 3UL; + ulong step5 = ((exponent % 5UL) << 1) % 5UL; + GpuUInt128 twoPGpu = (GpuUInt128)twoP; - var orderBuffer = accelerator.Allocate1D(batchSize); - // Device memory returned by Allocate1D is not guaranteed to be zeroed. - // Ensure the buffer starts with a known state so that lanes skipped by the - // kernel (for example due to early rejections) do not leave stale garbage - // values that would be interpreted as composite witnesses when copied back - // to the host. This mirrors the explicit zeroing performed by other GPU - // testers such as the order kernels. - orderBuffer.MemSetToZero(); - ulong[] orderArray = ArrayPool.Shared.Rent(batchSize); - UInt128 remaining; - int currentSize; - int i; - UInt128 batchSize128 = (UInt128)batchSize, q; - Span orders = orderArray.AsSpan(0, batchSize); - try - { - while (kStart < maxK && Volatile.Read(ref isPrime)) - { - remaining = maxK - kStart; + var orderBuffer = accelerator.Allocate1D(batchSize); + // Device memory returned by Allocate1D is not guaranteed to be zeroed. + // Ensure the buffer starts with a known state so that lanes skipped by the + // kernel (for example due to early rejections) do not leave stale garbage + // values that would be interpreted as composite witnesses when copied back + // to the host. This mirrors the explicit zeroing performed by other GPU + // testers such as the order kernels. + orderBuffer.MemSetToZero(); + ulong[] orderArray = ArrayPool.Shared.Rent(batchSize); + UInt128 remaining; + int currentSize; + int i; + UInt128 batchSize128 = (UInt128)batchSize, q; + Span orders = orderArray.AsSpan(0, batchSize); + try + { + while (kStart < maxK && Volatile.Read(ref isPrime)) + { + remaining = maxK - kStart; if (remaining > batchSize128) { currentSize = batchSize; @@ -58,35 +59,36 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) orders = orderArray.AsSpan(0, currentSize); } - q = twoP * kStart + UInt128.One; - q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3); - var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5); + q = twoP * kStart + UInt128.One; + q.Mod10_8_5_3(out ulong q0m10, out ulong q0m8, out ulong q0m5, out ulong q0m3); + var ra = new ResidueAutomatonArgs(q0m10, step10, q0m8, step8, q0m3, step3, q0m5, step5); - kernel(currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL, - ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2); + kernel(stream, currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL, + ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2); // kernel(currentSize, exponent, twoPGpu, (GpuUInt128)kStart, last, 0UL, + // ra, orderBuffer.View, smallCyclesView, primeViews.LastOne, primeViews.LastSeven, primeViews.LastOnePow2, primeViews.LastSevenPow2); - accelerator.Synchronize(); - orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize); - if (!Volatile.Read(ref isPrime)) - { - break; - } + accelerator.Synchronize(); + orderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(orders), currentSize); + if (!Volatile.Read(ref isPrime)) + { + break; + } - for (i = 0; i < currentSize; i++) - { - if (orders[i] != 0UL) - { - Volatile.Write(ref isPrime, false); - break; - } - } + for (i = 0; i < currentSize; i++) + { + if (orders[i] != 0UL) + { + Volatile.Write(ref isPrime, false); + break; + } + } - kStart += batchSize128; - } - } - finally - { - ArrayPool.Shared.Return(orderArray); + kStart += batchSize128; + } + } + finally + { + ArrayPool.Shared.Return(orderArray); orderBuffer.Dispose(); gpuLease.Dispose(); } diff --git a/PerfectNumbers.Core/MersenneNumberTester.cs b/PerfectNumbers.Core/MersenneNumberTester.cs index 15f7920..e41df77 100644 --- a/PerfectNumbers.Core/MersenneNumberTester.cs +++ b/PerfectNumbers.Core/MersenneNumberTester.cs @@ -16,6 +16,7 @@ public enum GpuKernelType } public sealed class MersenneNumberTester( + UInt128 maxK, bool useIncremental = true, bool useOrderCache = false, GpuKernelType kernelType = GpuKernelType.Incremental, @@ -24,12 +25,11 @@ public sealed class MersenneNumberTester( bool useGpuLucas = true, bool useGpuScan = true, bool useGpuOrder = true, - bool useResidue = true, - ulong maxK = 5_000_000UL) + bool useResidue = true) { private readonly bool _useResidue = useResidue; private readonly bool _useIncremental = useIncremental && !useResidue; - private readonly ulong _maxK = maxK; + private readonly UInt128 _maxK = maxK; private readonly GpuKernelType _kernelType = kernelType; private readonly bool _useModuloWorkaround = useModuloWorkaround; private readonly bool _useGpuLucas = useGpuLucas; @@ -228,11 +228,6 @@ public sealed class MersenneNumberTester( // In residue mode the residue scan is the final primality check. if (_useResidue) { - if ((UInt128)_maxK < maxK) - { - maxK = _maxK; - } - if (_useGpuScan) { _residueGpuTester!.Scan(exponent, twoP, lastIsSeven, maxK, ref prePrime);