diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 8d9705a..dc1e607 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -203,46 +203,90 @@ public static bool IsPrimeCpu(ulong n, CancellationToken ct) internal static class PrimeTesterGpuContextPool { - internal sealed class PooledContext + private static readonly ConcurrentQueue Pool = new(); + + internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1) { - public Context Context { get; } - - public Accelerator Accelerator { get; } - - public Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> Kernel { get; } - - public Action, ulong, ArrayView> HeuristicTrialDivisionKernel { get; } - - public MemoryBuffer1D DevicePrimesDefault { get; } - - public MemoryBuffer1D DevicePrimesLastOne { get; } - - public MemoryBuffer1D DevicePrimesLastSeven { get; } - - public MemoryBuffer1D DevicePrimesLastThree { get; } - - public MemoryBuffer1D DevicePrimesLastNine { get; } - - public MemoryBuffer1D DevicePrimesPow2Default { get; } - - public MemoryBuffer1D DevicePrimesPow2LastOne { get; } - - public MemoryBuffer1D DevicePrimesPow2LastSeven { get; } - - public MemoryBuffer1D DevicePrimesPow2LastThree { get; } - - public MemoryBuffer1D DevicePrimesPow2LastNine { get; } - - public MemoryBuffer1D Input { get; private set; } - - public MemoryBuffer1D Output { get; private set; } - - public int BufferCapacity { get; private set; } - - public PooledContext(int minBufferCapacity) + if (!Pool.TryDequeue(out var lease)) { - Context = Context.CreateDefault(); - Accelerator = Context.GetPreferredDevice(false).CreateAccelerator(Context); + lease = new PrimeTesterGpuContextLease(minBufferCapacity); + } + else + { + lease.EnsureCapacity(minBufferCapacity); + } + + lease.ResetReturnFlag(); + return lease; + } + + internal static void Return(PrimeTesterGpuContextLease lease) + { + lease.Accelerator.Synchronize(); + Pool.Enqueue(lease); + } + + internal static void DisposeAll() + { + while (Pool.TryDequeue(out var lease)) + { + lease.DisposeResources(); + } + } + + internal static void Clear(Accelerator accelerator) + { + if (accelerator is null) + { + return; + } + + var retained = new List(); + while (Pool.TryDequeue(out var lease)) + { + if (lease.Accelerator == accelerator) + { + lease.DisposeResources(); + } + else + { + retained.Add(lease); + } + } + + int retainedCount = retained.Count; + for (int i = 0; i < retainedCount; i++) + { + Pool.Enqueue(retained[i]); + } + } + + internal sealed class PrimeTesterGpuContextLease : IDisposable + { + private bool _returned; + + internal readonly Context AcceleratorContext; + public readonly Accelerator Accelerator; + public readonly Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> Kernel; + public readonly Action, ulong, ArrayView> HeuristicTrialDivisionKernel; + public readonly MemoryBuffer1D DevicePrimesDefault; + public readonly MemoryBuffer1D DevicePrimesLastOne; + public readonly MemoryBuffer1D DevicePrimesLastSeven; + public readonly MemoryBuffer1D DevicePrimesLastThree; + public readonly MemoryBuffer1D DevicePrimesLastNine; + public readonly MemoryBuffer1D DevicePrimesPow2Default; + public readonly MemoryBuffer1D DevicePrimesPow2LastOne; + public readonly MemoryBuffer1D DevicePrimesPow2LastSeven; + public readonly MemoryBuffer1D DevicePrimesPow2LastThree; + public readonly MemoryBuffer1D DevicePrimesPow2LastNine; + public MemoryBuffer1D Input; + public MemoryBuffer1D Output; + public int BufferCapacity; + + internal PrimeTesterGpuContextLease(int minBufferCapacity) + { + AcceleratorContext = Context.CreateDefault(); + Accelerator = AcceleratorContext.GetPreferredDevice(false).CreateAccelerator(AcceleratorContext); Kernel = Accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(PrimeTesterKernels.SmallPrimeSieveKernel); HeuristicTrialDivisionKernel = Accelerator.LoadAutoGroupedStreamKernel, ulong, ArrayView>(PrimeTesterKernels.HeuristicTrialDivisionKernel); @@ -289,29 +333,41 @@ public static bool IsPrimeCpu(ulong n, CancellationToken ct) BufferCapacity = minBufferCapacity < 1 ? 1 : minBufferCapacity; Input = Accelerator.Allocate1D(BufferCapacity); Output = Accelerator.Allocate1D(BufferCapacity); + _returned = false; } public void EnsureCapacity(int minCapacity) { - if (minCapacity < 1) - { - minCapacity = 1; - } - - if (minCapacity <= BufferCapacity) + int required = minCapacity < 1 ? 1 : minCapacity; + if (required <= BufferCapacity) { return; } Input.Dispose(); Output.Dispose(); - - Input = Accelerator.Allocate1D(minCapacity); - Output = Accelerator.Allocate1D(minCapacity); - BufferCapacity = minCapacity; + Input = Accelerator.Allocate1D(required); + Output = Accelerator.Allocate1D(required); + BufferCapacity = required; } - public void DisposeResources() + internal void ResetReturnFlag() + { + _returned = false; + } + + public void Dispose() + { + if (_returned) + { + return; + } + + _returned = true; + PrimeTesterGpuContextPool.Return(this); + } + + internal void DisposeResources() { Input.Dispose(); Output.Dispose(); @@ -326,115 +382,7 @@ public static bool IsPrimeCpu(ulong n, CancellationToken ct) DevicePrimesPow2LastThree.Dispose(); DevicePrimesPow2LastNine.Dispose(); Accelerator.Dispose(); - Context.Dispose(); - } - } - - private static readonly ConcurrentQueue Pool = new(); - - internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1) - { - if (!Pool.TryDequeue(out var ctx)) - { - ctx = new PooledContext(minBufferCapacity); - } - else - { - ctx.EnsureCapacity(minBufferCapacity); - } - - return new PrimeTesterGpuContextLease(ctx); - } - - internal static void DisposeAll() - { - while (Pool.TryDequeue(out var ctx)) - { - ctx.DisposeResources(); - } - } - - internal static void Clear(Accelerator accelerator) - { - if (accelerator is null) - { - return; - } - - var retained = new List(); - while (Pool.TryDequeue(out var ctx)) - { - if (ctx.Accelerator == accelerator) - { - ctx.DisposeResources(); - } - else - { - retained.Add(ctx); - } - } - - for (int i = 0; i < retained.Count; i++) - { - Pool.Enqueue(retained[i]); - } - } - - private static void Return(PooledContext ctx) - { - ctx.Accelerator.Synchronize(); - Pool.Enqueue(ctx); - } - - internal sealed class PrimeTesterGpuContextLease : IDisposable - { - private readonly PooledContext _ctx; - - internal PrimeTesterGpuContextLease(PooledContext ctx) - { - _ctx = ctx; - } - - public Accelerator Accelerator => _ctx.Accelerator; - - public Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> Kernel => _ctx.Kernel; - - public Action, ulong, ArrayView> HeuristicTrialDivisionKernel => _ctx.HeuristicTrialDivisionKernel; - - public MemoryBuffer1D Input => _ctx.Input; - - public MemoryBuffer1D Output => _ctx.Output; - - public MemoryBuffer1D DevicePrimesDefault => _ctx.DevicePrimesDefault; - - public MemoryBuffer1D DevicePrimesLastOne => _ctx.DevicePrimesLastOne; - - public MemoryBuffer1D DevicePrimesLastSeven => _ctx.DevicePrimesLastSeven; - - public MemoryBuffer1D DevicePrimesLastThree => _ctx.DevicePrimesLastThree; - - public MemoryBuffer1D DevicePrimesLastNine => _ctx.DevicePrimesLastNine; - - public MemoryBuffer1D DevicePrimesPow2Default => _ctx.DevicePrimesPow2Default; - - public MemoryBuffer1D DevicePrimesPow2LastOne => _ctx.DevicePrimesPow2LastOne; - - public MemoryBuffer1D DevicePrimesPow2LastSeven => _ctx.DevicePrimesPow2LastSeven; - - public MemoryBuffer1D DevicePrimesPow2LastThree => _ctx.DevicePrimesPow2LastThree; - - public MemoryBuffer1D DevicePrimesPow2LastNine => _ctx.DevicePrimesPow2LastNine; - - public int BufferCapacity => _ctx.BufferCapacity; - - public void EnsureCapacity(int minCapacity) - { - _ctx.EnsureCapacity(minCapacity); - } - - public void Dispose() - { - Return(_ctx); + AcceleratorContext.Dispose(); } } }