SemaphoreSlim replaced with PollingSemaphore.
This commit is contained in:
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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in new issue
Block a user