using System.Collections.Concurrent; using ILGPU; using ILGPU.Runtime; namespace PerfectNumbers.Core.Gpu; public sealed class ScratchBuffer { public MemoryBuffer1D SmallPrimeFactorPrimeSlots { get; private set; } public MemoryBuffer1D SmallPrimeFactorExponentSlots { get; private set; } public MemoryBuffer1D SmallPrimeFactorCountSlot { get; private set; } public MemoryBuffer1D SmallPrimeFactorRemainingSlot { get; private set; } public MemoryBuffer1D SpecialMaxFactors { get; private set; } public MemoryBuffer1D SpecialMaxCandidates { get; private set; } public MemoryBuffer1D SpecialMaxResult { get; private set; } internal readonly Accelerator _accelerator; public ScratchBuffer(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(); SmallPrimeFactorPrimeSlots = _accelerator.Allocate1D(newSize); SmallPrimeFactorExponentSlots = _accelerator.Allocate1D(newSize); } // else // { // SmallPrimeFactorPrimeSlots.MemSetToZero(); // SmallPrimeFactorExponentSlots.MemSetToZero(); // } } public void ResizeSpecialMaxFactors(int newSize) { if (SpecialMaxFactors.Length < newSize) { SpecialMaxFactors.Dispose(); SpecialMaxCandidates.Dispose(); SpecialMaxFactors = _accelerator.Allocate1D(newSize); SpecialMaxCandidates = _accelerator.Allocate1D(newSize); } // else // { // SpecialMaxFactors.MemSetToZero(); // SpecialMaxCandidates.MemSetToZero(); // } } } 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 ScratchBuffer 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 ScratchBuffer(accelerator, smallPrimeFactorSlotCount, specialMaxFactorCapacity); } public static void Return(ScratchBuffer 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); } } }