Use GpuPrimeWorkLimiter for GPU lease limiting

This commit is contained in:
Marek Lemańczyk committed 2025-11-02 14:02:15 +01:00
1 parent 00d4e9bde2
commit 1cbe7bf869
5 files changed
+42 -93

No files matched your search

+1 -4
View File
@@ -61,7 +61,7 @@ internal static class Program
int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads); int gpuPrimeThreads = Math.Max(1, _cliArguments.GpuPrimeThreads);
UnboundedTaskScheduler.ConfigureThreadCount(threadCount); UnboundedTaskScheduler.ConfigureThreadCount(threadCount);
PrimeTester.GpuBatchSize = gpuPrimeBatch; PrimeTester.GpuBatchSize = gpuPrimeBatch;
GpuPrimeLeaseLimiter.Initialize(gpuPrimeThreads); GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
GpuContextPool.WarmUpPool(threadCount); GpuContextPool.WarmUpPool(threadCount);
PrimeTester.WarmUpGpuKernels(gpuPrimeThreads); PrimeTester.WarmUpGpuKernels(gpuPrimeThreads);
Console.WriteLine("Starting up threads..."); Console.WriteLine("Starting up threads...");
@@ -118,9 +118,6 @@ internal static class Program
return; return;
} }
// Apply GPU prime sieve runtime configuration
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads);
PrimeTester.GpuBatchSize = gpuPrimeBatch;
MersenneDivisorCycles mersenneDivisorCycles = new(); MersenneDivisorCycles mersenneDivisorCycles = new();
if (!File.Exists(cyclesPath) || continueCyclesGeneration) if (!File.Exists(cyclesPath) || continueCyclesGeneration)
+7 -17
View File
@@ -48,27 +48,18 @@ public static class GpuContextPool
private static int WarmedGpuContextCount; private static int WarmedGpuContextCount;
// Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu. // Allows callers to choose CPU/GPU per use-case, decoupled from ForceCpu.
public static GpuContextLease Rent() public static GpuContextLease Rent()
{ {
GpuPrimeLeaseLimiter.Enter(); if (GpuPool.TryDequeue(out var gpu))
try
{ {
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. // Create a new accelerator when the pool does not have one available.
return new GpuContextLease(new PooledContext()); return new GpuContextLease(new PooledContext());
}
catch
{
GpuPrimeLeaseLimiter.Exit();
throw;
}
} }
public static void WarmUpPool(int threadCount) public static void WarmUpPool(int threadCount)
{ {
if (threadCount <= WarmedGpuContextCount) if (threadCount <= WarmedGpuContextCount)
@@ -106,7 +97,6 @@ public static class GpuContextPool
{ {
ctx.Accelerator.Synchronize(); ctx.Accelerator.Synchronize();
GpuPool.Enqueue(ctx); GpuPool.Enqueue(ctx);
GpuPrimeLeaseLimiter.Exit();
} }
public struct GpuContextLease public struct GpuContextLease
@@ -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();
}
}
+21 -12
View File
@@ -1,3 +1,6 @@
using System;
using System.Threading;
namespace PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu;
public static class GpuPrimeWorkLimiter public static class GpuPrimeWorkLimiter
@@ -10,24 +13,31 @@ public static class GpuPrimeWorkLimiter
// TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve // TODO: Inline the pooled limiter guard from the GpuLimiterThroughputBenchmarks so prime-sieve
// GPU jobs stop allocating Lease instances on every acquisition. // GPU jobs stop allocating Lease instances on every acquisition.
var sem = _semaphore; var sem = _semaphore;
// sem.Wait(); sem.Wait();
return new Lease(sem); return new Lease(sem);
} }
public static void SetLimit(int value) public static void SetLimit(int value)
{ {
if (value == _currentLimit) if (value <= 0)
{ {
return; value = 1;
} }
// TODO: Switch to a shared limiter implementation with GpuWorkLimiter so we can coordinate CPU/GPU prime work using if (value == _currentLimit)
// the same semaphore pool without reallocating per adjustment. {
_semaphore = new SemaphoreSlim(value, value); return;
_currentLimit = value; }
// 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; private readonly SemaphoreSlim _sem;
@@ -38,8 +48,7 @@ public static class GpuPrimeWorkLimiter
public void Dispose() public void Dispose()
{ {
// _sem.Release(); _sem.Release();
} }
} }
} }
+13 -23
View File
@@ -282,36 +282,26 @@ public sealed class PrimeTester
} }
} }
internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1) internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1)
{ {
GpuPrimeLeaseLimiter.Enter(); if (!Pool.TryDequeue(out var lease))
{
lease = new PrimeTesterGpuContextLease(minBufferCapacity);
}
else
{
lease.EnsureCapacity(minBufferCapacity);
}
try lease.ResetReturnFlag();
{ return lease;
if (!Pool.TryDequeue(out var lease)) }
{
lease = new PrimeTesterGpuContextLease(minBufferCapacity);
}
else
{
lease.EnsureCapacity(minBufferCapacity);
}
lease.ResetReturnFlag();
return lease;
}
catch
{
GpuPrimeLeaseLimiter.Exit();
throw;
}
}
internal static void Return(PrimeTesterGpuContextLease lease) internal static void Return(PrimeTesterGpuContextLease lease)
{ {
lease.Accelerator.Synchronize(); lease.Accelerator.Synchronize();
Pool.Enqueue(lease); Pool.Enqueue(lease);
GpuPrimeLeaseLimiter.Exit();
} }
internal static void DisposeAll() internal static void DisposeAll()