using System.Collections.Concurrent; using ILGPU; using ILGPU.Algorithms; using ILGPU.Runtime; namespace PerfectNumbers.Core.Gpu; public sealed class GpuKernelLease { private static readonly ConcurrentQueue Pool = new(); private GpuContextLease _gpu; public KernelContainer Kernels; public AcceleratorStream Stream; #pragma warning disable CS8618 // _gpu and Stream will be set by Rent method private GpuKernelLease(KernelContainer kernels) #pragma warning restore CS8618 { Kernels = kernels; } internal static GpuKernelLease Rent(GpuContextLease gpu, AcceleratorStream stream, KernelContainer kernels) { if (!Pool.TryDequeue(out var lease)) { lease = new GpuKernelLease(kernels); } lease._gpu = gpu; lease.Kernels = kernels; lease.Stream = stream; return lease; } public Accelerator Accelerator => _gpu?.Accelerator ?? throw new NullReferenceException("GPU context is already released"); public Action, ArrayView> OrderKernel { get { var accel = Accelerator; // avoid capturing 'this' in lambda return GpuKernelPool.InitOnce(ref Kernels!.Order, () => { var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView>(OrderKernels.OrderKernelScan); var kernel = KernelUtil.GetKernel(loaded); return kernel.CreateLauncherDelegate, ArrayView>>(); }); } } public Action, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D> Pow2ModKernel { get { var accel = Accelerator; return GpuKernelPool.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>>(); }); } } public Action, ArrayView1D> IncrementalKernel { get { var accel = Accelerator; // avoid capturing 'this' in lambda return GpuKernelPool.InitOnce(ref Kernels!.Incremental, () => { var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalKernelScan); var kernel = KernelUtil.GetKernel(loaded); return kernel.CreateLauncherDelegate, ArrayView1D>>(); }); } } public Action, ArrayView1D> IncrementalOrderKernel { get { var accel = Accelerator; return GpuKernelPool.InitOnce(ref Kernels!.IncrementalOrder, () => { var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalOrderKernelScan); var kernel = KernelUtil.GetKernel(loaded); return kernel.CreateLauncherDelegate, ArrayView1D>>(); }); } } public Action, ArrayView1D> Pow2ModOrderKernel { get { var accel = Accelerator; return GpuKernelPool.InitOnce(ref Kernels!.Pow2ModOrder, () => { var loaded = accel.LoadAutoGroupedStreamKernel, ArrayView1D>(Pow2ModKernels.Pow2ModOrderKernelScan); var kernel = KernelUtil.GetKernel(loaded); return kernel.CreateLauncherDelegate, ArrayView1D>>(); }); } } public Action, int, MontgomeryDivisorData, ArrayView1D, ArrayView1D> SpecialMaxKernel { get { var accel = Accelerator; return GpuKernelPool.InitOnce(ref Kernels!.SpecialMax, () => { var loaded = accel.LoadAutoGroupedStreamKernel, int, MontgomeryDivisorData, ArrayView1D, ArrayView1D>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel); var kernel = KernelUtil.GetKernel(loaded); return kernel.CreateLauncherDelegate, int, MontgomeryDivisorData, ArrayView1D, ArrayView1D>>(); }); } } public Action, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D> SmallPrimeFactorKernel { get { var accel = Accelerator; return GpuKernelPool.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>>(); }); } } public void Dispose() { Stream.Dispose(); _gpu.Dispose(); Pool.Enqueue(this); GpuPrimeWorkLimiter.Release(); } }