From 3c1519874c42c8b32c8b321c9838551bfd9b5220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Sat, 11 Oct 2025 15:58:34 +0200 Subject: [PATCH] Enable GPU fallbacks for CPU order calculations --- EvenPerfectBitScanner/Program.cs | 2 ++ PerfectNumbers.Core/Gpu/GpuKernelPool.cs | 2 ++ PerfectNumbers.Core/PrimeOrderCalculator.cs | 37 +++++++++++++++++---- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index 229f5dd..d11fb46 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -377,6 +377,8 @@ internal static class Program } } + GpuKernelPool.CpuCycleUsesGpu = orderOnGpu; + if (useByDivisor) { _byDivisorStartPrime = startPrimeProvided ? currentP : 0UL; diff --git a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs index 1fcee0e..d3b5013 100644 --- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs @@ -756,6 +756,8 @@ public class GpuKernelPool { private static readonly ConcurrentDictionary KernelCache = new(); // TODO: Replace this concurrent map with a simple accelerator-indexed lookup once kernel launchers are prewarmed during startup so we can drop the thread-safe wrapper entirely. + public static bool CpuCycleUsesGpu { get; set; } + private static KernelContainer GetKernels(Accelerator accelerator) { return KernelCache.GetOrAdd(accelerator, _ => new KernelContainer()); diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.cs b/PerfectNumbers.Core/PrimeOrderCalculator.cs index c89155e..2d013fb 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.cs @@ -39,8 +39,13 @@ internal static partial class PrimeOrderCalculator [ThreadStatic] private static bool s_allowGpuPow2; + [ThreadStatic] + private static bool s_allowGpuCycle; + private static bool IsGpuPow2Allowed => s_pow2ModeInitialized && s_allowGpuPow2; + private static bool IsGpuCycleAllowed => s_pow2ModeInitialized && s_allowGpuCycle; + [ThreadStatic] private static PrimeOrderHeuristicDevice s_deviceMode; @@ -61,22 +66,29 @@ internal static partial class PrimeOrderCalculator private readonly struct Pow2ModeScope { private readonly bool _previousInitialized; - private readonly bool _previousAllow; + private readonly bool _previousAllowPow2; + private readonly bool _previousAllowCycle; private readonly PrimeOrderHeuristicDevice _previousDevice; public Pow2ModeScope(PrimeOrderHeuristicDevice device) { _previousInitialized = s_pow2ModeInitialized; - _previousAllow = s_allowGpuPow2; + _previousAllowPow2 = s_allowGpuPow2; + _previousAllowCycle = s_allowGpuCycle; _previousDevice = s_deviceMode; s_pow2ModeInitialized = true; - s_allowGpuPow2 = device == PrimeOrderHeuristicDevice.Gpu; + bool gpuAvailable = !GpuContextPool.ForceCpu; + bool allowCpuPow2 = gpuAvailable && device == PrimeOrderHeuristicDevice.Cpu; + bool allowCpuCycle = gpuAvailable && device == PrimeOrderHeuristicDevice.Cpu && GpuKernelPool.CpuCycleUsesGpu; + s_allowGpuPow2 = device == PrimeOrderHeuristicDevice.Gpu || allowCpuPow2; + s_allowGpuCycle = device == PrimeOrderHeuristicDevice.Gpu || allowCpuCycle; s_deviceMode = device; } public void Dispose() { - s_allowGpuPow2 = _previousAllow; + s_allowGpuPow2 = _previousAllowPow2; + s_allowGpuCycle = _previousAllowCycle; s_pow2ModeInitialized = _previousInitialized; s_deviceMode = _previousDevice; } @@ -157,7 +169,7 @@ internal static partial class PrimeOrderCalculator // so PartialFactor never returns a null factor list on this call path. // if (phiFactors.Factors is null) // { - // result = CalculateByFactorizationCpu(prime, divisorData); + // result = CalculateByFactorizationCpu(prime, divisorData, config); // phiFactors.Dispose(); // return result; // } @@ -201,7 +213,7 @@ internal static partial class PrimeOrderCalculator if (config.Mode == PrimeOrderMode.Strict) { orderFactors.Dispose(); - return CalculateByFactorizationCpu(prime, divisorData); + return CalculateByFactorizationCpu(prime, divisorData, config); } if (TryHeuristicFinishCpu(prime, candidateOrder, previousOrder, divisorData, config, ref orderFactors, phiFactors, out ulong order)) @@ -1328,8 +1340,19 @@ internal static partial class PrimeOrderCalculator } } - private static ulong CalculateByFactorizationCpu(ulong prime, in MontgomeryDivisorData divisorData) + private static ulong CalculateByFactorizationCpu(ulong prime, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config) { + if (!IsGpuHeuristicDevice && IsGpuCycleAllowed) + { + PrimeOrderSearchConfig strictConfig = config.Mode == PrimeOrderMode.Strict + ? config + : new PrimeOrderSearchConfig(config.SmallFactorLimit, config.PollardRhoMilliseconds, config.MaxPowChecks, PrimeOrderMode.Strict); + if (PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder: null, strictConfig, divisorData, out ulong gpuOrder)) + { + return gpuOrder; + } + } + ulong phi = prime - 1UL; Dictionary counts = new(capacity: 8); FactorCompletely(phi, counts);