using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Runtime; namespace PerfectNumbers.Core.Gpu.Accelerators; internal sealed class PrimeTesterByLastDigitAccelerator { #region Pool [ThreadStatic] private static Queue? Pool; // internal static void EnsureStaticTables(Accelerator accelerator) // { // var tables = CreateSharedTables(accelerator); // } internal static void WarmUp() { Accelerator[]? accelerators = AcceleratorPool.Shared.Accelerators; int acceleratorCount = accelerators.Length; Span streams = new(new AcceleratorStream[acceleratorCount]); 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); streams[i] = stream; } for (var i = 0; i < acceleratorCount; i++) { AcceleratorStream stream = streams[i]; stream.Synchronize(); stream.Dispose(); Console.WriteLine($"Accelerator {i} is ready"); } } internal static PrimeTesterByLastDigitAccelerator Rent(int minBufferCapacity = 1) { var pool = Pool ??= new(); if (!pool.TryDequeue(out var lease)) { lease = new PrimeTesterByLastDigitAccelerator(minBufferCapacity); } else { lease.EnsureCapacity(minBufferCapacity); } return lease; } internal static void Return(PrimeTesterByLastDigitAccelerator lease) { var pool = Pool ??= new(); pool.Enqueue(lease); } internal static void DisposeAll() { if (Pool != null) { while (Pool.TryDequeue(out var lease)) { lease.DisposeResources(); } } } internal static void Clear() { if (Pool != null) { var retained = new List(); while (Pool.TryDequeue(out var lease)) { lease.DisposeResources(); } } } #endregion private readonly LastDigitGpuTables _sharedTables; internal readonly Context Context; public readonly Accelerator Accelerator; private AcceleratorStream? _stream; public AcceleratorStream Stream { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _stream ?? Accelerator.CreateStream(); } [ThreadStatic] public static Dictionary, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>? SmallPrimeSieveKernel; internal static Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> GetSmallPrimeSieveKernel(Accelerator accelerator) { var pool = SmallPrimeSieveKernel ??= []; 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; } [ThreadStatic] public static Dictionary, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>>? HeuristicTrialDivisionKernel; public MemoryBuffer1D Input = null!; public MemoryBuffer1D Output = null!; public MemoryBuffer1D HeuristicFlag = null!; public int BufferCapacity; public MemoryBuffer1D DevicePrimesLastOne => _sharedTables.DevicePrimesLastOne; public MemoryBuffer1D DevicePrimesLastSeven => _sharedTables.DevicePrimesLastSeven; public MemoryBuffer1D DevicePrimesLastThree => _sharedTables.DevicePrimesLastThree; public MemoryBuffer1D DevicePrimesLastNine => _sharedTables.DevicePrimesLastNine; public MemoryBuffer1D DevicePrimesPow2LastOne => _sharedTables.DevicePrimesPow2LastOne; public MemoryBuffer1D DevicePrimesPow2LastSeven => _sharedTables.DevicePrimesPow2LastSeven; public MemoryBuffer1D DevicePrimesPow2LastThree => _sharedTables.DevicePrimesPow2LastThree; public MemoryBuffer1D DevicePrimesPow2LastNine => _sharedTables.DevicePrimesPow2LastNine; internal PrimeTesterByLastDigitAccelerator(int minBufferCapacity) { Accelerator accelerator = AcceleratorPool.Shared.Rent(); Context = accelerator.Context; Accelerator = accelerator; AcceleratorStream stream = accelerator.CreateStream(); var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator, stream); _sharedTables = sharedTables; EnsureCapacity(minBufferCapacity); stream.Synchronize(); stream.Dispose(); } public void EnsureCapacity(int minCapacity) { if (minCapacity <= BufferCapacity) { return; } Input?.Dispose(); Output?.Dispose(); HeuristicFlag?.Dispose(); // lock (Accelerator) { Input = Accelerator.Allocate1D(minCapacity); Output = Accelerator.Allocate1D(minCapacity); HeuristicFlag = Accelerator.Allocate1D(minCapacity); } BufferCapacity = minCapacity; } public void Dispose() { _stream?.Dispose(); _stream = null; Return(this); } internal void DisposeResources() { Input?.Dispose(); Output?.Dispose(); HeuristicFlag?.Dispose(); _stream?.Dispose(); _stream = null; // ReleaseSharedTables(_sharedTables); // These resources are shared between GPU leases // Accelerator.Dispose(); // AcceleratorContext.Dispose(); } }