using ILGPU; using ILGPU.Runtime; namespace PerfectNumbers.Core.Gpu; public sealed class GpuScratchBuffer { internal readonly Accelerator _accelerator; public readonly MemoryBuffer1D SmallPrimeFactorCountSlot; public MemoryBuffer1D SmallPrimeFactorExponentSlots; public MemoryBuffer1D SmallPrimeFactorPrimeSlots; public readonly MemoryBuffer1D SmallPrimeFactorRemainingSlot; public MemoryBuffer1D SpecialMaxCandidates; public MemoryBuffer1D SpecialMaxFactors; public readonly MemoryBuffer1D SpecialMaxResult; public GpuScratchBuffer(Accelerator accelerator, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity) { _accelerator = accelerator; // lock (accelerator) { SmallPrimeFactorPrimeSlots = accelerator.Allocate1D(smallPrimeFactorSlotCount); SmallPrimeFactorExponentSlots = accelerator.Allocate1D(smallPrimeFactorSlotCount); SmallPrimeFactorCountSlot = accelerator.Allocate1D(1); SmallPrimeFactorRemainingSlot = accelerator.Allocate1D(1); SpecialMaxFactors = accelerator.Allocate1D(specialMaxFactorCapacity); SpecialMaxCandidates = accelerator.Allocate1D(specialMaxFactorCapacity); SpecialMaxResult = accelerator.Allocate1D(1); } } public void ResizeSmallPrimeFactorSlots(int newSize) { if (SmallPrimeFactorPrimeSlots.Length < newSize) { SmallPrimeFactorPrimeSlots.Dispose(); SmallPrimeFactorExponentSlots.Dispose(); // lock(_accelerator) { SmallPrimeFactorPrimeSlots = _accelerator.Allocate1D(newSize); SmallPrimeFactorExponentSlots = _accelerator.Allocate1D(newSize); } } } public void ResizeSpecialMaxFactors(int newSize) { if (SpecialMaxFactors.Length < newSize) { SpecialMaxFactors.Dispose(); SpecialMaxCandidates.Dispose(); // lock(_accelerator) { SpecialMaxFactors = _accelerator.Allocate1D(newSize); SpecialMaxCandidates = _accelerator.Allocate1D(newSize); } } } internal void Dispose() { SmallPrimeFactorPrimeSlots.Dispose(); SmallPrimeFactorExponentSlots.Dispose(); SmallPrimeFactorCountSlot.Dispose(); SmallPrimeFactorRemainingSlot.Dispose(); SpecialMaxFactors.Dispose(); SpecialMaxCandidates.Dispose(); SpecialMaxResult.Dispose(); } } public static class GpuScratchBufferPool { [ThreadStatic] private static Dictionary>? _pools; // private static readonly ConcurrentDictionary> _pools = new(); private const int DefaultSmallPrimeFactorSlotCount = 64; // From PrimeOrderCalculator.Gpu.cs private const int DefaultSpecialMaxFactorCapacity = 1024; // A reasonable default, will be resized if needed public static GpuScratchBuffer Rent(Accelerator accelerator, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity) { const int WarmUpCapacity = 100; // var pool = _pools.GetOrAdd(accelerator, _ => new ConcurrentQueue()); if (_pools is not { }) { _pools = new(WarmUpCapacity); WarmUp(WarmUpCapacity, DefaultSmallPrimeFactorSlotCount, DefaultSpecialMaxFactorCapacity); } var pools = _pools; if (!pools.TryGetValue(accelerator, out var pool)) { pool = new(); pools[accelerator] = pool; } // var pool = _pools.GetOrAdd(accelerator, _ => new ConcurrentQueue()); if (pool.TryDequeue(out var buffer)) { if (buffer.SmallPrimeFactorPrimeSlots.Length < smallPrimeFactorSlotCount || buffer.SpecialMaxFactors.Length < specialMaxFactorCapacity) { // throw new InvalidOperationException(); // lock (accelerator) { // Console.WriteLine($"Resizing GPU scratch buffer from pool ({buffer.SmallPrimeFactorPrimeSlots.Length} / {smallPrimeFactorSlotCount}), ({buffer.SpecialMaxFactors.Length}/{specialMaxFactorCapacity})"); buffer.ResizeSmallPrimeFactorSlots(smallPrimeFactorSlotCount); buffer.ResizeSpecialMaxFactors(specialMaxFactorCapacity); } } return buffer; } return new GpuScratchBuffer(accelerator, smallPrimeFactorSlotCount, specialMaxFactorCapacity); } public static void Return(GpuScratchBuffer buffer) { if (_pools!.TryGetValue(buffer._accelerator, out var pool)) { pool.Enqueue(buffer); } } public static void WarmUp(int count, int smallPrimeFactorSlots, int specialMaxFactorCapacity) { // Accelerator accelerator = SharedGpuContext.Accelerator; // for (int i = 0; i < count; i++) // { // var buffer = Rent(accelerator, smallPrimeFactorSlots, specialMaxFactorCapacity); // Return(buffer); // } } public static void DisposeAll() { var pool = _pools; if (pool is not null) { foreach (var queue in pool.Values) { while (queue.TryDequeue(out var buffer)) { buffer.Dispose(); } } pool.Clear(); _pools = null; } } }