From 3735a1cf0f052835f929a1ed6dfa8d4d33da559c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Sat, 25 Oct 2025 05:00:54 +0200 Subject: [PATCH] Gate GPU by-divisor scans to a single accelerator --- ...MersenneNumberDivisorByDivisorGpuTester.cs | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index bba681c..a80aee7 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -26,6 +26,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private readonly ConcurrentDictionary, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>> _kernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary> _resourcePools = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentBag _acceleratorPool = new(); + private readonly SemaphoreSlim _acceleratorAvailable = new(0); + private int _createdAccelerators; + // Limit GPU context concurrency to avoid replicating the large batch buffers across thousands of worker threads. + private int _acceleratorLimit = 1; + private int _waitingAcceleratorCount; private readonly ConcurrentBag _sessionPool = new(); private Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> GetKernel(Accelerator accelerator) => @@ -679,15 +684,58 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private GpuContextPool.GpuContextLease RentAccelerator() { - if (_acceleratorPool.TryTake(out var lease)) + while (true) { - return lease; - } + if (_acceleratorPool.TryTake(out var lease)) + { + return lease; + } - return GpuContextPool.RentPreferred(preferCpu: false); + if (TryCreateAccelerator(out lease)) + { + return lease; + } + + Interlocked.Increment(ref _waitingAcceleratorCount); + try + { + _acceleratorAvailable.Wait(); + } + finally + { + Interlocked.Decrement(ref _waitingAcceleratorCount); + } + } } - private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) => _acceleratorPool.Add(lease); + private bool TryCreateAccelerator(out GpuContextPool.GpuContextLease lease) + { + while (true) + { + int created = Volatile.Read(ref _createdAccelerators); + int limit = Volatile.Read(ref _acceleratorLimit); + if (created >= limit) + { + lease = default; + return false; + } + + if (Interlocked.CompareExchange(ref _createdAccelerators, created + 1, created) == created) + { + lease = GpuContextPool.RentPreferred(preferCpu: false); + return true; + } + } + } + + private void ReturnAccelerator(GpuContextPool.GpuContextLease lease) + { + _acceleratorPool.Add(lease); + if (Volatile.Read(ref _waitingAcceleratorCount) > 0) + { + _acceleratorAvailable.Release(); + } + } private BatchResources RentBatchResources(Accelerator accelerator, int capacity) {