diff --git a/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs b/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs index 6ace3ca..b33320f 100644 --- a/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs +++ b/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs @@ -121,7 +121,7 @@ public class GpuKernelCompilationTests private static readonly Action, ArrayView1D> MersenneDivisorCyclesKernel = DivisorCycleKernels.GpuDivisorCycleKernel; - private static readonly Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> PrimeTesterSmallPrimeKernel = + private static readonly Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> PrimeTesterSmallPrimeKernel = PrimeTesterKernels.SmallPrimeSieveKernel; private static readonly Action, ArrayView> PrimeTesterSharesFactorKernel = @@ -336,7 +336,7 @@ public class GpuKernelCompilationTests private static void CompilePrimeTesterSmallPrimeKernel(Accelerator accelerator) { - var kernel = accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(PrimeTesterSmallPrimeKernel); + var kernel = accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(PrimeTesterSmallPrimeKernel); _ = kernel; } diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs index c3f4c5c..f60596a 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs @@ -15,34 +15,44 @@ internal static class PrimeTesterKernels ArrayView smallPrimesLastSeven, ArrayView smallPrimesLastThree, ArrayView smallPrimesLastNine, + ArrayView smallPrimesPow2, + ArrayView smallPrimesPow2LastOne, + ArrayView smallPrimesPow2LastSeven, + ArrayView smallPrimesPow2LastThree, + ArrayView smallPrimesPow2LastNine, ArrayView results) { ulong n = numbers[index]; byte result = 1; ArrayView primes = smallPrimes; + ArrayView primeSquares = smallPrimesPow2; ulong lastDigit = n % 10UL; switch (lastDigit) { case 1UL: primes = smallPrimesLastOne; + primeSquares = smallPrimesPow2LastOne; break; case 3UL: primes = smallPrimesLastThree; + primeSquares = smallPrimesPow2LastThree; break; case 7UL: primes = smallPrimesLastSeven; + primeSquares = smallPrimesPow2LastSeven; break; case 9UL: primes = smallPrimesLastNine; + primeSquares = smallPrimesPow2LastNine; break; } - int length = (int)primes.Length; + int length = XMath.Min((int)primes.Length, (int)primeSquares.Length); for (int i = 0; i < length; i++) { ulong prime = primes[i]; - ulong primeSquare = prime * prime; + ulong primeSquare = primeSquares[i]; if (primeSquare > n) { break; diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 740f48e..476eea9 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -101,21 +101,21 @@ public sealed class PrimeTester public bool IsPrimeGpu(ulong n, CancellationToken ct) { bool forceCpu = GpuContextPool.ForceCpu; + bool belowGpuRange = n < 31UL; + + if (forceCpu || belowGpuRange) + { + return IsPrimeCpu(n, ct); + } + Span one = stackalloc ulong[1]; Span outFlags = stackalloc byte[1]; one[0] = n; outFlags[0] = 0; - if (!forceCpu) - { - IsPrimeBatchGpu(one, outFlags); - } + IsPrimeBatchGpu(one, outFlags); - bool belowGpuRange = n < 31UL; - bool gpuReportedPrime = !forceCpu && !belowGpuRange && outFlags[0] != 0; - bool requiresCpuFallback = forceCpu || belowGpuRange || !gpuReportedPrime; - - return requiresCpuFallback ? IsPrimeCpu(n, ct) : true; + return outFlags[0] != 0; } public static bool IsPrimeCpu(ulong n, CancellationToken ct) @@ -162,6 +162,11 @@ public sealed class PrimeTester state.DevicePrimesLastSeven.View, state.DevicePrimesLastThree.View, state.DevicePrimesLastNine.View, + state.DevicePrimesPow2Default.View, + state.DevicePrimesPow2LastOne.View, + state.DevicePrimesPow2LastSeven.View, + state.DevicePrimesPow2LastThree.View, + state.DevicePrimesPow2LastNine.View, output.View); accelerator.Synchronize(); output.View.CopyToCPU(ref results[pos], count); @@ -275,13 +280,18 @@ public sealed class PrimeTester // Per-accelerator GPU state for prime sieve (kernel + uploaded primes). internal sealed class KernelState { - public Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> Kernel { get; } + public Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> Kernel { get; } public Action, ulong, ArrayView> HeuristicTrialDivisionKernel { get; } public MemoryBuffer1D DevicePrimesDefault { get; } public MemoryBuffer1D DevicePrimesLastOne { get; } public MemoryBuffer1D DevicePrimesLastSeven { get; } public MemoryBuffer1D DevicePrimesLastThree { get; } public MemoryBuffer1D DevicePrimesLastNine { get; } + public MemoryBuffer1D DevicePrimesPow2Default { get; } + public MemoryBuffer1D DevicePrimesPow2LastOne { get; } + public MemoryBuffer1D DevicePrimesPow2LastSeven { get; } + public MemoryBuffer1D DevicePrimesPow2LastThree { get; } + public MemoryBuffer1D DevicePrimesPow2LastNine { get; } public bool IsDisposed { get; private set; } private readonly System.Collections.Concurrent.ConcurrentBag _scratchPool = []; // TODO: Replace this ConcurrentBag with the lock-free ring buffer variant validated in @@ -292,7 +302,7 @@ public sealed class PrimeTester { IsDisposed = false; // Compile once per accelerator and upload primes once. - Kernel = accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(PrimeTesterKernels.SmallPrimeSieveKernel); + Kernel = accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(PrimeTesterKernels.SmallPrimeSieveKernel); HeuristicTrialDivisionKernel = accelerator.LoadAutoGroupedStreamKernel, ulong, ArrayView>(PrimeTesterKernels.HeuristicTrialDivisionKernel); var primesDefault = DivisorGenerator.SmallPrimes; @@ -314,6 +324,26 @@ public sealed class PrimeTester var primesLastNine = DivisorGenerator.SmallPrimesLastNine; DevicePrimesLastNine = accelerator.Allocate1D(primesLastNine.Length); DevicePrimesLastNine.View.CopyFromCPU(primesLastNine); + + var primesPow2Default = DivisorGenerator.SmallPrimesPow2; + DevicePrimesPow2Default = accelerator.Allocate1D(primesPow2Default.Length); + DevicePrimesPow2Default.View.CopyFromCPU(primesPow2Default); + + var primesPow2LastOne = DivisorGenerator.SmallPrimesPow2LastOne; + DevicePrimesPow2LastOne = accelerator.Allocate1D(primesPow2LastOne.Length); + DevicePrimesPow2LastOne.View.CopyFromCPU(primesPow2LastOne); + + var primesPow2LastSeven = DivisorGenerator.SmallPrimesPow2LastSeven; + DevicePrimesPow2LastSeven = accelerator.Allocate1D(primesPow2LastSeven.Length); + DevicePrimesPow2LastSeven.View.CopyFromCPU(primesPow2LastSeven); + + var primesPow2LastThree = DivisorGenerator.SmallPrimesPow2LastThree; + DevicePrimesPow2LastThree = accelerator.Allocate1D(primesPow2LastThree.Length); + DevicePrimesPow2LastThree.View.CopyFromCPU(primesPow2LastThree); + + var primesPow2LastNine = DivisorGenerator.SmallPrimesPow2LastNine; + DevicePrimesPow2LastNine = accelerator.Allocate1D(primesPow2LastNine.Length); + DevicePrimesPow2LastNine.View.CopyFromCPU(primesPow2LastNine); } internal sealed class ScratchBuffers @@ -376,6 +406,11 @@ public sealed class PrimeTester DevicePrimesLastSeven.Dispose(); DevicePrimesLastThree.Dispose(); DevicePrimesLastNine.Dispose(); + DevicePrimesPow2Default.Dispose(); + DevicePrimesPow2LastOne.Dispose(); + DevicePrimesPow2LastSeven.Dispose(); + DevicePrimesPow2LastThree.Dispose(); + DevicePrimesPow2LastNine.Dispose(); IsDisposed = true; } }