From 183b1471358ed80072deb621f3a54afb7597e62d Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Mon, 17 Nov 2025 16:04:27 +0100 Subject: [PATCH] Heuristic tables split into 2 groups --- .../PrimeTesterBenchmarks.cs | 9 +- EvenPerfectBitScanner/Program.cs | 4 +- ...rsenneNumberDivisorByDivisorTesterTests.cs | 2 +- .../MersenneNumberDivisorCpuTesterTests.cs | 6 +- .../MersenneNumberDivisorGpuTesterTests.cs | 12 +- .../Cpu/MersenneCpuDivisorScanSession.cs | 2 +- ...MersenneNumberDivisorByDivisorCpuTester.cs | 962 +++++++++--------- .../Gpu/Accelerators/AcceleratorExtensions.cs | 34 + ...HeuristicCombinedPrimeTesterAccelerator.cs | 147 +++ .../HeuristicGpuCombinedTables.cs | 69 ++ .../Accelerators/HeuristicGpuGroupABTables.cs | 79 ++ .../Gpu/Accelerators/HeuristicGpuTables.cs | 150 --- ...HeuristicGroupABPrimeTesterAccelerator.cs} | 55 +- .../PrimeOrderCalculatorAccelerator.cs | 2 + .../Gpu/HeuristicGpuCombinedDivisorTables.cs | 45 + .../Gpu/HeuristicGpuDivisorTables.cs | 139 --- .../Gpu/HeuristicGpuGroupABDivisorTables.cs | 63 ++ .../Gpu/Kernels/PrimeTesterKernels.cs | 43 +- ...MersenneNumberDivisorByDivisorGpuTester.cs | 34 +- .../HeuristicCombinedPrimeTester.cs | 47 +- PerfectNumbers.Core/HeuristicPrimeTester.cs | 107 +- .../IMersenneNumberDivisorByDivisorTester.cs | 2 +- PerfectNumbers.Core/MersenneDivisorCycles.cs | 22 +- PerfectNumbers.Core/PerfectNumberConstants.cs | 1 + .../PrimeOrderCalculator.Cpu.cs | 72 +- .../PrimeOrderCalculator.Gpu.cs | 4 +- 26 files changed, 1172 insertions(+), 940 deletions(-) create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/AcceleratorExtensions.cs create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/HeuristicCombinedPrimeTesterAccelerator.cs create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuCombinedTables.cs create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuGroupABTables.cs delete mode 100644 PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs rename PerfectNumbers.Core/Gpu/Accelerators/{HeuristicPrimeTesterAccelerator.cs => HeuristicGroupABPrimeTesterAccelerator.cs} (67%) create mode 100644 PerfectNumbers.Core/Gpu/HeuristicGpuCombinedDivisorTables.cs delete mode 100644 PerfectNumbers.Core/Gpu/HeuristicGpuDivisorTables.cs create mode 100644 PerfectNumbers.Core/Gpu/HeuristicGpuGroupABDivisorTables.cs diff --git a/EvenPerfectBitScanner.Benchmarks/PrimeTesterBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/PrimeTesterBenchmarks.cs index 8cbc69c..f9a178b 100644 --- a/EvenPerfectBitScanner.Benchmarks/PrimeTesterBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/PrimeTesterBenchmarks.cs @@ -2,6 +2,7 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; using Open.Numeric.Primes; using PerfectNumbers.Core; +using PerfectNumbers.Core.Gpu.Accelerators; namespace EvenPerfectBitScanner.Benchmarks; @@ -25,6 +26,8 @@ public class PrimeTesterBenchmarks return; } + PrimeOrderCalculatorAccelerator.WarmUp(); + // HeuristicGroupABPrimeTesterAccelerator.WarmUp(); _ = _heuristicTester.IsPrimeCpu(_candidates[0]); _ = _heuristicTester.IsPrimeGpu(_candidates[0]); _ = Prime.Numbers.IsPrime(_candidates[0]); @@ -48,7 +51,7 @@ public class PrimeTesterBenchmarks return primeCount; } - // [Benchmark] + [Benchmark] public int HeuristicGpu() { HeuristicPrimeTester tester = _heuristicTester; @@ -66,7 +69,7 @@ public class PrimeTesterBenchmarks return primeCount; } - // [Benchmark] + [Benchmark] public int HeuristicCombinedGpu() { ulong[] values = _candidates; @@ -83,7 +86,7 @@ public class PrimeTesterBenchmarks return primeCount; } - // [Benchmark] + [Benchmark] public int HeuristicCombinedCpu() { ulong[] values = _candidates; diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index 29d0598..c632736 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -64,7 +64,7 @@ internal static class Program PrimeTester.GpuBatchSize = gpuPrimeBatch; GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads); Console.WriteLine("Warming up GPU kernels"); - PrimeTester.WarmUpGpuKernels(1024); + PrimeTester.WarmUpGpuKernels(1024); // PrimeTester.WarmUpGpuKernels(gpuPrimeThreads >> 4); Console.WriteLine("Starting up threads..."); _ = UnboundedTaskScheduler.Instance; @@ -628,7 +628,7 @@ internal static class Program { if (!(_runPrimesOnCpu ? PrimeTester.IsPrime(p) - : PrimeTester.IsPrimeGpu(p))) + : HeuristicCombinedPrimeTester.IsPrimeGpu(p))) { _lastCompositeP = true; return false; diff --git a/PerfectNumbers.Core.Tests/MersenneNumberDivisorByDivisorTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberDivisorByDivisorTesterTests.cs index 35b2fda..ae8f317 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberDivisorByDivisorTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberDivisorByDivisorTesterTests.cs @@ -83,7 +83,7 @@ public class MersenneNumberDivisorByDivisorTesterTests throw new NotSupportedException(); } - public void Dispose() + public void Return() { } } diff --git a/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs index 018911b..0372959 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs @@ -47,7 +47,7 @@ public class MersenneNumberDivisorCpuTesterTests } finally { - session.Dispose(); + session.Return(); } } @@ -72,7 +72,7 @@ public class MersenneNumberDivisorCpuTesterTests } finally { - session.Dispose(); + session.Return(); } } @@ -97,7 +97,7 @@ public class MersenneNumberDivisorCpuTesterTests } finally { - session.Dispose(); + session.Return(); } } } diff --git a/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs index 2ec1744..a3a62e2 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs @@ -161,7 +161,7 @@ public class MersenneNumberDivisorGpuTesterTests } finally { - session.Dispose(); + session.Return(); } } @@ -189,7 +189,7 @@ public class MersenneNumberDivisorGpuTesterTests } finally { - session.Dispose(); + session.Return(); } } @@ -217,7 +217,7 @@ public class MersenneNumberDivisorGpuTesterTests } finally { - session.Dispose(); + session.Return(); } } @@ -246,7 +246,7 @@ public class MersenneNumberDivisorGpuTesterTests } finally { - session.Dispose(); + session.Return(); } } @@ -275,7 +275,7 @@ public class MersenneNumberDivisorGpuTesterTests } finally { - session.Dispose(); + session.Return(); } } @@ -344,7 +344,7 @@ public class MersenneNumberDivisorGpuTesterTests } finally { - session.Dispose(); + session.Return(); } } } diff --git a/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs b/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs index 02cc808..44984ea 100644 --- a/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs +++ b/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs @@ -62,7 +62,7 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi } } - public void Dispose() + public void Return() { ThreadStaticPools.ReturnMersenneCpuDivisorSession(this); } diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs index 2fb0fa5..bf90f6a 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs @@ -2,486 +2,498 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using PerfectNumbers.Core; using PerfectNumbers.Core.Gpu; +using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core.Cpu; public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDivisorByDivisorTester { - private ulong _divisorLimit; - private int _batchSize = 1_024; - - [ThreadStatic] - private static GpuUInt128WorkSet _divisorScanGpuWorkSet; - - private struct GpuUInt128WorkSet - { - public GpuUInt128 Step; - public GpuUInt128 Divisor; - public GpuUInt128 Limit; - } - - public int BatchSize - { - get => _batchSize; - set => _batchSize = Math.Max(1, value); - } - - public void ConfigureFromMaxPrime(ulong maxPrime) - { - _divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime); - } - - public ulong DivisorLimit - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return _divisorLimit; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit); - - public bool IsPrime(ulong prime, out bool divisorsExhausted) - { - ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit); - - // The CPU by-divisor run always hands us primes with enormous divisor limits, so the fallback below never executes. - // if (allowedMax < 3UL) - // { - // // EvenPerfectBitScanner routes primes below the small-divisor cutoff to the GPU path, so the CPU path still sees - // // trivial candidates during targeted tests. Short-circuit here to keep those runs aligned with the production flow. - // divisorsExhausted = true; - // return true; - // } - - bool processedAll; - - bool composite = CheckDivisors( - prime, - allowedMax, - out processedAll); - - if (composite) - { - divisorsExhausted = true; - return false; - } - - divisorsExhausted = processedAll || composite; - return true; - } - - public void PrepareCandidates(in ReadOnlySpan primes, Span allowedMaxValues) - { - ulong divisorLimit = _divisorLimit; - int length = primes.Length; - for (int index = 0; index < length; index++) - { - allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit); - } - } - - public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession() - { - MersenneCpuDivisorScanSession? session = ThreadStaticPools.RentMersenneCpuDivisorSession(); - if (session is not null) - { - return session; - } - - return new MersenneCpuDivisorScanSession(); - } - - private bool CheckDivisors( - ulong prime, - ulong allowedMax, - out bool processedAll) - { - processedAll = false; - - // The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs. - // Keeping the guard commented out documents the reasoning for benchmarks and tests. - // if (allowedMax < 3UL) - // { - // return false; - // } - - ref GpuUInt128WorkSet workSet = ref _divisorScanGpuWorkSet; - - ref GpuUInt128 step = ref workSet.Step; - step.High = 0UL; - step.Low = prime; - step.ShiftLeft(1); - - ref GpuUInt128 limit = ref workSet.Limit; - limit.High = 0UL; - limit.Low = allowedMax; - - ref GpuUInt128 divisor = ref workSet.Divisor; - divisor.High = step.High; - divisor.Low = step.Low; - divisor.Add(1UL); - if (divisor.CompareTo(limit) > 0) - { - processedAll = true; - return false; - } - - // Intentionally recomputes factorizations without a per-thread cache. - // The previous factor cache recorded virtually no hits and only slowed down the scan. - DivisorCycleCache cycleCache = DivisorCycleCache.Shared; - - ulong stepHigh = step.High; - ulong stepLow = step.Low; - ulong limitHigh = limit.High; - ulong divisorLow = divisor.Low; - - // Keep the divisibility filters aligned with the divisor-cycle generator so the - // CPU path never requests cycles that were skipped during cache creation. - LastDigit lastDigit = (prime & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One; - ushort decimalMask = DivisorGenerator.GetDecimalMask(lastDigit); - - byte step10; - byte step8; - byte step3; - byte step7; - byte step11; - - byte remainder10; - byte remainder8; - byte remainder3; - byte remainder7; - byte remainder11; - - if (stepHigh == 0UL && limitHigh == 0UL) - { - step10 = (byte)(stepLow % 10UL); - step8 = (byte)(stepLow & 7UL); - step3 = (byte)(stepLow % 3UL); - step7 = (byte)(stepLow % 7UL); - step11 = (byte)(stepLow % 11UL); - - remainder10 = (byte)(divisorLow % 10UL); - remainder8 = (byte)(divisorLow & 7UL); - remainder3 = (byte)(divisorLow % 3UL); - remainder7 = (byte)(divisorLow % 7UL); - remainder11 = (byte)(divisorLow % 11UL); - - return CheckDivisors64( - prime, - stepLow, - limit.Low, - divisorLow, - decimalMask, - step10, - step8, - step3, - step7, - step11, - remainder10, - remainder8, - remainder3, - remainder7, - remainder11, - out processedAll); - } - - ulong divisorHigh = divisor.High; - step10 = (byte)((((stepHigh % 10UL) * 6UL) + (stepLow % 10UL)) % 10UL); - step8 = (byte)(stepLow % 8UL); - step3 = (byte)(((stepHigh % 3UL) + (stepLow % 3UL)) % 3UL); - step7 = (byte)((((stepHigh % 7UL) * 2UL) + (stepLow % 7UL)) % 7UL); - step11 = (byte)((((stepHigh % 11UL) * 5UL) + (stepLow % 11UL)) % 11UL); - - remainder10 = (byte)((((divisorHigh % 10UL) * 6UL) + (divisorLow % 10UL)) % 10UL); - remainder8 = (byte)(divisorLow % 8UL); - remainder3 = (byte)(((divisorHigh % 3UL) + (divisorLow % 3UL)) % 3UL); - remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL); - remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL); - - while (divisor.CompareTo(limit) <= 0) - { - bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0; - if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) - { - ulong candidate = divisor.Low; - MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(candidate); - ulong divisorCycle; - // Divisors generated from 2 * k * p + 1 exceed the small-cycle snapshot when p >= 138,000,000, so the short path below never runs. - // if (candidate <= PerfectNumberConstants.MaxQForDivisorCycles) - // { - // divisorCycle = cycleCache.GetCycleLength(candidate); - // } - // else - { - if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( - candidate, - prime, - divisorData, - out ulong computedCycle, - out bool primeOrderFailed) || computedCycle == 0UL) - { - // Divisors produced by 2 * k * p + 1 always exceed PerfectNumberConstants.MaxQForDivisorCycles - // for the exponents scanned here, so skip the unused cache fallback and compute directly. - divisorCycle = MersenneDivisorCycles.CalculateCycleLength( - candidate, - divisorData, - skipPrimeOrderHeuristic: primeOrderFailed); - } - else - { - divisorCycle = computedCycle; - } - } - - if (divisorCycle == prime) - { - // A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides - // the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p. - processedAll = true; - return true; - } - - if (divisorCycle == 0UL) - { - Console.WriteLine($"Divisor cycle was not calculated for {prime}"); - } - } - - divisor.Add(step); - remainder10 = AddMod10(remainder10, step10); - remainder8 = AddMod8(remainder8, step8); - remainder3 = AddMod3(remainder3, step3); - remainder7 = AddMod7(remainder7, step7); - remainder11 = AddMod11(remainder11, step11); - } - processedAll = true; - return false; - } - - private static bool CheckDivisors64( - ulong prime, - ulong step, - ulong limit, - ulong divisor, - ushort decimalMask, - byte step10, - byte step8, - byte step3, - byte step7, - byte step11, - byte remainder10, - byte remainder8, - byte remainder3, - byte remainder7, - byte remainder11, - out bool processedAll) - { - processedAll = false; - - bool canAdvance = step <= limit; - if (!canAdvance) - { - if (divisor <= limit) - { - if (remainder3 != 0 && remainder7 != 0 && remainder11 != 0 && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) - { - MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); - ulong divisorCycle; - if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( - divisor, - prime, - divisorData, - out ulong computedCycle, - out bool primeOrderFailed) || computedCycle == 0UL) - { - divisorCycle = MersenneDivisorCycles.CalculateCycleLength( - divisor, - divisorData, - skipPrimeOrderHeuristic: primeOrderFailed); - } - else - { - divisorCycle = computedCycle; - } - - if (divisorCycle == prime) - { - processedAll = true; - return true; - } - - if (divisorCycle == 0UL) - { - Console.WriteLine($"Divisor cycle was not calculated for {prime}"); - } - } - } - - processedAll = true; - return false; - } - - if (divisor > limit) - { - processedAll = true; - return false; - } - - ulong remainingIterations = ((limit - divisor) / step) + 1UL; - - while (true) - { - bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0; - if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) - { - MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); - ulong divisorCycle; - if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( - divisor, - prime, - divisorData, - out ulong computedCycle, - out bool primeOrderFailed) || computedCycle == 0UL) - { - divisorCycle = MersenneDivisorCycles.CalculateCycleLength( - divisor, - divisorData, - skipPrimeOrderHeuristic: primeOrderFailed); - } - else - { - divisorCycle = computedCycle; - } - - if (divisorCycle == prime) - { - processedAll = true; - return true; - } - - if (divisorCycle == 0UL) - { - Console.WriteLine($"Divisor cycle was not calculated for {prime}"); - } - } - - remainingIterations--; - if (remainingIterations == 0UL) - { - processedAll = true; - return false; - } - - divisor += step; - remainder10 = AddMod10(remainder10, step10); - remainder8 = AddMod8(remainder8, step8); - remainder3 = AddMod3(remainder3, step3); - remainder7 = AddMod7(remainder7, step7); - remainder11 = AddMod11(remainder11, step11); - } - } - - private static byte CheckDivisor(ulong prime, ulong divisorCycle, in MontgomeryDivisorData divisorData) - { - ulong residue = prime.Pow2MontgomeryModWithCycleCpu(divisorCycle, divisorData); - return residue == 1UL ? (byte)1 : (byte)0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte AddMod3(byte value, byte delta) - { - const int Modulus = 3; - int sum = value + delta; - - if (sum >= Modulus) - { - sum -= Modulus; - } - - return (byte)sum; - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte AddMod7(byte value, byte delta) - { - const int Modulus = 7; - int sum = value + delta; - - if (sum >= Modulus) - { - sum -= Modulus; - } - - return (byte)sum; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte AddMod10(byte value, byte delta) - { - const int Modulus = 10; - int sum = value + delta; - - if (sum >= Modulus) - { - sum -= Modulus; - } - - return (byte)sum; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte AddMod11(byte value, byte delta) - { - const int Modulus = 11; - int sum = value + delta; - - if (sum >= Modulus) - { - sum -= Modulus; - } - - return (byte)sum; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte AddMod8(byte value, byte delta) - { - return (byte)((value + delta) & 7); - } - - private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime) - { - // The by-divisor CPU configuration only supplies primes greater than 1, so the guard below never trips. - // if (maxPrime <= 1UL) - // { - // return 0UL; - // } - if (maxPrime - 1UL >= 64UL) - { - return ulong.MaxValue; - } - - return (1UL << (int)(maxPrime - 1UL)) - 1UL; - } - - private static ulong ComputeAllowedMaxDivisor(ulong prime, ulong divisorLimit) - { - // Production by-divisor scans only handle primes, so inputs never fall below 2. - // if (prime <= 1UL) - // { - // return 0UL; - // } - if (prime - 1UL >= 64UL) - { - return divisorLimit; - } - - return Math.Min((1UL << (int)(prime - 1UL)) - 1UL, divisorLimit); - } + private ulong _divisorLimit; + private int _batchSize = 1_024; + + [ThreadStatic] + private static GpuUInt128WorkSet _divisorScanGpuWorkSet; + + private struct GpuUInt128WorkSet + { + public GpuUInt128 Step; + public GpuUInt128 Divisor; + public GpuUInt128 Limit; + } + + public int BatchSize + { + get => _batchSize; + set => _batchSize = Math.Max(1, value); + } + + public void ConfigureFromMaxPrime(ulong maxPrime) + { + _divisorLimit = ComputeDivisorLimitFromMaxPrime(maxPrime); + } + + public ulong DivisorLimit + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _divisorLimit; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong GetAllowedMaxDivisor(ulong prime) => ComputeAllowedMaxDivisor(prime, _divisorLimit); + + public bool IsPrime(ulong prime, out bool divisorsExhausted) + { + ulong allowedMax = ComputeAllowedMaxDivisor(prime, _divisorLimit); + + // The CPU by-divisor run always hands us primes with enormous divisor limits, so the fallback below never executes. + // if (allowedMax < 3UL) + // { + // // EvenPerfectBitScanner routes primes below the small-divisor cutoff to the GPU path, so the CPU path still sees + // // trivial candidates during targeted tests. Short-circuit here to keep those runs aligned with the production flow. + // divisorsExhausted = true; + // return true; + // } + + bool processedAll; + + bool composite = CheckDivisors( + prime, + allowedMax, + out processedAll); + + if (composite) + { + divisorsExhausted = true; + return false; + } + + divisorsExhausted = processedAll || composite; + return true; + } + + public void PrepareCandidates(in ReadOnlySpan primes, Span allowedMaxValues) + { + ulong divisorLimit = _divisorLimit; + int length = primes.Length; + for (int index = 0; index < length; index++) + { + allowedMaxValues[index] = ComputeAllowedMaxDivisor(primes[index], divisorLimit); + } + } + + public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession() + { + MersenneCpuDivisorScanSession? session = ThreadStaticPools.RentMersenneCpuDivisorSession(); + if (session is not null) + { + return session; + } + + return new MersenneCpuDivisorScanSession(); + } + + private bool CheckDivisors( + ulong prime, + ulong allowedMax, + out bool processedAll) + { + processedAll = false; + + // The EvenPerfectBitScanner feeds primes >= 138,000,000 here, so allowedMax >= 3 in production runs. + // Keeping the guard commented out documents the reasoning for benchmarks and tests. + // if (allowedMax < 3UL) + // { + // return false; + // } + + ref GpuUInt128WorkSet workSet = ref _divisorScanGpuWorkSet; + + ref GpuUInt128 step = ref workSet.Step; + step.High = 0UL; + step.Low = prime; + step.ShiftLeft(1); + + ref GpuUInt128 limit = ref workSet.Limit; + limit.High = 0UL; + limit.Low = allowedMax; + + ref GpuUInt128 divisor = ref workSet.Divisor; + divisor.High = step.High; + divisor.Low = step.Low; + divisor.Add(1UL); + if (divisor.CompareTo(limit) > 0) + { + processedAll = true; + return false; + } + + // Intentionally recomputes factorizations without a per-thread cache. + // The previous factor cache recorded virtually no hits and only slowed down the scan. + _ = DivisorCycleCache.Shared; + + ulong stepHigh = step.High; + ulong stepLow = step.Low; + ulong limitHigh = limit.High; + ulong divisorLow = divisor.Low; + + // Keep the divisibility filters aligned with the divisor-cycle generator so the + // CPU path never requests cycles that were skipped during cache creation. + LastDigit lastDigit = (prime & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One; + ushort decimalMask = DivisorGenerator.GetDecimalMask(lastDigit); + + byte step10; + byte step8; + byte step3; + byte step7; + byte step11; + + byte remainder10; + byte remainder8; + byte remainder3; + byte remainder7; + byte remainder11; + + if (stepHigh == 0UL && limitHigh == 0UL) + { + step10 = (byte)(stepLow % 10UL); + step8 = (byte)(stepLow & 7UL); + step3 = (byte)(stepLow % 3UL); + step7 = (byte)(stepLow % 7UL); + step11 = (byte)(stepLow % 11UL); + + remainder10 = (byte)(divisorLow % 10UL); + remainder8 = (byte)(divisorLow & 7UL); + remainder3 = (byte)(divisorLow % 3UL); + remainder7 = (byte)(divisorLow % 7UL); + remainder11 = (byte)(divisorLow % 11UL); + + return CheckDivisors64( + prime, + stepLow, + limit.Low, + divisorLow, + decimalMask, + step10, + step8, + step3, + step7, + step11, + remainder10, + remainder8, + remainder3, + remainder7, + remainder11, + out processedAll); + } + + ulong divisorHigh = divisor.High; + step10 = (byte)((((stepHigh % 10UL) * 6UL) + (stepLow % 10UL)) % 10UL); + step8 = (byte)(stepLow % 8UL); + step3 = (byte)(((stepHigh % 3UL) + (stepLow % 3UL)) % 3UL); + step7 = (byte)((((stepHigh % 7UL) * 2UL) + (stepLow % 7UL)) % 7UL); + step11 = (byte)((((stepHigh % 11UL) * 5UL) + (stepLow % 11UL)) % 11UL); + + remainder10 = (byte)((((divisorHigh % 10UL) * 6UL) + (divisorLow % 10UL)) % 10UL); + remainder8 = (byte)(divisorLow % 8UL); + remainder3 = (byte)(((divisorHigh % 3UL) + (divisorLow % 3UL)) % 3UL); + remainder7 = (byte)((((divisorHigh % 7UL) * 2UL) + (divisorLow % 7UL)) % 7UL); + remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL); + + var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1); + + while (divisor.CompareTo(limit) <= 0) + { + bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0; + if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) + { + ulong candidate = divisor.Low; + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(candidate); + ulong divisorCycle; + // Divisors generated from 2 * k * p + 1 exceed the small-cycle snapshot when p >= 138,000,000, so the short path below never runs. + // if (candidate <= PerfectNumberConstants.MaxQForDivisorCycles) + // { + // divisorCycle = cycleCache.GetCycleLength(candidate); + // } + // else + { + if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + gpu, + candidate, + prime, + divisorData, + out ulong computedCycle, + out bool primeOrderFailed) || computedCycle == 0UL) + { + // Divisors produced by 2 * k * p + 1 always exceed PerfectNumberConstants.MaxQForDivisorCycles + // for the exponents scanned here, so skip the unused cache fallback and compute directly. + divisorCycle = MersenneDivisorCycles.CalculateCycleLength( + candidate, + divisorData, + skipPrimeOrderHeuristic: primeOrderFailed); + } + else + { + divisorCycle = computedCycle; + } + } + + if (divisorCycle == prime) + { + // A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides + // the corresponding Mersenne number because the order of 2 modulo the divisor is exactly p. + processedAll = true; + HeuristicCombinedPrimeTesterAccelerator.Return(gpu); + return true; + } + + if (divisorCycle == 0UL) + { + Console.WriteLine($"Divisor cycle was not calculated for {prime}"); + } + } + + divisor.Add(step); + remainder10 = AddMod10(remainder10, step10); + remainder8 = AddMod8(remainder8, step8); + remainder3 = AddMod3(remainder3, step3); + remainder7 = AddMod7(remainder7, step7); + remainder11 = AddMod11(remainder11, step11); + } + + HeuristicCombinedPrimeTesterAccelerator.Return(gpu); + processedAll = true; + return false; + } + + private static bool CheckDivisors64( + ulong prime, + ulong step, + ulong limit, + ulong divisor, + ushort decimalMask, + byte step10, + byte step8, + byte step3, + byte step7, + byte step11, + byte remainder10, + byte remainder8, + byte remainder3, + byte remainder7, + byte remainder11, + out bool processedAll) + { + processedAll = false; + + bool canAdvance = step <= limit; + var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1); + if (!canAdvance) + { + if (divisor <= limit) + { + if (remainder3 != 0 && remainder7 != 0 && remainder11 != 0 && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) + { + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); + ulong divisorCycle; + if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + gpu, + divisor, + prime, + divisorData, + out ulong computedCycle, + out bool primeOrderFailed) || computedCycle == 0UL) + { + divisorCycle = MersenneDivisorCycles.CalculateCycleLength( + divisor, + divisorData, + skipPrimeOrderHeuristic: primeOrderFailed); + } + else + { + divisorCycle = computedCycle; + } + + if (divisorCycle == prime) + { + processedAll = true; + HeuristicCombinedPrimeTesterAccelerator.Return(gpu); + return true; + } + + if (divisorCycle == 0UL) + { + Console.WriteLine($"Divisor cycle was not calculated for {prime}"); + } + } + } + + processedAll = true; + return false; + } + + if (divisor > limit) + { + processedAll = true; + return false; + } + + ulong remainingIterations = ((limit - divisor) / step) + 1UL; + + while (true) + { + bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0; + if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0) + { + MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); + ulong divisorCycle; + if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + gpu, + divisor, + prime, + divisorData, + out ulong computedCycle, + out bool primeOrderFailed) || computedCycle == 0UL) + { + divisorCycle = MersenneDivisorCycles.CalculateCycleLength( + divisor, + divisorData, + skipPrimeOrderHeuristic: primeOrderFailed); + } + else + { + divisorCycle = computedCycle; + } + + if (divisorCycle == prime) + { + processedAll = true; + HeuristicCombinedPrimeTesterAccelerator.Return(gpu); + return true; + } + + if (divisorCycle == 0UL) + { + Console.WriteLine($"Divisor cycle was not calculated for {prime}"); + } + } + + remainingIterations--; + if (remainingIterations == 0UL) + { + processedAll = true; + return false; + } + + divisor += step; + remainder10 = AddMod10(remainder10, step10); + remainder8 = AddMod8(remainder8, step8); + remainder3 = AddMod3(remainder3, step3); + remainder7 = AddMod7(remainder7, step7); + remainder11 = AddMod11(remainder11, step11); + } + } + + private static byte CheckDivisor(ulong prime, ulong divisorCycle, in MontgomeryDivisorData divisorData) + { + ulong residue = prime.Pow2MontgomeryModWithCycleCpu(divisorCycle, divisorData); + return residue == 1UL ? (byte)1 : (byte)0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte AddMod3(byte value, byte delta) + { + const int Modulus = 3; + int sum = value + delta; + + if (sum >= Modulus) + { + sum -= Modulus; + } + + return (byte)sum; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte AddMod7(byte value, byte delta) + { + const int Modulus = 7; + int sum = value + delta; + + if (sum >= Modulus) + { + sum -= Modulus; + } + + return (byte)sum; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte AddMod10(byte value, byte delta) + { + const int Modulus = 10; + int sum = value + delta; + + if (sum >= Modulus) + { + sum -= Modulus; + } + + return (byte)sum; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte AddMod11(byte value, byte delta) + { + const int Modulus = 11; + int sum = value + delta; + + if (sum >= Modulus) + { + sum -= Modulus; + } + + return (byte)sum; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte AddMod8(byte value, byte delta) + { + return (byte)((value + delta) & 7); + } + + private static ulong ComputeDivisorLimitFromMaxPrime(ulong maxPrime) + { + // The by-divisor CPU configuration only supplies primes greater than 1, so the guard below never trips. + // if (maxPrime <= 1UL) + // { + // return 0UL; + // } + if (maxPrime - 1UL >= 64UL) + { + return ulong.MaxValue; + } + + return (1UL << (int)(maxPrime - 1UL)) - 1UL; + } + + private static ulong ComputeAllowedMaxDivisor(ulong prime, ulong divisorLimit) + { + // Production by-divisor scans only handle primes, so inputs never fall below 2. + // if (prime <= 1UL) + // { + // return 0UL; + // } + if (prime - 1UL >= 64UL) + { + return divisorLimit; + } + + return Math.Min((1UL << (int)(prime - 1UL)) - 1UL, divisorLimit); + } } diff --git a/PerfectNumbers.Core/Gpu/Accelerators/AcceleratorExtensions.cs b/PerfectNumbers.Core/Gpu/Accelerators/AcceleratorExtensions.cs new file mode 100644 index 0000000..c546659 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/AcceleratorExtensions.cs @@ -0,0 +1,34 @@ +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu.Accelerators; + +public static class AcceleratorExtensions +{ + public static MemoryBuffer1D CopySpanToDevice(this Accelerator accelerator, AcceleratorStream stream, in ulong[] span) + { + MemoryBuffer1D? buffer; + buffer = accelerator.Allocate1D(stream, span); + + return buffer; + } + + public static MemoryBuffer1D CopyUintSpanToDevice(this Accelerator accelerator, AcceleratorStream stream, ReadOnlySpan span) + { + MemoryBuffer1D? buffer; + buffer = accelerator.Allocate1D(span.Length); + + if (!span.IsEmpty) + { + var converted = new ulong[span.Length]; + for (int i = 0; i < span.Length; i++) + { + converted[i] = span[i]; + } + + buffer.View.CopyFromCPU(stream, converted); + } + + return buffer; + } +} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicCombinedPrimeTesterAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicCombinedPrimeTesterAccelerator.cs new file mode 100644 index 0000000..33ef3fe --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicCombinedPrimeTesterAccelerator.cs @@ -0,0 +1,147 @@ +using System.Runtime.CompilerServices; +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu.Accelerators; + +public sealed class HeuristicCombinedPrimeTesterAccelerator +{ + #region Pool + + [ThreadStatic] + private static Queue? Pool; + + public static void WarmUp(int acceleratorIndex, AcceleratorStream stream) + { + Console.WriteLine($"Preparing heuristic accelerator {acceleratorIndex}..."); + HeuristicGpuCombinedTables.WarmUp(acceleratorIndex, stream); + // _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None); + // KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); + // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); + Console.WriteLine($"Heuristic accelerator {acceleratorIndex} is ready"); + } + + public static HeuristicCombinedPrimeTesterAccelerator Rent(int minBufferCapacity = 1) + { + var pool = Pool ??= new(); + if (!pool.TryDequeue(out var lease)) + { + lease = new HeuristicCombinedPrimeTesterAccelerator(minBufferCapacity); + } + else + { + lease.EnsureCapacity(minBufferCapacity); + } + + return lease; + } + + public static void Return(HeuristicCombinedPrimeTesterAccelerator lease) + { + var pool = Pool ??= new(); + pool.Enqueue(lease); + } + + public static void DisposeAll() + { + if (Pool != null) + { + while (Pool.TryDequeue(out var lease)) + { + lease.DisposeResources(); + } + } + } + + public static void Clear() + { + if (Pool != null) + { + var retained = new List(); + while (Pool.TryDequeue(out var lease)) + { + lease.DisposeResources(); + } + } + } + + #endregion + + public readonly HeuristicGpuCombinedDivisorTables DivisorTables; + internal readonly Context Context; + public readonly Accelerator Accelerator; + public readonly Kernel HeuristicCombinedTrialDivisionKernel; + + public MemoryBuffer1D? Input = null; + public MemoryBuffer1D? Output = null; + public MemoryBuffer1D? HeuristicFlag = null!; + public int BufferCapacity; + + private HeuristicGpuCombinedTables _sharedTables; + public MemoryBuffer1D HeuristicCombinedDivisorsEnding1 => _sharedTables.HeuristicCombinedDivisorsEnding1; + public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding1 => _sharedTables.HeuristicCombinedDivisorSquaresEnding1; + public MemoryBuffer1D HeuristicCombinedDivisorsEnding3 => _sharedTables.HeuristicCombinedDivisorsEnding3; + public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding3 => _sharedTables.HeuristicCombinedDivisorSquaresEnding3; + public MemoryBuffer1D HeuristicCombinedDivisorsEnding7 => _sharedTables.HeuristicCombinedDivisorsEnding7; + public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding7 => _sharedTables.HeuristicCombinedDivisorSquaresEnding7; + public MemoryBuffer1D HeuristicCombinedDivisorsEnding9 => _sharedTables.HeuristicCombinedDivisorsEnding9; + public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9 => _sharedTables.HeuristicCombinedDivisorSquaresEnding9; + + private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; + internal readonly int AcceleratorIndex; + + public HeuristicCombinedPrimeTesterAccelerator(int minBufferCapacity) + { + var acceleratorIndex = AcceleratorPool.Shared.Rent(); + AcceleratorIndex = acceleratorIndex; + Accelerator accelerator = _accelerators[acceleratorIndex]; + Context = accelerator.Context; + + Accelerator = accelerator; + EnsureCapacity(minBufferCapacity); + + HeuristicCombinedTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, HeuristicGpuCombinedDivisorTables>(PrimeTesterKernels.HeuristicTrialCombinedDivisionKernel)); + + AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); + + // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); + + _sharedTables = HeuristicGpuCombinedTables.GetStaticTables(acceleratorIndex); + DivisorTables = _sharedTables.CreateHeuristicDivisorTables(); + + stream.Synchronize(); + AcceleratorStreamPool.Return(acceleratorIndex, stream); + } + + public void EnsureCapacity(int minCapacity) + { + if (minCapacity <= BufferCapacity) + { + return; + } + + Input?.Dispose(); + Output?.Dispose(); + HeuristicFlag?.Dispose(); + + Input = Accelerator.Allocate1D(minCapacity); + Output = Accelerator.Allocate1D(minCapacity); + HeuristicFlag = Accelerator.Allocate1D(minCapacity); + + BufferCapacity = minCapacity; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Return() => Return(this); + + public void DisposeResources() + { + Input?.Dispose(); + Output?.Dispose(); + HeuristicFlag?.Dispose(); + // ReleaseSharedTables(_sharedTables); + // These resources are shared between GPU leases + // Accelerator.Dispose(); + // AcceleratorContext.Dispose(); + } +} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuCombinedTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuCombinedTables.cs new file mode 100644 index 0000000..8ba3400 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuCombinedTables.cs @@ -0,0 +1,69 @@ +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu.Accelerators; + +internal sealed class HeuristicGpuCombinedTables +{ + private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; + + private static readonly HeuristicGpuCombinedTables[] _sharedTables = new HeuristicGpuCombinedTables[PerfectNumberConstants.RollingAccelerators]; + + internal static void WarmUp(int acceleratorIndex, AcceleratorStream stream) => _sharedTables[acceleratorIndex] = new(_accelerators[acceleratorIndex], stream); + + internal static HeuristicGpuCombinedTables GetStaticTables(int acceleratorIndex) => _sharedTables[acceleratorIndex]; + + internal HeuristicGpuCombinedTables(Accelerator accelerator, AcceleratorStream stream) + { + var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1; + HeuristicCombinedDivisorsEnding1 = accelerator.CopySpanToDevice(stream, combinedEnding1); + var combinedSquaresEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Squares; + HeuristicCombinedDivisorSquaresEnding1 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding1); + + var combinedEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3; + HeuristicCombinedDivisorsEnding3 = accelerator.CopySpanToDevice(stream, combinedEnding3); + var combinedSquaresEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Squares; + HeuristicCombinedDivisorSquaresEnding3 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding3); + + var combinedEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7; + HeuristicCombinedDivisorsEnding7 = accelerator.CopySpanToDevice(stream, combinedEnding7); + var combinedSquaresEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Squares; + HeuristicCombinedDivisorSquaresEnding7 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding7); + + var combinedEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9; + HeuristicCombinedDivisorsEnding9 = accelerator.CopySpanToDevice(stream, combinedEnding9); + var combinedSquaresEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Squares; + HeuristicCombinedDivisorSquaresEnding9 = accelerator.CopySpanToDevice(stream, combinedSquaresEnding9); + } + + internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding1; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding1; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding3; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding3; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding7; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding7; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding9; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9; + + internal HeuristicGpuCombinedDivisorTables CreateHeuristicDivisorTables() => new( + HeuristicCombinedDivisorsEnding1.View, + HeuristicCombinedDivisorsEnding3.View, + HeuristicCombinedDivisorsEnding7.View, + HeuristicCombinedDivisorsEnding9.View, + HeuristicCombinedDivisorSquaresEnding1.View, + HeuristicCombinedDivisorSquaresEnding3.View, + HeuristicCombinedDivisorSquaresEnding7.View, + HeuristicCombinedDivisorSquaresEnding9.View); + + internal void Dispose() + { + HeuristicCombinedDivisorsEnding1.Dispose(); + HeuristicCombinedDivisorSquaresEnding1.Dispose(); + HeuristicCombinedDivisorsEnding3.Dispose(); + HeuristicCombinedDivisorSquaresEnding3.Dispose(); + HeuristicCombinedDivisorsEnding7.Dispose(); + HeuristicCombinedDivisorSquaresEnding7.Dispose(); + HeuristicCombinedDivisorsEnding9.Dispose(); + HeuristicCombinedDivisorSquaresEnding9.Dispose(); + } +} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuGroupABTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuGroupABTables.cs new file mode 100644 index 0000000..11ed95f --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuGroupABTables.cs @@ -0,0 +1,79 @@ +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu.Accelerators; + +internal sealed class HeuristicGpuGroupABTables +{ + private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; + + private static readonly HeuristicGpuGroupABTables[] _sharedTables = new HeuristicGpuGroupABTables[PerfectNumberConstants.RollingAccelerators]; + + private static readonly SemaphoreSlim[] _locks = [..Enumerable.Range(1, PerfectNumberConstants.RollingAccelerators).Select(_ => new SemaphoreSlim(1))]; + + internal static HeuristicGpuGroupABTables EnsureStaticTables(int acceleratorIndex, AcceleratorStream stream) + { + var @lock = _locks[acceleratorIndex]; + @lock.Wait(); + if (_sharedTables[acceleratorIndex] is {} existing) + { + @lock.Release(); + return existing; + } + + var accelerator = _accelerators[acceleratorIndex]; + existing = new(accelerator, stream); + _sharedTables[acceleratorIndex] = existing; + @lock.Release(); + return existing; + } + + internal HeuristicGpuGroupABTables(Accelerator accelerator, AcceleratorStream stream) + { + var heuristicGroupA = HeuristicPrimeSieves.GroupADivisorsStorage; + HeuristicGroupADivisors = accelerator.CopySpanToDevice(stream, heuristicGroupA); + + var heuristicGroupASquares = HeuristicPrimeSieves.GroupADivisorSquaresStorage; + HeuristicGroupADivisorSquares = accelerator.CopySpanToDevice(stream, heuristicGroupASquares); + + HeuristicGroupBDivisorsEnding1 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastOneWithoutLastThree); + HeuristicGroupBDivisorSquaresEnding1 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree); + + HeuristicGroupBDivisorsEnding7 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree); + HeuristicGroupBDivisorSquaresEnding7 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree); + + HeuristicGroupBDivisorsEnding9 = accelerator.CopyUintSpanToDevice(stream, DivisorGenerator.SmallPrimesLastNineWithoutLastThree); + HeuristicGroupBDivisorSquaresEnding9 = accelerator.CopySpanToDevice(stream, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree); + } + + internal readonly MemoryBuffer1D HeuristicGroupADivisors; + internal readonly MemoryBuffer1D HeuristicGroupADivisorSquares; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding1; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding1; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding7; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding7; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding9; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding9; + + internal HeuristicGpuGroupABDivisorTables CreateHeuristicDivisorTables() => new( + HeuristicGroupADivisors.View, + HeuristicGroupADivisorSquares.View, + HeuristicGroupBDivisorsEnding1.View, + HeuristicGroupBDivisorSquaresEnding1.View, + HeuristicGroupBDivisorsEnding7.View, + HeuristicGroupBDivisorSquaresEnding7.View, + HeuristicGroupBDivisorsEnding9.View, + HeuristicGroupBDivisorSquaresEnding9.View); + + internal void Dispose() + { + HeuristicGroupADivisors.Dispose(); + HeuristicGroupADivisorSquares.Dispose(); + HeuristicGroupBDivisorsEnding1.Dispose(); + HeuristicGroupBDivisorSquaresEnding1.Dispose(); + HeuristicGroupBDivisorsEnding7.Dispose(); + HeuristicGroupBDivisorSquaresEnding7.Dispose(); + HeuristicGroupBDivisorsEnding9.Dispose(); + HeuristicGroupBDivisorSquaresEnding9.Dispose(); + } +} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs deleted file mode 100644 index 36e3ff2..0000000 --- a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs +++ /dev/null @@ -1,150 +0,0 @@ -using ILGPU; -using ILGPU.Runtime; - -namespace PerfectNumbers.Core.Gpu.Accelerators; - -internal sealed class HeuristicGpuTables -{ - private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; - - private static readonly HeuristicGpuTables[] _sharedTables = new HeuristicGpuTables[PerfectNumberConstants.RollingAccelerators]; - - private static readonly SemaphoreSlim[] _locks = [..Enumerable.Range(1, PerfectNumberConstants.RollingAccelerators).Select(_ => new SemaphoreSlim(1))]; - - internal static HeuristicGpuTables EnsureStaticTables(int acceleratorIndex, AcceleratorStream stream) - { - var @lock = _locks[acceleratorIndex]; - @lock.Wait(); - if (_sharedTables[acceleratorIndex] is {} existing) - { - @lock.Release(); - return existing; - } - - var accelerator = _accelerators[acceleratorIndex]; - existing = new(accelerator, stream); - _sharedTables[acceleratorIndex] = existing; - @lock.Release(); - return existing; - } - - internal HeuristicGpuTables(Accelerator accelerator, AcceleratorStream stream) - { - var heuristicGroupA = HeuristicPrimeSieves.GroupADivisorsStorage; - HeuristicGroupADivisors = CopySpanToDevice(accelerator, stream, heuristicGroupA); - - var heuristicGroupASquares = HeuristicPrimeSieves.GroupADivisorSquaresStorage; - HeuristicGroupADivisorSquares = CopySpanToDevice(accelerator, stream, heuristicGroupASquares); - - HeuristicGroupBDivisorsEnding1 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastOneWithoutLastThree); - HeuristicGroupBDivisorSquaresEnding1 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree); - - HeuristicGroupBDivisorsEnding7 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree); - HeuristicGroupBDivisorSquaresEnding7 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree); - - HeuristicGroupBDivisorsEnding9 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastNineWithoutLastThree); - HeuristicGroupBDivisorSquaresEnding9 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree); - - var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1; - HeuristicCombinedDivisorsEnding1 = CopySpanToDevice(accelerator, stream, combinedEnding1); - var combinedSquaresEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Squares; - HeuristicCombinedDivisorSquaresEnding1 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding1); - - var combinedEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3; - HeuristicCombinedDivisorsEnding3 = CopySpanToDevice(accelerator, stream, combinedEnding3); - var combinedSquaresEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Squares; - HeuristicCombinedDivisorSquaresEnding3 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding3); - - var combinedEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7; - HeuristicCombinedDivisorsEnding7 = CopySpanToDevice(accelerator, stream, combinedEnding7); - var combinedSquaresEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Squares; - HeuristicCombinedDivisorSquaresEnding7 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding7); - - var combinedEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9; - HeuristicCombinedDivisorsEnding9 = CopySpanToDevice(accelerator, stream, combinedEnding9); - var combinedSquaresEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Squares; - HeuristicCombinedDivisorSquaresEnding9 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding9); - } - - internal readonly MemoryBuffer1D HeuristicGroupADivisors; - internal readonly MemoryBuffer1D HeuristicGroupADivisorSquares; - internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding1; - internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding1; - internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding7; - internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding7; - internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding9; - internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding9; - internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding1; - internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding1; - internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding3; - internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding3; - internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding7; - internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding7; - internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding9; - internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9; - - internal HeuristicGpuDivisorTables CreateHeuristicDivisorTables() => new( - HeuristicCombinedDivisorsEnding1.View, - HeuristicCombinedDivisorsEnding3.View, - HeuristicCombinedDivisorsEnding7.View, - HeuristicCombinedDivisorsEnding9.View, - HeuristicCombinedDivisorSquaresEnding1.View, - HeuristicCombinedDivisorSquaresEnding3.View, - HeuristicCombinedDivisorSquaresEnding7.View, - HeuristicCombinedDivisorSquaresEnding9.View, - HeuristicGroupADivisors.View, - HeuristicGroupADivisorSquares.View, - HeuristicGroupBDivisorsEnding1.View, - HeuristicGroupBDivisorSquaresEnding1.View, - HeuristicGroupBDivisorsEnding7.View, - HeuristicGroupBDivisorSquaresEnding7.View, - HeuristicGroupBDivisorsEnding9.View, - HeuristicGroupBDivisorSquaresEnding9.View); - - private static MemoryBuffer1D CopySpanToDevice(Accelerator accelerator, AcceleratorStream stream, in ulong[] span) - { - MemoryBuffer1D? buffer; - buffer = accelerator.Allocate1D(stream, span); - - return buffer; - } - - private static MemoryBuffer1D CopyUintSpanToDevice(Accelerator accelerator, AcceleratorStream stream, ReadOnlySpan span) - { - MemoryBuffer1D? buffer; - buffer = accelerator.Allocate1D(span.Length); - - if (!span.IsEmpty) - { - var converted = new ulong[span.Length]; - for (int i = 0; i < span.Length; i++) - { - converted[i] = span[i]; - } - - buffer.View.CopyFromCPU(stream, converted); - } - - return buffer; - } - - internal void Dispose() - { - HeuristicGroupADivisors.Dispose(); - HeuristicGroupADivisorSquares.Dispose(); - HeuristicGroupBDivisorsEnding1.Dispose(); - HeuristicGroupBDivisorSquaresEnding1.Dispose(); - HeuristicGroupBDivisorsEnding7.Dispose(); - HeuristicGroupBDivisorSquaresEnding7.Dispose(); - HeuristicGroupBDivisorsEnding9.Dispose(); - HeuristicGroupBDivisorSquaresEnding9.Dispose(); - HeuristicCombinedDivisorsEnding1.Dispose(); - HeuristicCombinedDivisorSquaresEnding1.Dispose(); - HeuristicCombinedDivisorsEnding3.Dispose(); - HeuristicCombinedDivisorSquaresEnding3.Dispose(); - HeuristicCombinedDivisorsEnding7.Dispose(); - HeuristicCombinedDivisorSquaresEnding7.Dispose(); - HeuristicCombinedDivisorsEnding9.Dispose(); - HeuristicCombinedDivisorSquaresEnding9.Dispose(); - } -} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicPrimeTesterAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGroupABPrimeTesterAccelerator.cs similarity index 67% rename from PerfectNumbers.Core/Gpu/Accelerators/HeuristicPrimeTesterAccelerator.cs rename to PerfectNumbers.Core/Gpu/Accelerators/HeuristicGroupABPrimeTesterAccelerator.cs index de9e4b4..ec284a4 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicPrimeTesterAccelerator.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGroupABPrimeTesterAccelerator.cs @@ -1,15 +1,14 @@ -using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Runtime; namespace PerfectNumbers.Core.Gpu.Accelerators; -internal sealed class HeuristicPrimeTesterAccelerator +internal sealed class HeuristicGroupABPrimeTesterAccelerator { #region Pool [ThreadStatic] - private static Queue? Pool; + private static Queue? Pool; internal static void WarmUp() { @@ -20,23 +19,22 @@ internal sealed class HeuristicPrimeTesterAccelerator Console.WriteLine($"Preparing heuristic accelerator {i}..."); var accelerator = accelerators[i]; AcceleratorStream stream = accelerator.CreateStream(); - Accelerators.HeuristicGpuTables.EnsureStaticTables(i, stream); + _ = HeuristicGpuGroupABTables.EnsureStaticTables(i, stream); // _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None); // KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); stream.Synchronize(); stream.Dispose(); - accelerator.Synchronize(); Console.WriteLine($"Heuristic accelerator {i} is ready"); } } - internal static HeuristicPrimeTesterAccelerator Rent(int minBufferCapacity = 1) + internal static HeuristicGroupABPrimeTesterAccelerator Rent(int minBufferCapacity = 1) { var pool = Pool ??= new(); if (!pool.TryDequeue(out var lease)) { - lease = new HeuristicPrimeTesterAccelerator(minBufferCapacity); + lease = new HeuristicGroupABPrimeTesterAccelerator(minBufferCapacity); } else { @@ -46,7 +44,7 @@ internal sealed class HeuristicPrimeTesterAccelerator return lease; } - internal static void Return(HeuristicPrimeTesterAccelerator lease) + internal static void Return(HeuristicGroupABPrimeTesterAccelerator lease) { var pool = Pool ??= new(); pool.Enqueue(lease); @@ -67,7 +65,7 @@ internal sealed class HeuristicPrimeTesterAccelerator { if (Pool != null) { - var retained = new List(); + var retained = new List(); while (Pool.TryDequeue(out var lease)) { lease.DisposeResources(); @@ -77,10 +75,9 @@ internal sealed class HeuristicPrimeTesterAccelerator #endregion - private readonly HeuristicGpuDivisorTables _heuristicDivisorTables; + public readonly HeuristicGpuGroupABDivisorTables DivisorTables; internal readonly Context Context; public readonly Accelerator Accelerator; - public AcceleratorStream? Stream; [ThreadStatic] public static Dictionary, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>? SmallPrimeSieveKernel; @@ -100,12 +97,12 @@ internal sealed class HeuristicPrimeTesterAccelerator public readonly Kernel HeuristicTrialDivisionKernel; - public MemoryBuffer1D Input = null!; - public MemoryBuffer1D Output = null!; - public MemoryBuffer1D HeuristicFlag = null!; + public MemoryBuffer1D? Input = null; + public MemoryBuffer1D? Output = null; + public MemoryBuffer1D? HeuristicFlag = null!; public int BufferCapacity; - private HeuristicGpuTables _sharedTables; + private HeuristicGpuGroupABTables _sharedTables; public MemoryBuffer1D HeuristicGroupADivisors => _sharedTables.HeuristicGroupADivisors; public MemoryBuffer1D HeuristicGroupADivisorSquares => _sharedTables.HeuristicGroupADivisorSquares; public MemoryBuffer1D HeuristicGroupBDivisorsEnding1 => _sharedTables.HeuristicGroupBDivisorsEnding1; @@ -114,36 +111,28 @@ internal sealed class HeuristicPrimeTesterAccelerator public MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding7 => _sharedTables.HeuristicGroupBDivisorSquaresEnding7; public MemoryBuffer1D HeuristicGroupBDivisorsEnding9 => _sharedTables.HeuristicGroupBDivisorsEnding9; public MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding9 => _sharedTables.HeuristicGroupBDivisorSquaresEnding9; - public MemoryBuffer1D HeuristicCombinedDivisorsEnding1 => _sharedTables.HeuristicCombinedDivisorsEnding1; - public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding1 => _sharedTables.HeuristicCombinedDivisorSquaresEnding1; - public MemoryBuffer1D HeuristicCombinedDivisorsEnding3 => _sharedTables.HeuristicCombinedDivisorsEnding3; - public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding3 => _sharedTables.HeuristicCombinedDivisorSquaresEnding3; - public MemoryBuffer1D HeuristicCombinedDivisorsEnding7 => _sharedTables.HeuristicCombinedDivisorsEnding7; - public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding7 => _sharedTables.HeuristicCombinedDivisorSquaresEnding7; - public MemoryBuffer1D HeuristicCombinedDivisorsEnding9 => _sharedTables.HeuristicCombinedDivisorsEnding9; - public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9 => _sharedTables.HeuristicCombinedDivisorSquaresEnding9; - - public HeuristicGpuDivisorTables HeuristicGpuTables => _heuristicDivisorTables; private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; + internal readonly int AcceleratorIndex; - internal HeuristicPrimeTesterAccelerator(int minBufferCapacity) + internal HeuristicGroupABPrimeTesterAccelerator(int minBufferCapacity) { var acceleratorIndex = AcceleratorPool.Shared.Rent(); + AcceleratorIndex = acceleratorIndex; Accelerator accelerator = _accelerators[acceleratorIndex]; Context = accelerator.Context; Accelerator = accelerator; EnsureCapacity(minBufferCapacity); - HeuristicTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel)); + HeuristicTrialDivisionKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuGroupABDivisorTables>(PrimeTesterKernels.HeuristicTrialGroupABDivisionKernel)); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); - _sharedTables = Accelerators.HeuristicGpuTables.EnsureStaticTables(acceleratorIndex, stream); - _heuristicDivisorTables = _sharedTables.CreateHeuristicDivisorTables(); + _sharedTables = HeuristicGpuGroupABTables.EnsureStaticTables(acceleratorIndex, stream); + DivisorTables = _sharedTables.CreateHeuristicDivisorTables(); stream.Synchronize(); AcceleratorStreamPool.Return(acceleratorIndex, stream); @@ -156,8 +145,8 @@ internal sealed class HeuristicPrimeTesterAccelerator return; } - Input.Dispose(); - Output.Dispose(); + Input?.Dispose(); + Output?.Dispose(); HeuristicFlag?.Dispose(); Input = Accelerator.Allocate1D(minCapacity); @@ -169,8 +158,6 @@ internal sealed class HeuristicPrimeTesterAccelerator public void Dispose() { - Stream?.Dispose(); - Stream = null; Return(this); } @@ -179,8 +166,6 @@ internal sealed class HeuristicPrimeTesterAccelerator Input?.Dispose(); Output?.Dispose(); HeuristicFlag?.Dispose(); - Stream?.Dispose(); - Stream = null; // ReleaseSharedTables(_sharedTables); // These resources are shared between GPU leases // Accelerator.Dispose(); diff --git a/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs index 35c0369..0f2ba4e 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs @@ -68,6 +68,7 @@ public sealed class PrimeOrderCalculatorAccelerator // Don't take this from the pool as quick uploads of data to the accelerator consumes much of GPU's memory and throws. AcceleratorStream stream = accelerator.CreateStream(); LastDigitGpuTables.WarmUp(i, stream); + HeuristicCombinedPrimeTesterAccelerator.WarmUp(i, stream); // SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream); // _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None); // KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); @@ -76,6 +77,7 @@ public sealed class PrimeOrderCalculatorAccelerator stream.Dispose(); accelerator.Synchronize(); } + } #endregion diff --git a/PerfectNumbers.Core/Gpu/HeuristicGpuCombinedDivisorTables.cs b/PerfectNumbers.Core/Gpu/HeuristicGpuCombinedDivisorTables.cs new file mode 100644 index 0000000..d8f0f3c --- /dev/null +++ b/PerfectNumbers.Core/Gpu/HeuristicGpuCombinedDivisorTables.cs @@ -0,0 +1,45 @@ +using System.Runtime.CompilerServices; +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu; + +public readonly struct HeuristicGpuCombinedDivisorTables( + ArrayView1D combinedDivisorsEnding1, + ArrayView1D combinedDivisorsEnding3, + ArrayView1D combinedDivisorsEnding7, + ArrayView1D combinedDivisorsEnding9, + ArrayView1D combinedDivisorSquaresEnding1, + ArrayView1D combinedDivisorSquaresEnding3, + ArrayView1D combinedDivisorSquaresEnding7, + ArrayView1D combinedDivisorSquaresEnding9) +{ + public readonly ArrayView1D CombinedDivisorsEnding1 = combinedDivisorsEnding1; + public readonly ArrayView1D CombinedDivisorsEnding3 = combinedDivisorsEnding3; + public readonly ArrayView1D CombinedDivisorsEnding7 = combinedDivisorsEnding7; + public readonly ArrayView1D CombinedDivisorsEnding9 = combinedDivisorsEnding9; + public readonly ArrayView1D CombinedDivisorSquaresEnding1 = combinedDivisorSquaresEnding1; + public readonly ArrayView1D CombinedDivisorSquaresEnding3 = combinedDivisorSquaresEnding3; + public readonly ArrayView1D CombinedDivisorSquaresEnding7 = combinedDivisorSquaresEnding7; + public readonly ArrayView1D CombinedDivisorSquaresEnding9 = combinedDivisorSquaresEnding9; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayView1D SelectDivisors(byte ending) => ending switch + { + 1 => CombinedDivisorsEnding1, + 3 => CombinedDivisorsEnding3, + 7 => CombinedDivisorsEnding7, + 9 => CombinedDivisorsEnding9, + _ => ArrayView1D.Empty, + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayView1D SelectDivisorSquares(byte ending) => ending switch + { + 1 => CombinedDivisorSquaresEnding1, + 3 => CombinedDivisorSquaresEnding3, + 7 => CombinedDivisorSquaresEnding7, + 9 => CombinedDivisorSquaresEnding9, + _ => ArrayView1D.Empty, + }; +} diff --git a/PerfectNumbers.Core/Gpu/HeuristicGpuDivisorTables.cs b/PerfectNumbers.Core/Gpu/HeuristicGpuDivisorTables.cs deleted file mode 100644 index 6322bec..0000000 --- a/PerfectNumbers.Core/Gpu/HeuristicGpuDivisorTables.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System.Runtime.CompilerServices; -using ILGPU; -using ILGPU.Runtime; - -namespace PerfectNumbers.Core.Gpu; - -public readonly struct HeuristicGpuDivisorTables -{ - public readonly ArrayView1D CombinedDivisorsEnding1; - public readonly ArrayView1D CombinedDivisorsEnding3; - public readonly ArrayView1D CombinedDivisorsEnding7; - public readonly ArrayView1D CombinedDivisorsEnding9; - public readonly ArrayView1D CombinedDivisorSquaresEnding1; - public readonly ArrayView1D CombinedDivisorSquaresEnding3; - public readonly ArrayView1D CombinedDivisorSquaresEnding7; - public readonly ArrayView1D CombinedDivisorSquaresEnding9; - public readonly ArrayView1D GroupADivisors; - public readonly ArrayView1D GroupADivisorSquares; - public readonly ArrayView1D GroupBDivisorsEnding1; - public readonly ArrayView1D GroupBDivisorSquaresEnding1; - public readonly ArrayView1D GroupBDivisorsEnding7; - public readonly ArrayView1D GroupBDivisorSquaresEnding7; - public readonly ArrayView1D GroupBDivisorsEnding9; - public readonly ArrayView1D GroupBDivisorSquaresEnding9; - - public HeuristicGpuDivisorTables( - ArrayView1D combinedDivisorsEnding1, - ArrayView1D combinedDivisorsEnding3, - ArrayView1D combinedDivisorsEnding7, - ArrayView1D combinedDivisorsEnding9, - ArrayView1D combinedDivisorSquaresEnding1, - ArrayView1D combinedDivisorSquaresEnding3, - ArrayView1D combinedDivisorSquaresEnding7, - ArrayView1D combinedDivisorSquaresEnding9, - ArrayView1D groupADivisors, - ArrayView1D groupADivisorSquares, - ArrayView1D groupBDivisorsEnding1, - ArrayView1D groupBDivisorSquaresEnding1, - ArrayView1D groupBDivisorsEnding7, - ArrayView1D groupBDivisorSquaresEnding7, - ArrayView1D groupBDivisorsEnding9, - ArrayView1D groupBDivisorSquaresEnding9) - { - CombinedDivisorsEnding1 = combinedDivisorsEnding1; - CombinedDivisorsEnding3 = combinedDivisorsEnding3; - CombinedDivisorsEnding7 = combinedDivisorsEnding7; - CombinedDivisorsEnding9 = combinedDivisorsEnding9; - CombinedDivisorSquaresEnding1 = combinedDivisorSquaresEnding1; - CombinedDivisorSquaresEnding3 = combinedDivisorSquaresEnding3; - CombinedDivisorSquaresEnding7 = combinedDivisorSquaresEnding7; - CombinedDivisorSquaresEnding9 = combinedDivisorSquaresEnding9; - GroupADivisors = groupADivisors; - GroupADivisorSquares = groupADivisorSquares; - GroupBDivisorsEnding1 = groupBDivisorsEnding1; - GroupBDivisorSquaresEnding1 = groupBDivisorSquaresEnding1; - GroupBDivisorsEnding7 = groupBDivisorsEnding7; - GroupBDivisorSquaresEnding7 = groupBDivisorSquaresEnding7; - GroupBDivisorsEnding9 = groupBDivisorsEnding9; - GroupBDivisorSquaresEnding9 = groupBDivisorSquaresEnding9; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayView1D SelectCombined(byte ending) - { - return ending switch - { - 1 => CombinedDivisorsEnding1, - 3 => CombinedDivisorsEnding3, - 7 => CombinedDivisorsEnding7, - 9 => CombinedDivisorsEnding9, - _ => ArrayView1D.Empty, - }; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayView1D SelectCombinedSquares(byte ending) - { - return ending switch - { - 1 => CombinedDivisorSquaresEnding1, - 3 => CombinedDivisorSquaresEnding3, - 7 => CombinedDivisorSquaresEnding7, - 9 => CombinedDivisorSquaresEnding9, - _ => ArrayView1D.Empty, - }; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayView1D SelectGroupB(byte ending) - { - return ending switch - { - 1 => GroupBDivisorsEnding1, - 7 => GroupBDivisorsEnding7, - 9 => GroupBDivisorsEnding9, - _ => ArrayView1D.Empty, - }; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayView1D SelectGroupBSquares(byte ending) - { - return ending switch - { - 1 => GroupBDivisorSquaresEnding1, - 7 => GroupBDivisorSquaresEnding7, - 9 => GroupBDivisorSquaresEnding9, - _ => ArrayView1D.Empty, - }; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayView1D SelectDivisors(HeuristicGpuDivisorTableKind kind, byte ending) - { - return kind switch - { - HeuristicGpuDivisorTableKind.GroupA => GroupADivisors, - HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1, - HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7, - HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorsEnding9, - HeuristicGpuDivisorTableKind.Combined => SelectCombined(ending), - _ => ArrayView1D.Empty, - }; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayView1D SelectDivisorSquares(HeuristicGpuDivisorTableKind kind, byte ending) - { - return kind switch - { - HeuristicGpuDivisorTableKind.GroupA => GroupADivisorSquares, - HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorSquaresEnding1, - HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorSquaresEnding7, - HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorSquaresEnding9, - HeuristicGpuDivisorTableKind.Combined => SelectCombinedSquares(ending), - _ => ArrayView1D.Empty, - }; - } -} diff --git a/PerfectNumbers.Core/Gpu/HeuristicGpuGroupABDivisorTables.cs b/PerfectNumbers.Core/Gpu/HeuristicGpuGroupABDivisorTables.cs new file mode 100644 index 0000000..de01098 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/HeuristicGpuGroupABDivisorTables.cs @@ -0,0 +1,63 @@ +using System.Runtime.CompilerServices; +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu; + +public readonly struct HeuristicGpuGroupABDivisorTables( + ArrayView1D groupADivisors, + ArrayView1D groupADivisorSquares, + ArrayView1D groupBDivisorsEnding1, + ArrayView1D groupBDivisorSquaresEnding1, + ArrayView1D groupBDivisorsEnding7, + ArrayView1D groupBDivisorSquaresEnding7, + ArrayView1D groupBDivisorsEnding9, + ArrayView1D groupBDivisorSquaresEnding9) +{ + public readonly ArrayView1D GroupADivisors = groupADivisors; + public readonly ArrayView1D GroupADivisorSquares = groupADivisorSquares; + public readonly ArrayView1D GroupBDivisorsEnding1 = groupBDivisorsEnding1; + public readonly ArrayView1D GroupBDivisorSquaresEnding1 = groupBDivisorSquaresEnding1; + public readonly ArrayView1D GroupBDivisorsEnding7 = groupBDivisorsEnding7; + public readonly ArrayView1D GroupBDivisorSquaresEnding7 = groupBDivisorSquaresEnding7; + public readonly ArrayView1D GroupBDivisorsEnding9 = groupBDivisorsEnding9; + public readonly ArrayView1D GroupBDivisorSquaresEnding9 = groupBDivisorSquaresEnding9; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayView1D SelectGroupB(byte ending) => ending switch + { + 1 => GroupBDivisorsEnding1, + 7 => GroupBDivisorsEnding7, + 9 => GroupBDivisorsEnding9, + _ => ArrayView1D.Empty, + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayView1D SelectGroupBSquares(byte ending) => ending switch + { + 1 => GroupBDivisorSquaresEnding1, + 7 => GroupBDivisorSquaresEnding7, + 9 => GroupBDivisorSquaresEnding9, + _ => ArrayView1D.Empty, + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayView1D SelectDivisors(HeuristicGpuDivisorTableKind kind) => kind switch + { + HeuristicGpuDivisorTableKind.GroupA => GroupADivisors, + HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorsEnding1, + HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorsEnding7, + HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorsEnding9, + _ => ArrayView1D.Empty, + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayView1D SelectDivisorSquares(HeuristicGpuDivisorTableKind kind) => kind switch + { + HeuristicGpuDivisorTableKind.GroupA => GroupADivisorSquares, + HeuristicGpuDivisorTableKind.GroupBEnding1 => GroupBDivisorSquaresEnding1, + HeuristicGpuDivisorTableKind.GroupBEnding7 => GroupBDivisorSquaresEnding7, + HeuristicGpuDivisorTableKind.GroupBEnding9 => GroupBDivisorSquaresEnding9, + _ => ArrayView1D.Empty, + }; +} diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs index efe1b82..1547b8b 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs @@ -11,7 +11,6 @@ public enum HeuristicGpuDivisorTableKind : byte GroupBEnding1 = 1, GroupBEnding7 = 7, GroupBEnding9 = 9, - Combined = 255, } internal static class PrimeTesterKernels @@ -78,17 +77,49 @@ internal static class PrimeTesterKernels results[index] = result; } - public static void HeuristicTrialDivisionKernel( + public static void HeuristicTrialCombinedDivisionKernel( + Index1D index, + byte nMod10, + ArrayView resultFlag, + ulong n, + ulong maxDivisorSquare, + HeuristicGpuCombinedDivisorTables tables) + { + // byte nMod10 = (byte)(n % 10UL); + ArrayView1D divisors = tables.SelectDivisors(nMod10); + ArrayView1D divisorSquares = tables.SelectDivisorSquares(nMod10); + + int divisorLength = (int)divisorSquares.Length; + int threadIndex = index; + if (threadIndex >= divisorLength) + { + return; + } + + ulong divisorSquare = divisorSquares[threadIndex]; + if (divisorSquare > maxDivisorSquare) + { + return; + } + + ulong divisor = divisors[threadIndex]; + divisor = n % divisor; + if (divisor == 0UL) + { + resultFlag[0] = 1; + } + } + + public static void HeuristicTrialGroupABDivisionKernel( Index1D index, ArrayView resultFlag, ulong n, ulong maxDivisorSquare, HeuristicGpuDivisorTableKind tableKind, - HeuristicGpuDivisorTables tables) + HeuristicGpuGroupABDivisorTables tables) { - byte nMod10 = (byte)(n % 10UL); - ArrayView1D divisors = tables.SelectDivisors(tableKind, nMod10); - ArrayView1D divisorSquares = tables.SelectDivisorSquares(tableKind, nMod10); + ArrayView1D divisors = tables.SelectDivisors(tableKind); + ArrayView1D divisorSquares = tables.SelectDivisorSquares(tableKind); int divisorLength = (int)divisorSquares.Length; int threadIndex = index; diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index b0eb10b..e90ba30 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -8,6 +8,7 @@ using System.Threading; using PerfectNumbers.Core; using ILGPU; using ILGPU.Runtime; +using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core.Gpu; @@ -106,9 +107,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv ulong lastProcessed; // GpuPrimeWorkLimiter(); - int acceleratorIndex = AcceleratorPool.Shared.Rent(); - var accelerator = _accelerators[acceleratorIndex]; - var stream = accelerator.CreateStream(); + // int acceleratorIndex = AcceleratorPool.Shared.Rent(); + // var accelerator = _accelerators[acceleratorIndex]; + var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(batchCapacity); + var acceleratorIndex = gpu.AcceleratorIndex; + var accelerator = gpu.Accelerator; + var stream = AcceleratorStreamPool.Rent(acceleratorIndex); // Monitor.Enter(gpuLease.ExecutionLock); @@ -116,6 +120,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv var resources = RentBatchResources(accelerator, batchCapacity); composite = CheckDivisors( + gpu, prime, allowedMax, kernel, @@ -137,8 +142,9 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv out coveredRange, out processedCount); - stream.Dispose(); + AcceleratorStreamPool.Return(acceleratorIndex, stream); ReturnBatchResources(resources); + HeuristicCombinedPrimeTesterAccelerator.Return(gpu); // Monitor.Exit(gpuLease.ExecutionLock); // GpuPrimeWorkLimiter.Release(); @@ -182,6 +188,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv } private static bool CheckDivisors( + HeuristicCombinedPrimeTesterAccelerator gpu, ulong prime, ulong allowedMax, Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> kernel, @@ -205,7 +212,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv { int batchCapacity = (int)divisorDataBuffer.Length; bool composite = false; - bool processedAll = false; + bool processedAll; processedCount = 0UL; lastProcessed = 0UL; @@ -303,7 +310,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv { ulong divisorValue = (ulong)nextDivisor128; MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue); - ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, in montgomeryData); + ulong divisorCycle = ResolveDivisorCycle(gpu, divisorValue, prime, in montgomeryData); if (divisorCycle == prime) { processedCount += (ulong)(i + 1); @@ -375,9 +382,9 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong ResolveDivisorCycle(ulong divisor, ulong prime, in MontgomeryDivisorData divisorData) + private static ulong ResolveDivisorCycle(HeuristicCombinedPrimeTesterAccelerator gpu, ulong divisor, ulong prime, in MontgomeryDivisorData divisorData) { - if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) + if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) { return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); } @@ -498,6 +505,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; private readonly MersenneNumberDivisorByDivisorGpuTester _owner; + private readonly HeuristicCombinedPrimeTesterAccelerator _primeTesterAccelerator; private readonly Accelerator _accelerator; private readonly AcceleratorStream _stream; private Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> _kernel = null!; @@ -516,8 +524,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv { _owner = owner; // GpuPrimeWorkLimiter.Acquire(); - int acceleratorIndex = AcceleratorPool.Shared.Rent(); - Accelerator accelerator = _accelerators[acceleratorIndex]; + _primeTesterAccelerator = HeuristicCombinedPrimeTesterAccelerator.Rent(); + Accelerator accelerator = _primeTesterAccelerator.Accelerator; _accelerator = accelerator; _stream = accelerator.CreateStream(); _capacity = Math.Max(1, owner._gpuBatchSize); @@ -600,7 +608,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv if (cycle == 0UL) { - if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) + var gpu = _primeTesterAccelerator; + if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(gpu, divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) { cycle = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); } @@ -657,10 +666,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv ref byte hitRef = ref MemoryMarshal.GetReference(hitSlice); hitView.CopyToCPU(stream, ref hitRef, length); stream.Synchronize(); + // Monitor.Exit(_lease.ExecutionLock); } - public void Dispose() + public void Return() { _owner._sessionPool.Add(this); } diff --git a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs index 5c14b30..a78e78d 100644 --- a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs @@ -3,6 +3,7 @@ using ILGPU.Runtime; using System.Runtime.CompilerServices; using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu.Accelerators; +using ILGPU; namespace PerfectNumbers.Core; @@ -172,10 +173,20 @@ public sealed class HeuristicCombinedPrimeTester { byte nMod10 = (byte)n.Mod10(); ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n); - return IsPrimeGpu(n, maxDivisorSquare, nMod10); + var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1); + bool isPrime = IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10); + HeuristicCombinedPrimeTesterAccelerator.Return(gpu); + return isPrime; } - public static bool IsPrimeGpu(ulong n, ulong maxDivisorSquare, byte nMod10) + public static bool IsPrimeGpu(HeuristicCombinedPrimeTesterAccelerator gpu, ulong n) + { + byte nMod10 = (byte)n.Mod10(); + ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n); + return IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10); + } + + public static bool IsPrimeGpu(HeuristicCombinedPrimeTesterAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10) { // TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner? if (maxDivisorSquare < 9UL) @@ -183,7 +194,7 @@ public sealed class HeuristicCombinedPrimeTester return EvaluateWithOpenNumericFallback(n); } - bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(n, maxDivisorSquare, nMod10); + bool compositeDetected = HeuristicTrialDivisionGpuDetectsDivisor(gpu, n, maxDivisorSquare, nMod10); return !compositeDetected; } @@ -222,40 +233,38 @@ public sealed class HeuristicCombinedPrimeTester }; - private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10) + private static bool HeuristicTrialDivisionGpuDetectsDivisor(HeuristicCombinedPrimeTesterAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10) { // GpuPrimeWorkLimiter.Acquire(); - var gpu = HeuristicPrimeTesterAccelerator.Rent(1); - var accelerator = gpu.Accelerator; - var stream = gpu.Stream; - var kernel = gpu.HeuristicTrialDivisionKernel; - var flagView1D = gpu.HeuristicFlag.View; - var flagView = flagView1D.AsContiguous(); + // var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1); + var acceleratorIndex = gpu.AcceleratorIndex; + var stream = AcceleratorStreamPool.Rent(acceleratorIndex); + var kernel = gpu.HeuristicCombinedTrialDivisionKernel; + var flagView1D = gpu.HeuristicFlag!.View; bool compositeDetected = false; int divisorLength = GetCombinedDivisors(nMod10).Length; int compositeFlag = 0; flagView1D.CopyFromCPU(stream, ref compositeFlag, 1); - stream!.Synchronize(); - - kernel.Launch( + var kernelLauncher = kernel.CreateLauncherDelegate, ulong, ulong, HeuristicGpuCombinedDivisorTables>>(); + kernelLauncher( stream, 1, - divisorLength, - flagView, + nMod10, + flagView1D, n, maxDivisorSquare, - HeuristicGpuDivisorTableKind.Combined, - gpu.HeuristicGpuTables); + gpu.DivisorTables); flagView1D.CopyToCPU(stream, ref compositeFlag, 1); stream.Synchronize(); + AcceleratorStreamPool.Return(acceleratorIndex, stream); // GpuPrimeWorkLimiter.Release(); compositeDetected = compositeFlag != 0; - HeuristicPrimeTesterAccelerator.Return(gpu); + HeuristicCombinedPrimeTesterAccelerator.Return(gpu); return compositeDetected; } @@ -490,6 +499,7 @@ public sealed class HeuristicCombinedPrimeTester [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ulong ResolveHeuristicCycleLength( + HeuristicCombinedPrimeTesterAccelerator gpu, ulong exponent, in HeuristicDivisorPreparation preparation, out bool cycleFromHint, @@ -508,6 +518,7 @@ public sealed class HeuristicCombinedPrimeTester MontgomeryDivisorData divisorData = preparation.DivisorData; bool trySuccess = MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + gpu, divisor, exponent, divisorData, diff --git a/PerfectNumbers.Core/HeuristicPrimeTester.cs b/PerfectNumbers.Core/HeuristicPrimeTester.cs index 29865e1..e0c3bde 100644 --- a/PerfectNumbers.Core/HeuristicPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicPrimeTester.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu.Accelerators; +using ILGPU; namespace PerfectNumbers.Core; @@ -242,72 +243,80 @@ public sealed class HeuristicPrimeTester private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10) { // GpuPrimeWorkLimiter.Acquire(); - var gpu = HeuristicPrimeTesterAccelerator.Rent(1); - var stream = gpu.Stream; - var kernel = gpu.HeuristicTrialDivisionKernel; - var flagView1D = gpu.HeuristicFlag.View; - var flagView = flagView1D.AsContiguous(); + var gpu = HeuristicCombinedPrimeTesterAccelerator.Rent(1); + int acceleratorIndex = gpu.AcceleratorIndex; + var stream = AcceleratorStreamPool.Rent(acceleratorIndex); + var kernel = gpu.HeuristicCombinedTrialDivisionKernel; + var flagView1D = gpu.HeuristicFlag!.View; - bool compositeDetected = false; + bool compositeDetected; int compositeFlag = 0; - int groupALength = HeuristicPrimeSieves.GroupADivisors.Length; - flagView1D.CopyFromCPU(stream, ref compositeFlag, 1); - kernel.Launch( - stream!, + + // Index1D index, + // ArrayView resultFlag, + // ulong n, + // ulong maxDivisorSquare, + // HeuristicGpuDivisorTableKind tableKind, + // HeuristicGpuDivisorTables tables) + + var kernelLauncher = kernel.CreateLauncherDelegate, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuCombinedDivisorTables>>(); + kernelLauncher( + stream, 1, - groupALength, - flagView, + nMod10, + flagView1D, n, maxDivisorSquare, HeuristicGpuDivisorTableKind.GroupA, - gpu.HeuristicGpuTables); + gpu.DivisorTables); flagView1D.CopyToCPU(stream, ref compositeFlag, 1); stream!.Synchronize(); compositeDetected = compositeFlag != 0; + // compositeDetected = compositeFlag != 0; - if (!compositeDetected) - { - ReadOnlySpan endingOrder = GetGroupBEndingOrder(nMod10); - for (int i = 0; i < endingOrder.Length && !compositeDetected; i++) - { - byte ending = endingOrder[i]; - HeuristicGpuDivisorTableKind tableKind = ending switch - { - 1 => HeuristicGpuDivisorTableKind.GroupBEnding1, - 7 => HeuristicGpuDivisorTableKind.GroupBEnding7, - 9 => HeuristicGpuDivisorTableKind.GroupBEnding9, - _ => HeuristicGpuDivisorTableKind.GroupA, - }; + // if (!compositeDetected) + // { + // ReadOnlySpan endingOrder = GetGroupBEndingOrder(nMod10); + // for (int i = 0; i < endingOrder.Length && !compositeDetected; i++) + // { + // byte ending = endingOrder[i]; + // HeuristicGpuDivisorTableKind tableKind = ending switch + // { + // 1 => HeuristicGpuDivisorTableKind.GroupBEnding1, + // 7 => HeuristicGpuDivisorTableKind.GroupBEnding7, + // 9 => HeuristicGpuDivisorTableKind.GroupBEnding9, + // _ => HeuristicGpuDivisorTableKind.GroupA, + // }; - if (tableKind == HeuristicGpuDivisorTableKind.GroupA) - { - continue; - } + // if (tableKind == HeuristicGpuDivisorTableKind.GroupA) + // { + // continue; + // } - int divisorLength = GetGroupBDivisors(ending).Length; + // int divisorLength = GetGroupBDivisors(ending).Length; - compositeFlag = 0; - flagView1D.CopyFromCPU(stream, ref compositeFlag, 1); - kernel.Launch( - stream, - 1, - divisorLength, - flagView, - n, - maxDivisorSquare, - tableKind, - gpu.HeuristicGpuTables); - flagView1D.CopyToCPU(stream, ref compositeFlag, 1); - stream.Synchronize(); - compositeDetected = compositeFlag != 0; - } - } + // compositeFlag = 0; + // flagView1D.CopyFromCPU(stream, ref compositeFlag, 1); + // kernelLauncher( + // stream, + // 1, + // flagView1D, + // n, + // maxDivisorSquare, + // tableKind, + // gpu.HeuristicGpuTables); + // flagView1D.CopyToCPU(stream, ref compositeFlag, 1); + // stream.Synchronize(); + // compositeDetected = compositeFlag != 0; + // } + // } - gpu.Dispose(); + AcceleratorStreamPool.Return(acceleratorIndex, stream); + gpu.Return(); // GpuPrimeWorkLimiter.Release(); return compositeDetected; } @@ -457,6 +466,7 @@ public sealed class HeuristicPrimeTester [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ulong ResolveHeuristicCycleLength( + HeuristicCombinedPrimeTesterAccelerator gpu, ulong exponent, in HeuristicDivisorPreparation preparation, out bool cycleFromHint, @@ -475,6 +485,7 @@ public sealed class HeuristicPrimeTester MontgomeryDivisorData divisorData = preparation.DivisorData; bool trySuccess = MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + gpu, divisor, exponent, divisorData, diff --git a/PerfectNumbers.Core/IMersenneNumberDivisorByDivisorTester.cs b/PerfectNumbers.Core/IMersenneNumberDivisorByDivisorTester.cs index 43be5dc..e72255f 100644 --- a/PerfectNumbers.Core/IMersenneNumberDivisorByDivisorTester.cs +++ b/PerfectNumbers.Core/IMersenneNumberDivisorByDivisorTester.cs @@ -20,6 +20,6 @@ public interface IMersenneNumberDivisorByDivisorTester { void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan primes, Span hits); - void Dispose(); + void Return(); } } diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index 271b56f..389ed8f 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -52,7 +52,7 @@ public class MersenneDivisorCycles return _smallCycles!; } - public static bool CycleEqualsExponent(ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent) + public static bool CycleEqualsExponent(HeuristicCombinedPrimeTesterAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent) { if (CycleEqualsExponentForMersenneCandidate(divisor, divisorData, exponent)) { @@ -82,7 +82,7 @@ public class MersenneDivisorCycles return heuristicCycle == exponent; } - if (TryCalculateCycleLengthForExponentCpu(divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL) + if (TryCalculateCycleLengthForExponentCpu(gpu, divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL) { return cycleLength == exponent; } @@ -260,6 +260,7 @@ public class MersenneDivisorCycles } public static bool TryCalculateCycleLengthForExponentCpu( + HeuristicCombinedPrimeTesterAccelerator gpu, ulong divisor, ulong exponent, in MontgomeryDivisorData divisorData, @@ -328,8 +329,8 @@ public class MersenneDivisorCycles factorCounts[2UL] = twoCount; } - bool exponentAccumulated = AccumulateFactors(exponent, factorCounts); - bool factorsAccumulated = exponentAccumulated && AccumulateFactors(k, factorCounts); + bool exponentAccumulated = AccumulateFactors(gpu, exponent, factorCounts); + bool factorsAccumulated = exponentAccumulated && AccumulateFactors(gpu, k, factorCounts); if (factorsAccumulated) { @@ -344,6 +345,7 @@ public class MersenneDivisorCycles } private static bool AccumulateFactors( + HeuristicCombinedPrimeTesterAccelerator gpu, ulong value, Dictionary counts) { @@ -353,7 +355,7 @@ public class MersenneDivisorCycles } Dictionary scratch = ThreadStaticPools.RentFactorScratchDictionary(); - bool success = TryFactorIntoCountsInternal(value, scratch); + bool success = TryFactorIntoCountsInternal(gpu, value, scratch); if (!success) { scratch.Clear(); @@ -375,7 +377,7 @@ public class MersenneDivisorCycles // private static ulong _tryFactorIntoCountsInternalHits; // private static ulong _tryCalculateCycleLengthHeuristicHits; - private static bool TryFactorIntoCountsInternal(ulong value, Dictionary counts) + private static bool TryFactorIntoCountsInternal(HeuristicCombinedPrimeTesterAccelerator gpu, ulong value, Dictionary counts) { // The EvenPerfectBitScanner only requests factors for values >= 2 here. Leave the guard documented without executing it. // if (value <= 1UL) @@ -412,7 +414,7 @@ public class MersenneDivisorCycles // Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}"); // HeuristicPrimeTester primeTester = _primeTester ??= new(); - if (PrimeTester.IsPrimeGpu(remaining)) + if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining)) // if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None)) { AddFactor(counts, remaining); @@ -437,13 +439,13 @@ public class MersenneDivisorCycles // return false; // } - if (!TryFactorIntoCountsInternal(factor, counts)) + if (!TryFactorIntoCountsInternal(gpu, factor, counts)) { return false; } // Guard the recursive calls to terminate once Pollard-Rho reduces either branch to 1. - if (quotient > 1UL && !TryFactorIntoCountsInternal(quotient, counts)) + if (quotient > 1UL && !TryFactorIntoCountsInternal(gpu, quotient, counts)) { return false; } @@ -958,7 +960,7 @@ public class MersenneDivisorCycles // HeuristicPrimeTester primeTester = _primeTester ??= new(); // if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None)) // if (!skipPrimeOrderHeuristic && primeTester.IsPrimeGpu(divisor)) - if (!skipPrimeOrderHeuristic && PrimeTester.IsPrimeGpu(divisor)) + if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor)) { ulong computedOrder = PrimeOrderCalculator.Calculate( divisor, diff --git a/PerfectNumbers.Core/PerfectNumberConstants.cs b/PerfectNumbers.Core/PerfectNumberConstants.cs index ef99aff..6eae9c8 100644 --- a/PerfectNumbers.Core/PerfectNumberConstants.cs +++ b/PerfectNumbers.Core/PerfectNumberConstants.cs @@ -21,3 +21,4 @@ public static class PerfectNumberConstants // TODO: Promote these magic numbers into a runtime profile derived from EvenPerfectBitScanner.Benchmarks so we can retune // them automatically when the optimal ranges for 2kp+1 >= 138M change. } + \ No newline at end of file diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 0dabed6..7123564 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using ILGPU; using PerfectNumbers.Core.Cpu; using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu.Accelerators; @@ -54,34 +55,39 @@ internal static partial class PrimeOrderCalculator ulong phi = prime - 1UL; PrimeOrderCalculatorAccelerator gpu = PrimeOrderCalculatorAccelerator.Rent(1); + HeuristicCombinedPrimeTesterAccelerator gpu2 = HeuristicCombinedPrimeTesterAccelerator.Rent(1); if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out ulong gpuOrder)) { PrimeOrderCalculatorAccelerator.Return(gpu); + HeuristicCombinedPrimeTesterAccelerator.Return(gpu2); return gpuOrder; } - PartialFactorResult phiFactors = PartialFactor(gpu, phi, config); + PartialFactorResult phiFactors = PartialFactor(gpu, gpu2, phi, config); ulong result; if (phiFactors.Factors is null) { result = prime <= PerfectNumberConstants.MaxQForDivisorCycles - ? CalculateByFactorizationGpu(gpu, prime, divisorData) - : CalculateByFactorizationCpu(gpu, prime, divisorData); + ? CalculateByFactorizationGpu(gpu, gpu2, prime, divisorData) + : CalculateByFactorizationCpu(gpu, gpu2, prime, divisorData); phiFactors.Dispose(); PrimeOrderCalculatorAccelerator.Return(gpu); + HeuristicCombinedPrimeTesterAccelerator.Return(gpu2); return result; } - result = RunHeuristicPipelineCpu(gpu, prime, previousOrder, config, divisorData, phi, phiFactors); + result = RunHeuristicPipelineCpu(gpu, gpu2, prime, previousOrder, config, divisorData, phi, phiFactors); phiFactors.Dispose(); PrimeOrderCalculatorAccelerator.Return(gpu); + HeuristicCombinedPrimeTesterAccelerator.Return(gpu2); return result; } private static ulong RunHeuristicPipelineCpu( PrimeOrderCalculatorAccelerator gpu, + HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, ulong? previousOrder, in PrimeOrderSearchConfig config, @@ -98,7 +104,7 @@ internal static partial class PrimeOrderCalculator ulong candidateOrder = InitializeStartingOrderCpu(gpu, prime, phi, divisorData); candidateOrder = ExponentLoweringCpu(candidateOrder, prime, phiFactors, divisorData); - if (TryConfirmOrderCpu(gpu, prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors)) + if (TryConfirmOrderCpu(gpu, gpu2, prime, candidateOrder, divisorData, config, out PartialFactorResult? orderFactors)) { // GpuPrimeWorkLimiter.Release(); return candidateOrder; @@ -108,10 +114,10 @@ internal static partial class PrimeOrderCalculator { orderFactors?.Dispose(); // GpuPrimeWorkLimiter.Release(); - return CalculateByFactorizationGpu(gpu, prime, divisorData); + return CalculateByFactorizationGpu(gpu, gpu2, prime, divisorData); } - if (TryHeuristicFinishCpu(gpu, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order)) + if (TryHeuristicFinishCpu(gpu, gpu2, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order)) { // GpuPrimeWorkLimiter.Release(); return order; @@ -340,6 +346,7 @@ internal static partial class PrimeOrderCalculator private static bool TryConfirmOrderCpu( PrimeOrderCalculatorAccelerator gpu, + HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, ulong order, in MontgomeryDivisorData divisorData, @@ -358,7 +365,7 @@ internal static partial class PrimeOrderCalculator return false; } - PartialFactorResult factorization = PartialFactor(gpu, order, config); + PartialFactorResult factorization = PartialFactor(gpu, gpu2, order, config); if (factorization.Factors is null) { factorization.Dispose(); @@ -503,6 +510,7 @@ internal static partial class PrimeOrderCalculator private static bool TryHeuristicFinishCpu( PrimeOrderCalculatorAccelerator gpu, + HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, ulong order, ulong? previousOrder, @@ -520,7 +528,7 @@ internal static partial class PrimeOrderCalculator } // Reuse the partial factorization from TryConfirmOrderCpu when available. - PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactor(gpu, order, config); + PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactor(gpu, gpu2, order, config); try { if (orderFactors.Factors is null) @@ -636,7 +644,7 @@ internal static partial class PrimeOrderCalculator bool equalsOne = powStepper.InitializeCpuIsUnity(candidate); powStepperInitialized = true; - if (equalsOne && TryConfirmCandidateCpu(gpu, prime, candidate, divisorData, config, ref powUsed, powBudget)) + if (equalsOne && TryConfirmCandidateCpu(gpu, gpu2, prime, candidate, divisorData, config, ref powUsed, powBudget)) { if (gpuPool is not null) { @@ -673,7 +681,7 @@ internal static partial class PrimeOrderCalculator continue; } - if (!TryConfirmCandidateCpu(gpu, prime, candidate, divisorData, config, ref powUsed, powBudget)) + if (!TryConfirmCandidateCpu(gpu, gpu2, prime, candidate, divisorData, config, ref powUsed, powBudget)) { continue; } @@ -884,9 +892,9 @@ internal static partial class PrimeOrderCalculator } } - private static bool TryConfirmCandidateCpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget) + private static bool TryConfirmCandidateCpu(PrimeOrderCalculatorAccelerator gpu, HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget) { - PartialFactorResult factorization = PartialFactor(gpu, candidate, config); + PartialFactorResult factorization = PartialFactor(gpu, gpu2, candidate, config); try { if (factorization.Factors is null) @@ -1082,7 +1090,9 @@ internal static partial class PrimeOrderCalculator // private static ulong _partialFactorPendingHits; // private static ulong _partialFactorCofactorHits; - private static PartialFactorResult PartialFactor(PrimeOrderCalculatorAccelerator gpu, ulong value, in PrimeOrderSearchConfig config) + private static bool _goToGpu = true; + + private static PartialFactorResult PartialFactor(PrimeOrderCalculatorAccelerator gpu, HeuristicCombinedPrimeTesterAccelerator gpu2, ulong value, in PrimeOrderSearchConfig config) { if (value <= 1UL) { @@ -1169,7 +1179,11 @@ internal static partial class PrimeOrderCalculator // Console.WriteLine($"Partial factor hits {Volatile.Read(ref _partialFactorHits)}"); // bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None); - bool isPrime = PrimeTester.IsPrimeGpu(gpu, composite); + bool goToGpu = Volatile.Read(ref _goToGpu); + Volatile.Write(ref _goToGpu, !goToGpu); + bool isPrime = goToGpu + ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu2, composite) + : PrimeTester.IsPrime(composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); if (isPrime) @@ -1241,7 +1255,7 @@ internal static partial class PrimeOrderCalculator // Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}"); // bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None); - bool isPrime = PrimeTester.IsPrimeGpu(gpu, composite); + bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu2, composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); entry = entry.WithPrimality(isPrime); @@ -1278,7 +1292,8 @@ internal static partial class PrimeOrderCalculator // Atomic.Add(ref _partialFactorCofactorHits, 1UL); // Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}"); - cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor); + cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu2, cofactor); + // cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor); // cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor); // cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor); } @@ -1698,11 +1713,11 @@ internal static partial class PrimeOrderCalculator exponents = newExponentSpan; } - private static ulong CalculateByFactorizationCpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) + private static ulong CalculateByFactorizationCpu(PrimeOrderCalculatorAccelerator gpu, HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; Dictionary counts = new(capacity: 8); - FactorCompletelyCpu(phi, counts); + FactorCompletelyCpu(gpu2, phi, counts); if (counts.Count == 0) { return phi; @@ -1746,9 +1761,9 @@ internal static partial class PrimeOrderCalculator return order; } - private static void FactorCompletelyCpu(ulong value, Dictionary counts) + private static void FactorCompletelyCpu(HeuristicCombinedPrimeTesterAccelerator gpu, ulong value, Dictionary counts) { - FactorCompletely(value, counts, knownComposite: false); + FactorCompletely(gpu, value, counts, knownComposite: false); } // private static ulong _factorCompletelyHits; @@ -1756,7 +1771,7 @@ internal static partial class PrimeOrderCalculator // [ThreadStatic] // private static HeuristicPrimeTester? _tester; - private static void FactorCompletely(ulong value, Dictionary counts, bool knownComposite) + private static void FactorCompletely(HeuristicCombinedPrimeTesterAccelerator gpu, ulong value, Dictionary counts, bool knownComposite) { if (value <= 1UL) { @@ -1768,7 +1783,8 @@ internal static partial class PrimeOrderCalculator // HeuristicPrimeTester tester = _tester ??= new(); // bool isPrime = primeTester.HeuristicIsPrimeGpu(value); - bool isPrime = PrimeTester.IsPrimeGpu(value); + bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value); + // bool isPrime = PrimeTester.IsPrimeGpu(value); // bool isPrime = PrimeTester.IsPrimeGpu(value); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value); if (!knownComposite && isPrime) @@ -1784,7 +1800,7 @@ internal static partial class PrimeOrderCalculator // Console.WriteLine($"Factor completely after PollardRho hits {Volatile.Read(ref _factorCompletelyPollardRhoHits)}"); // isPrime = primeTester.HeuristicIsPrimeGpu(factor); - isPrime = PrimeTester.IsPrimeGpu(factor); + isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, factor); // isPrime = PrimeTester.IsPrimeGpu(factor); // isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor); if (isPrime) @@ -1801,13 +1817,13 @@ internal static partial class PrimeOrderCalculator if (remaining > 1UL) { - FactorCompletely(remaining, counts, knownComposite: false); + FactorCompletely(gpu, remaining, counts, knownComposite: false); } return; } - FactorCompletely(factor, counts, knownComposite: true); + FactorCompletely(gpu, factor, counts, knownComposite: true); if (quotient == factor) { @@ -1821,7 +1837,7 @@ internal static partial class PrimeOrderCalculator // Atomic.Add(ref _partialFactorCofactorHits, 1UL); // Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}"); - isPrime = PrimeTester.IsPrimeGpu(quotient); + isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, quotient); // isPrime = PrimeTester.IsPrimeGpu(quotient); // isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(quotient); } @@ -1832,7 +1848,7 @@ internal static partial class PrimeOrderCalculator } else { - FactorCompletely(quotient, counts, knownComposite: true); + FactorCompletely(gpu, quotient, counts, knownComposite: true); } } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index 38d45f0..e6ead45 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -12,13 +12,13 @@ internal static partial class PrimeOrderCalculator private const int GpuSmallPrimeFactorSlots = 64; - private static ulong CalculateByFactorizationGpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) + private static ulong CalculateByFactorizationGpu(PrimeOrderCalculatorAccelerator gpu, HeuristicCombinedPrimeTesterAccelerator gpu2, ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; Dictionary counts = gpu.Pow2ModEntriesToTestOnHost; counts.Clear(); - FactorCompletelyCpu(phi, counts); + FactorCompletelyCpu(gpu2, phi, counts); if (counts.Count == 0) { return phi;