diff --git a/PerfectNumbers.Core/DivisorCycleCache.cs b/PerfectNumbers.Core/DivisorCycleCache.cs index 98fe8bf..47521b4 100644 --- a/PerfectNumbers.Core/DivisorCycleCache.cs +++ b/PerfectNumbers.Core/DivisorCycleCache.cs @@ -227,7 +227,6 @@ public sealed class DivisorCycleCache { int length = divisors.Length; var gpuLease = GpuKernelPool.GetKernel(useGpuOrder: true); - var execution = gpuLease.EnterExecutionScope(); Accelerator accelerator = gpuLease.Accelerator; var kernel = _gpuKernelCache.GetOrAdd(accelerator, LoadKernel); @@ -309,7 +308,6 @@ public sealed class DivisorCycleCache ArrayPool.Shared.Return(rentedStatus!, clearArray: false); } - execution.Dispose(); gpuLease.Dispose(); } diff --git a/PerfectNumbers.Core/Gpu/GpuContextPool.cs b/PerfectNumbers.Core/Gpu/GpuContextPool.cs index 560dd7d..4943be7 100644 --- a/PerfectNumbers.Core/Gpu/GpuContextPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuContextPool.cs @@ -47,7 +47,6 @@ public static class GpuContextPool private static readonly ConcurrentQueue CpuPool = new(); private static readonly ConcurrentQueue GpuPool = new(); - private static readonly object CreationLock = new(); public static GpuContextLease Rent() { @@ -61,20 +60,17 @@ public static class GpuContextPool { if (preferCpu && CpuPool.TryDequeue(out var cpu)) { - return new GpuContextLease(cpu, CreationLock); + return new GpuContextLease(cpu); } if (!preferCpu && GpuPool.TryDequeue(out var gpu)) { - return new GpuContextLease(gpu, CreationLock); + return new GpuContextLease(gpu); } } - // Serialize accelerator creation to avoid concurrent CL device/stream setup across threads. - lock (CreationLock) - { - return new GpuContextLease(new PooledContext(preferCpu), CreationLock); - } + // Create a new accelerator when the pool does not have one available. + return new GpuContextLease(new PooledContext(preferCpu)); } public static void DisposeAll() @@ -114,10 +110,7 @@ public static class GpuContextPool else { - lock (CreationLock) - { - ctx.Dispose(); - } + ctx.Dispose(); } } @@ -125,18 +118,15 @@ public static class GpuContextPool { private readonly PooledContext _ctx; - internal GpuContextLease(PooledContext ctx, object kernelInitLock) + internal GpuContextLease(PooledContext ctx) { _ctx = ctx; - KernelInitLock = kernelInitLock; } public Context Context => _ctx.Context; public Accelerator Accelerator => _ctx.Accelerator; - public object KernelInitLock { get; } - public object ExecutionLock => _ctx.ExecutionLock; diff --git a/PerfectNumbers.Core/Gpu/GpuKernelLease.cs b/PerfectNumbers.Core/Gpu/GpuKernelLease.cs index 66b2d7f..a79af7a 100644 --- a/PerfectNumbers.Core/Gpu/GpuKernelLease.cs +++ b/PerfectNumbers.Core/Gpu/GpuKernelLease.cs @@ -14,7 +14,6 @@ public sealed class GpuKernelLease private GpuContextLease? _gpu; private KernelContainer? _kernels; private AcceleratorStream? _stream; - private object? _executionLock; private GpuKernelLease() { @@ -31,7 +30,6 @@ public sealed class GpuKernelLease lease._gpu = gpu; lease._kernels = kernels; lease._stream = null; - lease._executionLock = gpu.ExecutionLock; return lease; } @@ -53,22 +51,17 @@ public sealed class GpuKernelLease } } - public ExecutionScope EnterExecutionScope() => new ExecutionScope(_executionLock); - public Action, ArrayView> OrderKernel { get { var accel = Accelerator; // avoid capturing 'this' in lambda - lock (_gpu!.Value.KernelInitLock) + return KernelContainer.InitOnce(ref _kernels!.Order, () => { - return KernelContainer.InitOnce(ref _kernels!.Order, () => - { - var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView>(OrderKernels.OrderKernelScan); - var kernel = KernelUtil.GetKernel(loaded); - return kernel.CreateLauncherDelegate, ArrayView>>(); - }); - } + var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView>(OrderKernels.OrderKernelScan); + var kernel = KernelUtil.GetKernel(loaded); + return kernel.CreateLauncherDelegate, ArrayView>>(); + }); } } @@ -77,15 +70,12 @@ public sealed class GpuKernelLease get { var accel = Accelerator; - lock (_gpu!.Value.KernelInitLock) + return KernelContainer.InitOnce(ref _kernels!.Pow2Mod, () => { - return KernelContainer.InitOnce(ref _kernels!.Pow2Mod, () => - { - var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(Pow2ModKernels.Pow2ModKernelScan); - var kernel = KernelUtil.GetKernel(loaded); - return kernel.CreateLauncherDelegate, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); - }); - } + var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(Pow2ModKernels.Pow2ModKernelScan); + var kernel = KernelUtil.GetKernel(loaded); + return kernel.CreateLauncherDelegate, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); + }); } } @@ -94,15 +84,12 @@ public sealed class GpuKernelLease get { var accel = Accelerator; - lock (_gpu!.Value.KernelInitLock) + return KernelContainer.InitOnce(ref _kernels!.Incremental, () => { - return KernelContainer.InitOnce(ref _kernels!.Incremental, () => - { - var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalKernelScan); - var kernel = KernelUtil.GetKernel(loaded); - return kernel.CreateLauncherDelegate, ArrayView1D>>(); - }); - } + var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalKernelScan); + var kernel = KernelUtil.GetKernel(loaded); + return kernel.CreateLauncherDelegate, ArrayView1D>>(); + }); } } @@ -111,15 +98,12 @@ public sealed class GpuKernelLease get { var accel = Accelerator; - lock (_gpu!.Value.KernelInitLock) + return KernelContainer.InitOnce(ref _kernels!.IncrementalOrder, () => { - return KernelContainer.InitOnce(ref _kernels!.IncrementalOrder, () => - { - var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalOrderKernelScan); - var kernel = KernelUtil.GetKernel(loaded); - return kernel.CreateLauncherDelegate, ArrayView1D>>(); - }); - } + var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalOrderKernelScan); + var kernel = KernelUtil.GetKernel(loaded); + return kernel.CreateLauncherDelegate, ArrayView1D>>(); + }); } } @@ -128,15 +112,12 @@ public sealed class GpuKernelLease get { var accel = Accelerator; - lock (_gpu!.Value.KernelInitLock) + return KernelContainer.InitOnce(ref _kernels!.Pow2ModOrder, () => { - return KernelContainer.InitOnce(ref _kernels!.Pow2ModOrder, () => - { - var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(Pow2ModKernels.Pow2ModOrderKernelScan); - var kernel = KernelUtil.GetKernel(loaded); - return kernel.CreateLauncherDelegate, ArrayView1D>>(); - }); - } + var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(Pow2ModKernels.Pow2ModOrderKernelScan); + var kernel = KernelUtil.GetKernel(loaded); + return kernel.CreateLauncherDelegate, ArrayView1D>>(); + }); } } @@ -145,15 +126,12 @@ public sealed class GpuKernelLease get { var accel = Accelerator; - lock (_gpu!.Value.KernelInitLock) + return KernelContainer.InitOnce(ref _kernels!.SmallPrimeFactor, () => { - return KernelContainer.InitOnce(ref _kernels!.SmallPrimeFactor, () => - { - var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan); - var kernel = KernelUtil.GetKernel(loaded); - return kernel.CreateLauncherDelegate, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); - }); - } + var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan); + var kernel = KernelUtil.GetKernel(loaded); + return kernel.CreateLauncherDelegate, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); + }); } } @@ -165,8 +143,6 @@ public sealed class GpuKernelLease _gpu?.Dispose(); _gpu = null; - _executionLock = null; - _releaseLimiter?.Invoke(); _releaseLimiter = null; @@ -174,25 +150,4 @@ public sealed class GpuKernelLease Pool.Enqueue(this); } - public readonly struct ExecutionScope - { - private readonly object? _lock; - - public ExecutionScope(object? sync) - { - _lock = sync; - if (_lock is not null) - { - Monitor.Enter(_lock); - } - } - - public void Dispose() - { - if (_lock is not null) - { - Monitor.Exit(_lock); - } - } - } } diff --git a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs index c16d9ca..fb8a934 100644 --- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs @@ -293,12 +293,10 @@ public class GpuKernelPool public static void Run(Action action) { var lease = GetKernel(useGpuOrder: true); - var execution = lease.EnterExecutionScope(); var accelerator = lease.Accelerator; var stream = accelerator.CreateStream(); action(accelerator, stream); stream.Dispose(); - execution.Dispose(); lease.Dispose(); } diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs index 280288f..bdfe409 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs @@ -17,7 +17,6 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u throw new NotImplementedException($"GPU incremental scanning requires the device cycle heuristics implementation (kernel: {_kernelType}, GPU order: {_useGpuOrder})."); var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); - var execution = gpuLease.EnterExecutionScope(); var accelerator = gpuLease.Accelerator; var stream = gpuLease.Stream; int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy @@ -123,7 +122,6 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u ArrayPool.Shared.Return(orderArray); orderBuffer.Dispose(); - execution.Dispose(); gpuLease.Dispose(); } } diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs index 2a355d7..3a0d556 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs @@ -10,7 +10,6 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO public void Scan(ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime) { var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); - var execution = gpuLease.EnterExecutionScope(); var accelerator = gpuLease.Accelerator; var stream = gpuLease.Stream; int batchSize = GpuConstants.ScanBatchSize; // large batch improves GPU occupancy and avoids TDR @@ -61,7 +60,6 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO } foundBuffer.Dispose(); - execution.Dispose(); gpuLease.Dispose(); } } diff --git a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs index 7b17b48..bfb9851 100644 --- a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs +++ b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs @@ -194,21 +194,20 @@ internal static partial class PrimeOrderGpuHeuristics try { var lease = GpuKernelPool.GetKernel(useGpuOrder: true); - var execution = lease.EnterExecutionScope(); Accelerator accelerator = lease.Accelerator; AcceleratorStream stream = lease.Stream; var kernel = GetPartialFactorKernel(accelerator); - SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator); + SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator); - // TODO: We should create / reallocate these buffer only once or if the new required length is bigger than capacity. + // TODO: We should create / reallocate these buffer only once or if the new required length is bigger than capacity. var factorBuffer = accelerator.Allocate1D(primeTargets.Length); var exponentBuffer = accelerator.Allocate1D(exponentTargets.Length); var countBuffer = accelerator.Allocate1D(1); var remainingBuffer = accelerator.Allocate1D(1); var fullyFactoredBuffer = accelerator.Allocate1D(1); - // TODO: There is no need to clear these buffers because the kernel will always assign values within the required bounds. + // TODO: There is no need to clear these buffers because the kernel will always assign values within the required bounds. // factorBuffer.MemSetToZero(); // exponentBuffer.MemSetToZero(); // countBuffer.MemSetToZero(); @@ -242,13 +241,12 @@ internal static partial class PrimeOrderGpuHeuristics fullyFactoredBuffer.View.CopyToCPU(ref fullyFactoredFlag, 1); fullyFactored = fullyFactoredFlag != 0; - // TODO: These buffers shouldn't be disposed but rather reused accross call with the assigned accelerator. + // TODO: These buffers shouldn't be disposed but rather reused accross call with the assigned accelerator. factorBuffer.Dispose(); exponentBuffer.Dispose(); countBuffer.Dispose(); remainingBuffer.Dispose(); fullyFactoredBuffer.Dispose(); - execution.Dispose(); lease.Dispose(); return true; } @@ -362,12 +360,11 @@ internal static partial class PrimeOrderGpuHeuristics order = 0UL; var lease = GpuKernelPool.GetKernel(useGpuOrder: true); - var execution = lease.EnterExecutionScope(); Accelerator accelerator = lease.Accelerator; AcceleratorStream stream = lease.Stream; var kernel = GetOrderKernel(accelerator); - SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator); + SmallPrimeDeviceCache cache = GetSmallPrimeDeviceCache(accelerator); // TODO: These buffers should be created reallocated once and assigned to an accelerator. Callers should use their own // thread static cache to prevent other threads from using taking accelerators with pre-allocated buffers if they don't @@ -467,7 +464,6 @@ internal static partial class PrimeOrderGpuHeuristics stackProductBuffer.Dispose(); resultBuffer.Dispose(); statusBuffer.Dispose(); - execution.Dispose(); } } @@ -526,7 +522,6 @@ internal static partial class PrimeOrderGpuHeuristics private static bool TryComputeOnGpu(ReadOnlySpan exponents, ulong prime, in MontgomeryDivisorData divisorData, Span results) { var lease = GpuKernelPool.GetKernel(useGpuOrder: true); - var execution = lease.EnterExecutionScope(); Accelerator accelerator = lease.Accelerator; AcceleratorStream stream = lease.Stream; var kernel = GetPow2ModKernel(accelerator); @@ -551,7 +546,6 @@ internal static partial class PrimeOrderGpuHeuristics Console.WriteLine($"Exception for {prime} and exponents: {string.Join(",", exponents.ToArray())}."); exponentBuffer.Dispose(); remainderBuffer.Dispose(); - execution.Dispose(); lease.Dispose(); throw; } @@ -559,7 +553,6 @@ internal static partial class PrimeOrderGpuHeuristics remainderBuffer.View.CopyToCPU(ref MemoryMarshal.GetReference(results), exponents.Length); exponentBuffer.Dispose(); remainderBuffer.Dispose(); - execution.Dispose(); lease.Dispose(); return true; } @@ -587,7 +580,6 @@ internal static partial class PrimeOrderGpuHeuristics try { - var execution = lease.EnterExecutionScope(); Accelerator accelerator = lease.Accelerator; AcceleratorStream stream = lease.Stream; var kernel = GetPow2ModWideKernel(accelerator); @@ -628,7 +620,6 @@ internal static partial class PrimeOrderGpuHeuristics exponentBuffer.Dispose(); remainderBuffer.Dispose(); - execution.Dispose(); return true; } catch (Exception) diff --git a/PerfectNumbers.Core/MersenneNumberTester.cs b/PerfectNumbers.Core/MersenneNumberTester.cs index dcc17ea..de77340 100644 --- a/PerfectNumbers.Core/MersenneNumberTester.cs +++ b/PerfectNumbers.Core/MersenneNumberTester.cs @@ -147,7 +147,6 @@ public sealed class MersenneNumberTester( UInt128[] qs = qsBuffer; var gpuLease = GpuKernelPool.GetKernel(_useGpuOrder); - var execution = gpuLease.EnterExecutionScope(); var accelerator = gpuLease.Accelerator; var stream = gpuLease.Stream; var orderKernel = gpuLease.OrderKernel!; @@ -194,7 +193,6 @@ public sealed class MersenneNumberTester( qBuffer.Dispose(); } - execution.Dispose(); gpuLease.Dispose(); ArrayPool.Shared.Return(qs); } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index 4a9538b..41ef94a 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -22,7 +22,6 @@ internal static partial class PrimeOrderCalculator remaining = value; var lease = GpuKernelPool.GetKernel(useGpuOrder: true); - var execution = lease.EnterExecutionScope(); Accelerator accelerator = lease.Accelerator; AcceleratorStream stream = lease.Stream; @@ -64,7 +63,6 @@ internal static partial class PrimeOrderCalculator counts.Add(primeValue, exponent); } - execution.Dispose(); lease.Dispose(); ThreadStaticPools.UlongPool.Return(primeBufferArray); ThreadStaticPools.IntPool.Return(exponentBufferArray); diff --git a/PerfectNumbers.Core/ULongExtensions.Gpu.cs b/PerfectNumbers.Core/ULongExtensions.Gpu.cs index 2e37a22..085d41c 100644 --- a/PerfectNumbers.Core/ULongExtensions.Gpu.cs +++ b/PerfectNumbers.Core/ULongExtensions.Gpu.cs @@ -417,7 +417,6 @@ public static partial class ULongExtensions ulong result = 0UL; GpuKernelLease lease = GpuKernelPool.GetKernel(useGpuOrder: true); - var execution = lease.EnterExecutionScope(); Accelerator accelerator = lease.Accelerator; // Keep this commented out. It should never happen in production code. @@ -444,7 +443,6 @@ public static partial class ULongExtensions resultBuffer.Dispose(); exponentBuffer.Dispose(); - execution.Dispose(); lease.Dispose(); // Keep this commented. We don't want to catch any exceptions. All should crash the scanner. // catch (AcceleratorException)