From 8c662c5c857c7b757b40e22b71916e6aeff8676a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Fri, 31 Oct 2025 21:47:42 +0100 Subject: [PATCH] Address feedback on GPU pooling and heuristic factoring --- PerfectNumbers.Core/Gpu/GpuContextPool.cs | 92 +++++++------------ .../Gpu/Kernels/PrimeTesterKernels.cs | 22 +---- .../HeuristicCombinedPrimeTester.cs | 27 +++--- PerfectNumbers.Core/HeuristicPrimeTester.cs | 29 +++--- .../PrimeOrderCalculator.Cpu.cs | 20 ++-- PerfectNumbers.Core/PrimeTester.cs | 41 +++++---- 6 files changed, 92 insertions(+), 139 deletions(-) diff --git a/PerfectNumbers.Core/Gpu/GpuContextPool.cs b/PerfectNumbers.Core/Gpu/GpuContextPool.cs index 3731c77..730f365 100644 --- a/PerfectNumbers.Core/Gpu/GpuContextPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuContextPool.cs @@ -7,7 +7,6 @@ namespace PerfectNumbers.Core.Gpu; public static class GpuContextPool { - private static readonly bool PoolingEnabled = true; // The pool intentionally skips pre-loading ProcessEightBitWindows kernels so accelerator initialization // stays stable; GpuKernelPool JIT-compiles them on first use alongside the small-cycle uploads. // Default device preference for generic GPU kernels (prime scans, NTT, etc.) @@ -48,7 +47,6 @@ public static class GpuContextPool private static readonly ConcurrentQueue CpuPool = new(); private static readonly ConcurrentQueue GpuPool = new(); - private static readonly object WarmUpLock = new(); private static int WarmedGpuContextCount; public static GpuContextLease Rent() @@ -59,17 +57,14 @@ public static class GpuContextPool // Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu. public static GpuContextLease RentPreferred(bool preferCpu) { - if (PoolingEnabled) + if (preferCpu && CpuPool.TryDequeue(out var cpu)) { - if (preferCpu && CpuPool.TryDequeue(out var cpu)) - { - return new GpuContextLease(cpu); - } + return new GpuContextLease(cpu); + } - if (!preferCpu && GpuPool.TryDequeue(out var gpu)) - { - return new GpuContextLease(gpu); - } + if (!preferCpu && GpuPool.TryDequeue(out var gpu)) + { + return new GpuContextLease(gpu); } // Create a new accelerator when the pool does not have one available. @@ -78,53 +73,40 @@ public static class GpuContextPool public static void WarmUpPool(int threadCount) { - if (!PoolingEnabled || ForceCpu) + if (ForceCpu) { return; } - if (threadCount <= 0) - { - return; - } - - int target = threadCount / 4; + int target = threadCount >> 2; if (target == 0) { - target = 1; + target = threadCount; } - lock (WarmUpLock) + if (target <= WarmedGpuContextCount) { - if (target <= WarmedGpuContextCount) - { - return; - } - - int toCreate = target - WarmedGpuContextCount; - var contexts = new PooledContext[toCreate]; - for (int i = 0; i < toCreate; i++) - { - contexts[i] = new PooledContext(preferCpu: false); - } - - for (int i = 0; i < toCreate; i++) - { - GpuPool.Enqueue(contexts[i]); - } - - WarmedGpuContextCount = target; + return; } + + int toCreate = target - WarmedGpuContextCount; + var contexts = new PooledContext[toCreate]; + for (int i = 0; i < toCreate; i++) + { + contexts[i] = new PooledContext(preferCpu: false); + } + + for (int i = 0; i < toCreate; i++) + { + GpuPool.Enqueue(contexts[i]); + } + + WarmedGpuContextCount = target; } public static void DisposeAll() { - if (!PoolingEnabled) - { - return; - } - while (CpuPool.TryDequeue(out var cpu)) { cpu.Dispose(); @@ -141,24 +123,16 @@ public static class GpuContextPool private static void Return(PooledContext ctx) { - if (PoolingEnabled) + ctx.Accelerator.Synchronize(); + if (ctx.IsCpu) { - ctx.Accelerator.Synchronize(); - if (ctx.IsCpu) - { - CpuPool.Enqueue(ctx); - } - else - { - GpuPool.Enqueue(ctx); - } + CpuPool.Enqueue(ctx); } - - else - { - ctx.Dispose(); - } - } + else + { + GpuPool.Enqueue(ctx); + } + } public struct GpuContextLease { diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs index 5b2f3fe..19b08c9 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs @@ -68,11 +68,6 @@ internal static class PrimeTesterKernels } ulong prime = primes[i]; - if (prime == 0) - { - break; - } - if (n % prime == 0UL) { result = 0; @@ -94,32 +89,23 @@ internal static class PrimeTesterKernels byte nMod10 = (byte)(n % 10UL); ArrayView1D divisors = tables.SelectDivisors(tableKind, nMod10); ArrayView1D divisorSquares = tables.SelectDivisorSquares(tableKind, nMod10); - if (divisors.Length == 0 || divisorSquares.Length == 0) - { - return; - } - int divisorLength = (int)divisors.Length; - int squareLength = (int)divisorSquares.Length; + int divisorLength = (int)divisorSquares.Length; int threadIndex = index; - if (threadIndex >= divisorLength || threadIndex >= squareLength) + if (threadIndex >= divisorLength) { return; } ulong divisorSquare = divisorSquares[threadIndex]; - if (divisorSquare == 0UL || divisorSquare > maxDivisorSquare) + if (divisorSquare > maxDivisorSquare) { return; } ulong divisor = divisors[threadIndex]; + divisor = n % divisor; if (divisor == 0UL) - { - return; - } - - if (n % divisor == 0UL) { resultFlag[0] = 1; } diff --git a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs index 3b4c051..0413fd9 100644 --- a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs @@ -235,21 +235,18 @@ public sealed class HeuristicCombinedPrimeTester bool compositeDetected = false; int divisorLength = GetCombinedDivisors(nMod10).Length; - if (divisorLength > 0) - { - int compositeFlag = 0; - flagView1D.CopyFromCPU(ref compositeFlag, 1); - kernel( - divisorLength, - flagView, - n, - maxDivisorSquare, - HeuristicGpuDivisorTableKind.Combined, - gpu.HeuristicGpuTables); - accelerator.Synchronize(); - flagView1D.CopyToCPU(ref compositeFlag, 1); - compositeDetected = compositeFlag != 0; - } + int compositeFlag = 0; + flagView1D.CopyFromCPU(ref compositeFlag, 1); + kernel( + divisorLength, + flagView, + n, + maxDivisorSquare, + HeuristicGpuDivisorTableKind.Combined, + gpu.HeuristicGpuTables); + accelerator.Synchronize(); + flagView1D.CopyToCPU(ref compositeFlag, 1); + compositeDetected = compositeFlag != 0; gpu.Dispose(); limiter.Dispose(); diff --git a/PerfectNumbers.Core/HeuristicPrimeTester.cs b/PerfectNumbers.Core/HeuristicPrimeTester.cs index 0a4e697..a60227b 100644 --- a/PerfectNumbers.Core/HeuristicPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicPrimeTester.cs @@ -252,20 +252,17 @@ public sealed class HeuristicPrimeTester int groupALength = HeuristicPrimeSieves.GroupADivisors.Length; - if (groupALength > 0) - { - flagView1D.CopyFromCPU(ref compositeFlag, 1); - kernel( - groupALength, - flagView, - n, - maxDivisorSquare, - HeuristicGpuDivisorTableKind.GroupA, - gpu.HeuristicGpuTables); - accelerator.Synchronize(); - flagView1D.CopyToCPU(ref compositeFlag, 1); - compositeDetected = compositeFlag != 0; - } + flagView1D.CopyFromCPU(ref compositeFlag, 1); + kernel( + groupALength, + flagView, + n, + maxDivisorSquare, + HeuristicGpuDivisorTableKind.GroupA, + gpu.HeuristicGpuTables); + accelerator.Synchronize(); + flagView1D.CopyToCPU(ref compositeFlag, 1); + compositeDetected = compositeFlag != 0; if (!compositeDetected) { @@ -287,10 +284,6 @@ public sealed class HeuristicPrimeTester } int divisorLength = GetGroupBDivisors(ending).Length; - if (divisorLength == 0) - { - continue; - } compositeFlag = 0; flagView1D.CopyFromCPU(ref compositeFlag, 1); diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 5ec3245..702039e 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -1289,11 +1289,7 @@ internal static partial class PrimeOrderCalculator if (primeSlotArray is not null) { ThreadStaticPools.UlongPool.Return(primeSlotArray, clearArray: false); - } - - if (exponentSlotArray is not null) - { - ThreadStaticPools.IntPool.Return(exponentSlotArray, clearArray: false); + ThreadStaticPools.IntPool.Return(exponentSlotArray!, clearArray: false); } if (compositeStack is not null) @@ -1340,12 +1336,20 @@ internal static partial class PrimeOrderCalculator } ulong primeSquare = squares[i]; + // primeSquare will never == 0 in production code + // if (primeSquare != 0 && primeSquare > remainingLocal) if (primeSquare > remainingLocal) { break; } ulong primeValue = primeCandidate; + // primeCandidate will never equal 0 in production code + // if (primeCandidate == 0UL) + // { + // continue; + // } + if ((remainingLocal % primeValue) != 0UL) { continue; @@ -1626,11 +1630,7 @@ internal static partial class PrimeOrderCalculator if (primeArray is not null) { ThreadStaticPools.UlongPool.Return(primeArray, clearArray: false); - } - - if (exponentArray is not null) - { - ThreadStaticPools.IntPool.Return(exponentArray, clearArray: false); + ThreadStaticPools.IntPool.Return(exponentArray!, clearArray: false); } primeArray = newPrimeArray; diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index f20d9d4..f0f6897 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -152,15 +152,10 @@ public sealed class PrimeTester return; } - if (threadCount <= 0) - { - return; - } - - int target = threadCount / 4; + int target = threadCount >> 2; if (target == 0) { - target = 1; + target = threadCount; } lock (GpuWarmUpLock) @@ -267,12 +262,14 @@ public sealed class PrimeTester private static void ReleaseSharedTables(SharedHeuristicGpuTables tables) { + if (tables is null) + { + return; + } + lock (SharedTablesLock) { - if (tables.Release()) - { - SharedTables.Remove(tables.Key); - } + tables.Release(); } } @@ -303,6 +300,16 @@ public sealed class PrimeTester { lease.DisposeResources(); } + + lock (SharedTablesLock) + { + foreach (var entry in SharedTables.Values) + { + entry.Dispose(); + } + + SharedTables.Clear(); + } } internal static void Clear(Accelerator accelerator) @@ -572,16 +579,12 @@ public sealed class PrimeTester _referenceCount++; } - internal bool Release() + internal void Release() { - _referenceCount--; - if (_referenceCount == 0) + if (_referenceCount > 0) { - Dispose(); - return true; + _referenceCount--; } - - return false; } private static MemoryBuffer1D CopySpanToDevice(Accelerator accelerator, ReadOnlySpan span) @@ -613,7 +616,7 @@ public sealed class PrimeTester return buffer; } - private void Dispose() + internal void Dispose() { DevicePrimesDefault.Dispose(); DevicePrimesLastOne.Dispose();