SemaphoreSlim replaced with PollingSemaphore.

This commit is contained in:
mlemanczyk committed 2025-12-12 03:41:13 +01:00
1 parent 471f9ac6cf
commit 15bcde1776
4 files changed
+20 -14

No files matched your search

@@ -6,10 +6,10 @@ namespace PerfectNumbers.Core.Gpu.Accelerators;
public static class AcceleratorStreamPool public static class AcceleratorStreamPool
{ {
private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators;
private static readonly SemaphoreSlim[] _locks = new SemaphoreSlim[PerfectNumberConstants.RollingAccelerators]; private static readonly PollingSemaphore[] _locks = new PollingSemaphore[PerfectNumberConstants.RollingAccelerators];
private static readonly ConcurrentFixedCapacityStack<AcceleratorStream>[] _streams = new ConcurrentFixedCapacityStack<AcceleratorStream>[PerfectNumberConstants.RollingAccelerators]; private static readonly ConcurrentFixedCapacityStack<AcceleratorStream>[] _streams = new ConcurrentFixedCapacityStack<AcceleratorStream>[PerfectNumberConstants.RollingAccelerators];
private static SemaphoreSlim CreateLock() => new(PerfectNumberConstants.ThreadsByAccelerator); private static PollingSemaphore CreateLock() => new(PerfectNumberConstants.ThreadsByAccelerator, TimeSpan.FromMilliseconds(1000), 1);
// private static int _rented; // private static int _rented;
+10 -9
View File
@@ -1,11 +1,15 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu;
public static class GpuWorkLimiter public static class GpuWorkLimiter
{ {
private static SemaphoreSlim _semaphore = new(1, int.MaxValue); private static readonly TimeSpan _pauseBetweenChecks = TimeSpan.FromSeconds(30);
private static PollingSemaphore _semaphore = new(int.MaxValue, _pauseBetweenChecks, 1);
private static readonly object _lock = new(); private static readonly object _lock = new();
private static int _currentLimit = 1; private static int _currentLimit = 1;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static Lease Acquire() public static Lease Acquire()
{ {
// TODO: Replace the per-call Lease allocation with the pooled struct-based guard from the // TODO: Replace the per-call Lease allocation with the pooled struct-based guard from the
@@ -35,20 +39,17 @@ public static class GpuWorkLimiter
// that may still be in use by existing Lease instances. // that may still be in use by existing Lease instances.
// TODO: Consolidate with GpuPrimeWorkLimiter so both limiters share a pooled SemaphoreSlim and avoid rebuilding the // TODO: Consolidate with GpuPrimeWorkLimiter so both limiters share a pooled SemaphoreSlim and avoid rebuilding the
// limiter state whenever limits are adjusted from the CLI. // limiter state whenever limits are adjusted from the CLI.
_semaphore = new SemaphoreSlim(value, value); _semaphore = new PollingSemaphore(value, _pauseBetweenChecks, 1);
_currentLimit = value; _currentLimit = value;
} }
} }
public sealed class Lease [method: MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public readonly struct Lease(PollingSemaphore sem)
{ {
private readonly SemaphoreSlim _sem; private readonly PollingSemaphore _sem = sem;
internal Lease(SemaphoreSlim sem)
{
_sem = sem;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Dispose() public void Dispose()
{ {
_sem.Release(); _sem.Release();
+7 -2
View File
@@ -11,17 +11,21 @@ public sealed class PollingSemaphore
{ {
private readonly int _maximumConcurrency; private readonly int _maximumConcurrency;
private readonly TimeSpan _pauseBetweenChecks; private readonly TimeSpan _pauseBetweenChecks;
private readonly int _attemptsBeforeSleep;
private readonly bool _hasPause; private readonly bool _hasPause;
private int _currentHolders; private int _currentHolders;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public PollingSemaphore(int maximumConcurrency, TimeSpan pauseBetweenChecks) public PollingSemaphore(int maximumConcurrency, TimeSpan pauseBetweenChecks, int attemptsBeforeSleep = 10)
{ {
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(maximumConcurrency, 0); ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(maximumConcurrency, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(pauseBetweenChecks, TimeSpan.Zero); ArgumentOutOfRangeException.ThrowIfLessThan(pauseBetweenChecks, TimeSpan.Zero);
_maximumConcurrency = maximumConcurrency; _maximumConcurrency = maximumConcurrency;
_pauseBetweenChecks = pauseBetweenChecks; _pauseBetweenChecks = pauseBetweenChecks;
_attemptsBeforeSleep = attemptsBeforeSleep;
_hasPause = pauseBetweenChecks > TimeSpan.Zero; _hasPause = pauseBetweenChecks > TimeSpan.Zero;
} }
@@ -45,6 +49,7 @@ public sealed class PollingSemaphore
bool hasPause = _hasPause; bool hasPause = _hasPause;
bool hasStopwatch = timeout != Timeout.InfiniteTimeSpan; bool hasStopwatch = timeout != Timeout.InfiniteTimeSpan;
int attemptsBeforeSleep = _attemptsBeforeSleep;
SpinWait spinner = new(); SpinWait spinner = new();
TimeSpan pauseBetweenChecks = _pauseBetweenChecks; TimeSpan pauseBetweenChecks = _pauseBetweenChecks;
Stopwatch? stopwatch = hasStopwatch ? Stopwatch.StartNew() : null; Stopwatch? stopwatch = hasStopwatch ? Stopwatch.StartNew() : null;
@@ -62,7 +67,7 @@ public sealed class PollingSemaphore
return false; return false;
} }
if (spinner.Count < 10) if (spinner.Count < attemptsBeforeSleep)
{ {
spinner.SpinOnce(); spinner.SpinOnce();
continue; continue;
+1 -1
View File
@@ -10,7 +10,7 @@ internal sealed class TaskThreadPool
public readonly Task Task = task; public readonly Task Task = task;
} }
private readonly ConcurrentFixedCapacityStack<WorkItem> _pendingTaskQueue = new(PerfectNumberConstants.DefaultThreadPoolCapacity); private readonly ConcurrentFixedCapacityStack<WorkItem> _pendingTaskQueue = new(PerfectNumberConstants.DefaultThreadPoolCapacity << 1);
private readonly HashSet<Thread> _runningThreads = []; private readonly HashSet<Thread> _runningThreads = [];
private readonly object _lock = new(); private readonly object _lock = new();
private readonly Action<Task> _taskExecutor; private readonly Action<Task> _taskExecutor;