using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu.Kernels; namespace PerfectNumbers.Core.Gpu.Accelerators; public sealed class Pow2MontgomeryAccelerator { #region Pool [ThreadStatic] private static Queue? _pool; private static AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators << 1); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Pow2MontgomeryAccelerator Rent(int primeTesterCapacity) { var pool = _pool ??= []; if (!pool.TryDequeue(out var gpu)) { Accelerator accelerator = _accelerators.Rent(); gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity); } else { gpu.Stream = gpu.Accelerator.CreateStream(); gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer,primeTesterCapacity); } return gpu; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Return(Pow2MontgomeryAccelerator gpu) { gpu.Stream!.Dispose(); gpu.Stream = null; _pool!.Enqueue(gpu); } internal static void Clear() { Queue? pool = _pool; if (pool != null) { while (pool.TryDequeue(out var lease)) { lease.Dispose(); } } } internal static void DisposeAll() { Queue? pool = _pool; if (pool != null) { while (pool.TryDequeue(out var lease)) { lease.Dispose(); } } } internal static void WarmUp() { Accelerator[]? accelerators = _accelerators.Accelerators; int acceleratorCount = accelerators.Length; for (var i = 0; i < acceleratorCount; i++) { Console.WriteLine($"Preparing accelerator {i}..."); var accelerator = accelerators[i]; AcceleratorStream stream = accelerator.CreateStream(); LastDigitGpuTables.EnsureStaticTables(accelerator, stream); // SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream); // _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None); // KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); stream.Synchronize(); stream.Dispose(); } } #endregion public readonly Accelerator Accelerator; public readonly MemoryBuffer1D Input; public readonly MemoryBuffer1D Output; public MemoryBuffer1D PrimeTestInput; public MemoryBuffer1D PrimeTestOutput; public MemoryBuffer1D HeuristicFlag; public MemoryBuffer1D, Stride1D.Dense> Pow2ModEntriesToTestOnDevice; public Dictionary Pow2ModEntriesToTestOnHost = []; public readonly MemoryBuffer1D DevicePrimesLastOne; public readonly MemoryBuffer1D DevicePrimesLastSeven; public readonly MemoryBuffer1D DevicePrimesLastThree; public readonly MemoryBuffer1D DevicePrimesLastNine; public readonly MemoryBuffer1D DevicePrimesPow2LastOne; public readonly MemoryBuffer1D DevicePrimesPow2LastSeven; public readonly MemoryBuffer1D DevicePrimesPow2LastThree; public readonly MemoryBuffer1D DevicePrimesPow2LastNine; public readonly Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> SmallPrimeSieveKernel; public AcceleratorStream? Stream; public readonly Action, MontgomeryDivisorData, ArrayView1D> ConvertToStandardKernel; public readonly Action, MontgomeryDivisorData, ArrayView1D> KeepMontgomeryKernel; public readonly Kernel CheckFactorsKernel; [ThreadStatic] private static Dictionary, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>? SmallPrimeSieveKernelPool; internal static Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> GetSmallPrimeSieveKernel(Accelerator accelerator) { var pool = SmallPrimeSieveKernelPool ??= []; if (pool.TryGetValue(accelerator, out var cached)) { return cached; } cached = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>(); pool[accelerator] = cached; return cached; } public void EnsureCapacity(int factorsCount, int primeTesterCapacity) { if (factorsCount > Pow2ModEntriesToTestOnDevice.Length) { Accelerator accelerator = Accelerator; Pow2ModEntriesToTestOnDevice.Dispose(); Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D>(factorsCount); } if (primeTesterCapacity > PrimeTestInput.Length) { PrimeTestInput?.Dispose(); PrimeTestOutput?.Dispose(); HeuristicFlag?.Dispose(); // lock (Accelerator) { PrimeTestInput = Accelerator.Allocate1D(primeTesterCapacity); PrimeTestOutput = Accelerator.Allocate1D(primeTesterCapacity); HeuristicFlag = Accelerator.Allocate1D(primeTesterCapacity); } } } public Pow2MontgomeryAccelerator(Accelerator accelerator, int factorsCount, int primeTesterCapacity) { Accelerator = accelerator; AcceleratorStream stream = accelerator.CreateStream(); Stream = stream; Input = accelerator.Allocate1D(1); Output = accelerator.Allocate1D(1); Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D>(factorsCount); PrimeTestInput = Accelerator.Allocate1D(primeTesterCapacity); PrimeTestOutput = Accelerator.Allocate1D(primeTesterCapacity); HeuristicFlag = Accelerator.Allocate1D(primeTesterCapacity); var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator, stream); DevicePrimesLastOne = sharedTables.DevicePrimesLastOne; DevicePrimesLastSeven = sharedTables.DevicePrimesLastSeven; DevicePrimesLastThree = sharedTables.DevicePrimesLastThree; DevicePrimesLastNine = sharedTables.DevicePrimesLastNine; DevicePrimesPow2LastOne = sharedTables.DevicePrimesPow2LastOne; DevicePrimesPow2LastSeven = sharedTables.DevicePrimesPow2LastSeven; DevicePrimesPow2LastThree = sharedTables.DevicePrimesPow2LastThree; DevicePrimesPow2LastNine = sharedTables.DevicePrimesPow2LastNine; var keepKernel = accelerator.LoadAutoGroupedStreamKernel, MontgomeryDivisorData, ArrayView1D>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery); var keepLauncher = KernelUtil.GetKernel(keepKernel); KeepMontgomeryKernel = keepLauncher.CreateLauncherDelegate, MontgomeryDivisorData, ArrayView1D>>(); var convertKernel = accelerator.LoadAutoGroupedStreamKernel, MontgomeryDivisorData, ArrayView1D>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard); var convertLauncher = KernelUtil.GetKernel(convertKernel); ConvertToStandardKernel = convertLauncher.CreateLauncherDelegate, MontgomeryDivisorData, ArrayView1D>>(); var checkFactorsKernel = accelerator.LoadStreamKernel, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D>(PrimeOrderGpuHeuristics.CheckFactorsKernel); CheckFactorsKernel = KernelUtil.GetKernel(checkFactorsKernel); SmallPrimeSieveKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>(); } public void Dispose() { Input.Dispose(); Output.Dispose(); Stream?.Dispose(); Pow2ModEntriesToTestOnDevice.Dispose(); PrimeTestInput?.Dispose(); PrimeTestOutput?.Dispose(); HeuristicFlag?.Dispose(); // Stream is disposed upon return to the queue, so we don't need to dispose it here. // Stream.Dispose(); // These resources are shared between GPU leases // ReleaseSharedTables(_sharedTables); // Accelerator.Dispose(); // Context.Dispose(); // Accelerator.Dispose(); } }