From 1cbe7bf869fec9679d8da8f4893ae10ef37edb71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Sun, 2 Nov 2025 14:02:15 +0100 Subject: [PATCH] Use GpuPrimeWorkLimiter for GPU lease limiting --- EvenPerfectBitScanner/Program.cs | 5 +-- PerfectNumbers.Core/Gpu/GpuContextPool.cs | 24 ++++-------- .../Gpu/GpuPrimeLeaseLimiter.cs | 37 ------------------- .../Gpu/GpuPrimeWorkLimiter.cs | 33 +++++++++++------ PerfectNumbers.Core/PrimeTester.cs | 36 +++++++----------- 5 files changed, 42 insertions(+), 93 deletions(-) delete mode 100644 PerfectNumbers.Core/Gpu/GpuPrimeLeaseLimiter.cs diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index c12e9cc..bcd7937 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -61,7 +61,7 @@ internal static class Program int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads); UnboundedTaskScheduler.ConfigureThreadCount(threadCount); PrimeTester.GpuBatchSize = gpuPrimeBatch; - GpuPrimeLeaseLimiter.Initialize(gpuPrimeThreads); + GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads); GpuContextPool.WarmUpPool(threadCount); PrimeTester.WarmUpGpuKernels(gpuPrimeThreads); Console.WriteLine("Starting up threads..."); @@ -118,9 +118,6 @@ internal static class Program return; } - // Apply GPU prime sieve runtime configuration - GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads); - PrimeTester.GpuBatchSize = gpuPrimeBatch; MersenneDivisorCycles mersenneDivisorCycles = new(); if (!File.Exists(cyclesPath) || continueCyclesGeneration) diff --git a/PerfectNumbers.Core/Gpu/GpuContextPool.cs b/PerfectNumbers.Core/Gpu/GpuContextPool.cs index 47c546b..b6218c4 100644 --- a/PerfectNumbers.Core/Gpu/GpuContextPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuContextPool.cs @@ -48,27 +48,18 @@ public static class GpuContextPool private static int WarmedGpuContextCount; // Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu. - public static GpuContextLease Rent() + public static GpuContextLease Rent() { - GpuPrimeLeaseLimiter.Enter(); - - try + if (GpuPool.TryDequeue(out var gpu)) { - if (GpuPool.TryDequeue(out var gpu)) - { - return new GpuContextLease(gpu); - } + return new GpuContextLease(gpu); + } - // Create a new accelerator when the pool does not have one available. - return new GpuContextLease(new PooledContext()); - } - catch - { - GpuPrimeLeaseLimiter.Exit(); - throw; - } + // Create a new accelerator when the pool does not have one available. + return new GpuContextLease(new PooledContext()); } + public static void WarmUpPool(int threadCount) { if (threadCount <= WarmedGpuContextCount) @@ -106,7 +97,6 @@ public static class GpuContextPool { ctx.Accelerator.Synchronize(); GpuPool.Enqueue(ctx); - GpuPrimeLeaseLimiter.Exit(); } public struct GpuContextLease diff --git a/PerfectNumbers.Core/Gpu/GpuPrimeLeaseLimiter.cs b/PerfectNumbers.Core/Gpu/GpuPrimeLeaseLimiter.cs deleted file mode 100644 index a444668..0000000 --- a/PerfectNumbers.Core/Gpu/GpuPrimeLeaseLimiter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Threading; - -namespace PerfectNumbers.Core.Gpu; - -public static class GpuPrimeLeaseLimiter -{ - private static SemaphoreSlim _semaphore = new(int.MaxValue, int.MaxValue); - private static int _currentLimit = int.MaxValue; - - public static void Initialize(int limit) - { - if (limit <= 0) - { - limit = 1; - } - - if (limit == _currentLimit) - { - return; - } - - var previous = _semaphore; - _semaphore = new SemaphoreSlim(limit, limit); - _currentLimit = limit; - previous.Dispose(); - } - - public static void Enter() - { - _semaphore.Wait(); - } - - public static void Exit() - { - _semaphore.Release(); - } -} diff --git a/PerfectNumbers.Core/Gpu/GpuPrimeWorkLimiter.cs b/PerfectNumbers.Core/Gpu/GpuPrimeWorkLimiter.cs index 1d39b7d..48f3abb 100644 --- a/PerfectNumbers.Core/Gpu/GpuPrimeWorkLimiter.cs +++ b/PerfectNumbers.Core/Gpu/GpuPrimeWorkLimiter.cs @@ -1,3 +1,6 @@ +using System; +using System.Threading; + namespace PerfectNumbers.Core.Gpu; public static class GpuPrimeWorkLimiter @@ -10,24 +13,31 @@ public static class GpuPrimeWorkLimiter // TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve // GPU jobs stop allocating Lease instances on every acquisition. var sem = _semaphore; - // sem.Wait(); + sem.Wait(); return new Lease(sem); } public static void SetLimit(int value) { - if (value == _currentLimit) - { - return; - } + if (value <= 0) + { + value = 1; + } - // TODO: Switch to a shared limiter implementation with GpuWorkLimiter so we can coordinate CPU/GPU prime work using - // the same semaphore pool without reallocating per adjustment. - _semaphore = new SemaphoreSlim(value, value); - _currentLimit = value; + if (value == _currentLimit) + { + return; + } + + // TODO: Switch to a shared limiter implementation with GpuWorkLimiter so we can coordinate CPU/GPU prime work using + // the same semaphore pool without reallocating per adjustment. + var previous = _semaphore; + _semaphore = new SemaphoreSlim(value, value); + _currentLimit = value; + previous.Dispose(); } - public sealed class Lease + public sealed class Lease : IDisposable { private readonly SemaphoreSlim _sem; @@ -38,8 +48,7 @@ public static class GpuPrimeWorkLimiter public void Dispose() { - // _sem.Release(); + _sem.Release(); } } } - diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 100e9f6..75f2b0d 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -282,36 +282,26 @@ public sealed class PrimeTester } } - internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1) - { - GpuPrimeLeaseLimiter.Enter(); + internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1) + { + if (!Pool.TryDequeue(out var lease)) + { + lease = new PrimeTesterGpuContextLease(minBufferCapacity); + } + else + { + lease.EnsureCapacity(minBufferCapacity); + } - try - { - if (!Pool.TryDequeue(out var lease)) - { - lease = new PrimeTesterGpuContextLease(minBufferCapacity); - } - else - { - lease.EnsureCapacity(minBufferCapacity); - } + lease.ResetReturnFlag(); + return lease; + } - lease.ResetReturnFlag(); - return lease; - } - catch - { - GpuPrimeLeaseLimiter.Exit(); - throw; - } - } internal static void Return(PrimeTesterGpuContextLease lease) { lease.Accelerator.Synchronize(); Pool.Enqueue(lease); - GpuPrimeLeaseLimiter.Exit(); } internal static void DisposeAll()