diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index aa8d2c9..af4a93b 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -67,7 +67,6 @@ internal static class Program PrimeTester.WarmUpGpuKernels(1024); // PrimeTester.WarmUpGpuKernels(gpuPrimeThreads >> 4); Console.WriteLine("Warming up scratch buffers"); - GpuScratchBufferPool.WarmUp(gpuPrimeThreads >> 4, 64, 128); Console.WriteLine("Starting up threads..."); _ = UnboundedTaskScheduler.Instance; int blockSize = Math.Max(1, _cliArguments.BlockSize); diff --git a/PerfectNumbers.Core/Gpu/Accelerators/AcceleratorStreamPool.cs b/PerfectNumbers.Core/Gpu/Accelerators/AcceleratorStreamPool.cs index 6282cca..1a334b6 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/AcceleratorStreamPool.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/AcceleratorStreamPool.cs @@ -6,21 +6,20 @@ namespace PerfectNumbers.Core.Gpu.Accelerators; public static class AcceleratorStreamPool { - private static readonly Dictionary> _streams = new(PerfectNumberConstants .RollingAccelerators); - // private static readonly ConcurrentDictionary _streams = new(AcceleratorPool.Shared.Accelerators.ToDictionary(static x => x, CreateStream)); + private static readonly Dictionary> _streams = new(PerfectNumberConstants .RollingAccelerators << 1); private static SemaphoreSlim CreateLock() => new(PerfectNumberConstants.ThreadsByStream); - private static AcceleratorStream CreateStream(Accelerator accelerator) => accelerator.CreateStream(); - private static readonly Dictionary _locks = new(AcceleratorPool.Shared.Accelerators.ToDictionary(x => x, static x => CreateLock())); + private static readonly Dictionary _locks = new(PerfectNumberConstants.RollingAccelerators << 1); + + // private static int _rented; public static AcceleratorStream Rent(Accelerator accelerator) { var streamLock = _locks[accelerator]; - // var streamLock = _locks.GetOrAdd(accelerator, static _ => CreateLock()); + var queue = _streams[accelerator]; streamLock.Wait(); - var queue = _streams[accelerator]; return queue.TryDequeue(out var stream) ? stream @@ -40,13 +39,5 @@ public static class AcceleratorStreamPool { _streams.Add(accelerator, new()); _ = _locks.TryAdd(accelerator, CreateLock()); - - // ConcurrentQueue streamQueue = new(); - // var stream = accelerator.CreateStream(); - // // var stream = accelerator.CreateStream(); - // streamQueue.Enqueue(stream); - // _streams[accelerator] = streamQueue; - // _locks[accelerator] = CreateLock(); - // stream.Synchronize(); } } diff --git a/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs index f36f112..2e04865 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Runtime; @@ -6,10 +6,13 @@ namespace PerfectNumbers.Core.Gpu.Accelerators; internal sealed class LastDigitGpuTables { - private static readonly ConcurrentDictionary _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators * 2); + private static readonly Dictionary _sharedTables = new(PerfectNumberConstants.RollingAccelerators); - internal static LastDigitGpuTables EnsureStaticTables(Accelerator accelerator, AcceleratorStream stream) - => _sharedTables.GetOrAdd(accelerator, _ => new LastDigitGpuTables(accelerator, stream)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static LastDigitGpuTables EnsureStaticTables(Accelerator accelerator) => _sharedTables[accelerator]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void WarmUp(Accelerator accelerator, AcceleratorStream stream) => _sharedTables[accelerator] = new LastDigitGpuTables(accelerator, stream); internal LastDigitGpuTables(Accelerator accelerator, AcceleratorStream stream) { @@ -21,18 +24,14 @@ internal sealed class LastDigitGpuTables var primesPow2LastSeven = DivisorGenerator.SmallPrimesPow2LastSeven; var primesPow2LastThree = DivisorGenerator.SmallPrimesPow2LastThree; var primesPow2LastNine = DivisorGenerator.SmallPrimesPow2LastNine; - - // lock (accelerator) - { - DevicePrimesLastOne = accelerator.Allocate1D(stream, primesLastOne); - DevicePrimesLastSeven = accelerator.Allocate1D(stream, primesLastSeven); - DevicePrimesLastThree = accelerator.Allocate1D(stream, primesLastThree); - DevicePrimesLastNine = accelerator.Allocate1D(stream, primesLastNine); - DevicePrimesPow2LastOne = accelerator.Allocate1D(stream, primesPow2LastOne); - DevicePrimesPow2LastSeven = accelerator.Allocate1D(stream, primesPow2LastSeven); - DevicePrimesPow2LastThree = accelerator.Allocate1D(stream, primesPow2LastThree); - DevicePrimesPow2LastNine = accelerator.Allocate1D(stream, primesPow2LastNine); - } + DevicePrimesLastOne = accelerator.Allocate1D(stream, primesLastOne); + DevicePrimesLastSeven = accelerator.Allocate1D(stream, primesLastSeven); + DevicePrimesLastThree = accelerator.Allocate1D(stream, primesLastThree); + DevicePrimesLastNine = accelerator.Allocate1D(stream, primesLastNine); + DevicePrimesPow2LastOne = accelerator.Allocate1D(stream, primesPow2LastOne); + DevicePrimesPow2LastSeven = accelerator.Allocate1D(stream, primesPow2LastSeven); + DevicePrimesPow2LastThree = accelerator.Allocate1D(stream, primesPow2LastThree); + DevicePrimesPow2LastNine = accelerator.Allocate1D(stream, primesPow2LastNine); } internal readonly MemoryBuffer1D DevicePrimesLastOne; diff --git a/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs similarity index 61% rename from PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs rename to PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs index 7160552..81813cf 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs @@ -5,27 +5,26 @@ using PerfectNumbers.Core.Gpu.Kernels; namespace PerfectNumbers.Core.Gpu.Accelerators; -public sealed class Pow2MontgomeryAccelerator +public sealed class PrimeOrderCalculatorAccelerator { #region Pool [ThreadStatic] - private static Queue? _pool; + private static Queue? _pool; - private static AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators); + private static readonly AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Pow2MontgomeryAccelerator Rent(int primeTesterCapacity) + public static PrimeOrderCalculatorAccelerator Rent(int primeTesterCapacity) { var pool = _pool ??= []; if (!pool.TryDequeue(out var gpu)) { Accelerator accelerator = _accelerators.Rent(); - gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity); + gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity, PerfectNumberConstants.DefaultSmallPrimeFactorSlotCount, PerfectNumberConstants.DefaultSpecialMaxFactorCapacity); } else { - // gpu.Stream = gpu.Accelerator.CreateStream(); gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity); } @@ -33,33 +32,28 @@ public sealed class Pow2MontgomeryAccelerator } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Return(Pow2MontgomeryAccelerator gpu) - { - // gpu.Stream!.Dispose(); - // gpu.Stream = null; - _pool!.Enqueue(gpu); - } + public static void Return(PrimeOrderCalculatorAccelerator gpu) => _pool!.Enqueue(gpu); internal static void Clear() { - Queue? pool = _pool; + Queue? pool = _pool; if (pool != null) { - while (pool.TryDequeue(out var lease)) + while (pool.TryDequeue(out var gpu)) { - lease.Dispose(); + gpu.Dispose(); } } } internal static void DisposeAll() { - Queue? pool = _pool; + Queue? pool = _pool; if (pool != null) { - while (pool.TryDequeue(out var lease)) + while (pool.TryDequeue(out var gpu)) { - lease.Dispose(); + gpu.Dispose(); } } } @@ -73,17 +67,15 @@ public sealed class Pow2MontgomeryAccelerator Console.WriteLine($"Preparing accelerator {i}..."); var accelerator = accelerators[i]; AcceleratorStreamPool.WarmUp(accelerator); - // AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); + // Don't take this from the pool as quick uploads of data to the accelerator consumes much of GPU's memory and throws. AcceleratorStream stream = accelerator.CreateStream(); - LastDigitGpuTables.EnsureStaticTables(accelerator, stream); + LastDigitGpuTables.WarmUp(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(); - - // AcceleratorStreamPool.Return(stream); } } @@ -105,31 +97,20 @@ public sealed class Pow2MontgomeryAccelerator public readonly MemoryBuffer1D DevicePrimesPow2LastSeven; public readonly MemoryBuffer1D DevicePrimesPow2LastThree; public readonly MemoryBuffer1D DevicePrimesPow2LastNine; + 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 readonly Kernel SmallPrimeSieveKernel; - - // public AcceleratorStream? Stream; - public readonly Kernel CheckFactorsKernel; public readonly Kernel ConvertToStandardKernel; public readonly Kernel KeepMontgomeryKernel; public readonly Kernel PollardRhoKernel; - [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) @@ -141,35 +122,62 @@ public sealed class Pow2MontgomeryAccelerator if (primeTesterCapacity > PrimeTestInput.Length) { - PrimeTestInput?.Dispose(); - OutputByte?.Dispose(); - HeuristicFlag?.Dispose(); + PrimeTestInput.Dispose(); + OutputByte.Dispose(); + HeuristicFlag.Dispose(); - // lock (Accelerator) - { - PrimeTestInput = Accelerator.Allocate1D(primeTesterCapacity); - OutputByte = Accelerator.Allocate1D(primeTesterCapacity); - HeuristicFlag = Accelerator.Allocate1D(primeTesterCapacity); - } + PrimeTestInput = Accelerator.Allocate1D(primeTesterCapacity); + OutputByte = Accelerator.Allocate1D(primeTesterCapacity); + HeuristicFlag = Accelerator.Allocate1D(primeTesterCapacity); } } - public Pow2MontgomeryAccelerator(Accelerator accelerator, int factorsCount, int primeTesterCapacity) + public void EnsureSmallPrimeFactorSlotsCapacity(int newSize) + { + // Console.WriteLine($"Resizing GPU scratch buffer from pool ({buffer.SmallPrimeFactorPrimeSlots.Length} / {smallPrimeFactorSlotCount}), ({buffer.SpecialMaxFactors.Length}/{specialMaxFactorCapacity})"); + if (SmallPrimeFactorPrimeSlots.Length < newSize) + { + SmallPrimeFactorPrimeSlots.Dispose(); + SmallPrimeFactorExponentSlots.Dispose(); + + var accelerator = Accelerator; + SmallPrimeFactorPrimeSlots = accelerator.Allocate1D(newSize); + SmallPrimeFactorExponentSlots = accelerator.Allocate1D(newSize); + } + } + + public void EnsureSpecialMaxFactorsCapacity(int newSize) + { + if (SpecialMaxFactors.Length < newSize) + { + SpecialMaxFactors.Dispose(); + SpecialMaxCandidates.Dispose(); + + var accelerator = Accelerator; + SpecialMaxFactors = accelerator.Allocate1D(newSize); + SpecialMaxCandidates = accelerator.Allocate1D(newSize); + } + } + + public PrimeOrderCalculatorAccelerator(Accelerator accelerator, int factorsCount, int primeTesterCapacity, int smallPrimeFactorSlotCount, int specialMaxFactorCapacity) { Accelerator = accelerator; - AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); - // AcceleratorStream stream = accelerator.DefaultStream; - // AcceleratorStream stream = accelerator.CreateStream(); - // Stream = stream; - Input = accelerator.Allocate1D(1); OutputUlong = accelerator.Allocate1D(1); Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D>(factorsCount); PrimeTestInput = Accelerator.Allocate1D(primeTesterCapacity); OutputByte = Accelerator.Allocate1D(primeTesterCapacity); HeuristicFlag = Accelerator.Allocate1D(primeTesterCapacity); + SmallPrimeFactorPrimeSlots = accelerator.Allocate1D(smallPrimeFactorSlotCount); + SmallPrimeFactorExponentSlots = accelerator.Allocate1D(smallPrimeFactorSlotCount); + SmallPrimeFactorCountSlot = accelerator.Allocate1D(1); + SmallPrimeFactorRemainingSlot = accelerator.Allocate1D(1); - var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator, stream); + SpecialMaxFactors = accelerator.Allocate1D(specialMaxFactorCapacity); + SpecialMaxCandidates = accelerator.Allocate1D(specialMaxFactorCapacity); + SpecialMaxResult = accelerator.Allocate1D(1); + + var sharedTables = LastDigitGpuTables.EnsureStaticTables(accelerator); DevicePrimesLastOne = sharedTables.DevicePrimesLastOne; DevicePrimesLastSeven = sharedTables.DevicePrimesLastSeven; DevicePrimesLastThree = sharedTables.DevicePrimesLastThree; @@ -191,27 +199,27 @@ public sealed class Pow2MontgomeryAccelerator SmallPrimeSieveKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(PrimeTesterKernels.SmallPrimeSieveKernel)); PollardRhoKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel, ArrayView1D, ArrayView1D>(Pow2MontgomeryKernels.TryPollardRhoKernel)); - - AcceleratorStreamPool.Return(stream); } public void Dispose() { Input.Dispose(); OutputUlong.Dispose(); - // Stream?.Dispose(); Pow2ModEntriesToTestOnDevice.Dispose(); - PrimeTestInput?.Dispose(); - OutputByte?.Dispose(); - HeuristicFlag?.Dispose(); - - // Stream is disposed upon return to the queue, so we don't need to dispose it here. - // Stream.Dispose(); + PrimeTestInput.Dispose(); + OutputByte.Dispose(); + HeuristicFlag.Dispose(); + SmallPrimeFactorPrimeSlots.Dispose(); + SmallPrimeFactorExponentSlots.Dispose(); + SmallPrimeFactorCountSlot.Dispose(); + SmallPrimeFactorRemainingSlot.Dispose(); + SpecialMaxFactors.Dispose(); + SpecialMaxCandidates.Dispose(); + SpecialMaxResult.Dispose(); // These resources are shared between GPU leases - // ReleaseSharedTables(_sharedTables); + // Stream.Dispose(); // Accelerator.Dispose(); // Context.Dispose(); - // Accelerator.Dispose(); } } \ No newline at end of file diff --git a/PerfectNumbers.Core/Gpu/GpuPrimeWorkLimiter.cs b/PerfectNumbers.Core/Gpu/GpuPrimeWorkLimiter.cs index cd458f1..85b397d 100644 --- a/PerfectNumbers.Core/Gpu/GpuPrimeWorkLimiter.cs +++ b/PerfectNumbers.Core/Gpu/GpuPrimeWorkLimiter.cs @@ -5,11 +5,11 @@ namespace PerfectNumbers.Core.Gpu; public static class GpuPrimeWorkLimiter { - private static SemaphoreSlim _semaphore = new(1, int.MaxValue); - private static int _currentLimit = 1; + private static SemaphoreSlim _semaphore = new(1, int.MaxValue); + private static int _currentLimit = 1; - public static void Acquire() => _semaphore.Wait(); - public static void Release() => _semaphore.Release(); + public static void Acquire() { } // => _semaphore.Wait(); + public static void Release() { }// => _semaphore.Release(); public static void SetLimit(int value) { diff --git a/PerfectNumbers.Core/Gpu/GpuScratchBufferPool.cs b/PerfectNumbers.Core/Gpu/GpuScratchBufferPool.cs deleted file mode 100644 index e05a7af..0000000 --- a/PerfectNumbers.Core/Gpu/GpuScratchBufferPool.cs +++ /dev/null @@ -1,157 +0,0 @@ -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; - } - } -} \ No newline at end of file diff --git a/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs b/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs index 1efe5c9..f723748 100644 --- a/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs +++ b/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs @@ -12,10 +12,9 @@ public static class Pow2MontgomeryGpuCalculator { GpuPrimeWorkLimiter.Acquire(); - var gpu = Pow2MontgomeryAccelerator.Rent(1); + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); Accelerator? accelerator = gpu.Accelerator; AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); - // AcceleratorStream stream = gpu.Stream!; MemoryBuffer1D exponentBuffer = gpu.Input; exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1); @@ -27,7 +26,6 @@ public static class Pow2MontgomeryGpuCalculator var kernelLauncher = kernel.CreateLauncherDelegate, MontgomeryDivisorData, ArrayView1D>>(); kernelLauncher(stream, 1, exponentBuffer.View, divisor, resultBuffer.View); - // kernel.Launch(stream, 1, 1, exponentBuffer.View, divisor, resultBuffer.View); ulong result = 0UL; resultBuffer.View.CopyToCPU(stream, ref result, 1); @@ -35,7 +33,7 @@ public static class Pow2MontgomeryGpuCalculator AcceleratorStreamPool.Return(stream); GpuPrimeWorkLimiter.Release(); - Pow2MontgomeryAccelerator.Return(gpu); + PrimeOrderCalculatorAccelerator.Return(gpu); return result; } @@ -44,9 +42,8 @@ public static class Pow2MontgomeryGpuCalculator { GpuPrimeWorkLimiter.Acquire(); - var gpu = Pow2MontgomeryAccelerator.Rent(1); + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator); - // AcceleratorStream stream = gpu.Stream!; MemoryBuffer1D exponentBuffer = gpu.Input; exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1); @@ -64,15 +61,14 @@ public static class Pow2MontgomeryGpuCalculator AcceleratorStreamPool.Return(stream); GpuPrimeWorkLimiter.Release(); - Pow2MontgomeryAccelerator.Return(gpu); + PrimeOrderCalculatorAccelerator.Return(gpu); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong CalculateConvert(Pow2MontgomeryAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor) + public static ulong CalculateConvert(PrimeOrderCalculatorAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor) { AcceleratorStream stream = AcceleratorStreamPool.Rent(gpu.Accelerator); - // AcceleratorStream stream = gpu.Stream!; MemoryBuffer1D exponentBuffer = gpu.Input; exponentBuffer.View.CopyFromCPU(stream, ref exponent, 1); @@ -84,8 +80,7 @@ public static class Pow2MontgomeryGpuCalculator var kernelLauncher = kernel.CreateLauncherDelegate, MontgomeryDivisorData, ArrayView1D>>(); kernelLauncher(stream, 1,exponentBuffer.View, divisor, resultBuffer.View); - // kernel.Launch(stream, 1, 1,exponentBuffer.View, divisor, resultBuffer.View); - + ulong result = 0UL; resultBuffer.View.CopyToCPU(stream, ref result, 1); stream.Synchronize(); diff --git a/PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs b/PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs index 7f4eace..58e3ebb 100644 --- a/PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs +++ b/PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs @@ -304,8 +304,7 @@ public static class MersenneNumberDivisorByDivisorTester } Console.WriteLine($"Task finished for range {rangeStart}"); - Pow2MontgomeryAccelerator.DisposeAll(); - GpuScratchBufferPool.DisposeAll(); + PrimeOrderCalculatorAccelerator.DisposeAll(); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, diff --git a/PerfectNumbers.Core/PartialFactorResult.cs b/PerfectNumbers.Core/PartialFactorResult.cs index 84b5c98..712ae44 100644 --- a/PerfectNumbers.Core/PartialFactorResult.cs +++ b/PerfectNumbers.Core/PartialFactorResult.cs @@ -1,5 +1,3 @@ -using System.Buffers; - namespace PerfectNumbers.Core { public sealed class PartialFactorResult @@ -7,7 +5,8 @@ namespace PerfectNumbers.Core [ThreadStatic] private static PartialFactorResult? s_poolHead; - public FactorEntry[]? Factors; + public ulong[]? Factors; + public int[]? Exponents; private PartialFactorResult? _next; public ulong Cofactor; public bool FullyFactored; @@ -18,7 +17,7 @@ namespace PerfectNumbers.Core { } - public static PartialFactorResult Rent(in FactorEntry[]? factors, ulong cofactor, bool fullyFactored, int count, bool cofactorIsPrime) + public static PartialFactorResult Rent(in ulong[]? factors, in int[]? exponents, ulong cofactor, bool fullyFactored, int count, bool cofactorIsPrime) { PartialFactorResult? instance = s_poolHead; if (instance is null) @@ -31,8 +30,31 @@ namespace PerfectNumbers.Core } instance._next = null; - instance.Factors = factors; - instance.Cofactor = cofactor; + instance.Factors = factors; + instance.Exponents = exponents; + instance.Cofactor = cofactor; + instance.FullyFactored = fullyFactored; + instance.Count = count; + instance.CofactorIsPrime = cofactorIsPrime; + return instance; + } + + public static PartialFactorResult Rent(ulong cofactor, bool fullyFactored, int count, bool cofactorIsPrime) + { + PartialFactorResult? instance = s_poolHead; + if (instance is null) + { + instance = new PartialFactorResult(); + } + else + { + s_poolHead = instance._next; + } + + instance._next = null; + instance.Factors = null; + instance.Exponents = null; + instance.Cofactor = cofactor; instance.FullyFactored = fullyFactored; instance.Count = count; instance.CofactorIsPrime = cofactorIsPrime; @@ -46,32 +68,50 @@ namespace PerfectNumbers.Core }; public PartialFactorResult WithAdditionalPrime(ulong prime) - { - FactorEntry[]? source = Factors; - if (source is null || Count == 0) - { - FactorEntry[] local = ThreadStaticPools.FactorEntryPool.Rent(1); - local[0] = new FactorEntry(prime, 1, true); - return Rent(local, 1UL, true, 1, false); + { + ulong[]? source = Factors; + int sourceCount = Count; + int[] exponents; + if (source is null || sourceCount == 0) + { + source = ThreadStaticPools.UlongPool.Rent(1); + exponents = ThreadStaticPools.IntPool.Rent(1); + // source = new ulong[1]; + // exponents = new int[1]; + source[0] = prime; + exponents[0] = 1; + + return Rent(source, exponents, 1UL, true, 1, false); } - FactorEntry[] extended = ThreadStaticPools.FactorEntryPool.Rent(Count + 1); - Array.Copy(source, 0, extended, 0, Count); - extended[Count] = new FactorEntry(prime, 1, true); - Span span = extended.AsSpan(0, Count + 1); - span.Sort(static (a, b) => a.Value.CompareTo(b.Value)); - return Rent(extended, 1UL, true, Count + 1, false); + ulong[] extended = ThreadStaticPools.UlongPool.Rent(sourceCount + 1); + Array.Copy(source, 0, extended, 0, sourceCount); + extended[sourceCount] = prime; + + exponents = ThreadStaticPools.IntPool.Rent(sourceCount + 1); + Array.Copy(Exponents!, 0, exponents, 0, sourceCount); + exponents[sourceCount] = 1; + Array.Sort(extended, exponents, 0, sourceCount + 1); + + return Rent(extended, exponents, 1UL, true, sourceCount + 1, false); } public void Dispose() { if (this != Empty) { - FactorEntry[]? factors = Factors; + ulong[]? factors = Factors; if (factors is not null) { - Factors = null; - ThreadStaticPools.FactorEntryPool.Return(factors, clearArray: false); + int[] exponents = Exponents!; + Factors = null; + Exponents = null; + + // if (factors.Length > 1) + // { + ThreadStaticPools.UlongPool.Return(factors, clearArray: false); + ThreadStaticPools.IntPool.Return(exponents, clearArray: false); + // } } _next = s_poolHead; diff --git a/PerfectNumbers.Core/PerfectNumberConstants.cs b/PerfectNumbers.Core/PerfectNumberConstants.cs index bc99e91..aefa2c1 100644 --- a/PerfectNumbers.Core/PerfectNumberConstants.cs +++ b/PerfectNumbers.Core/PerfectNumberConstants.cs @@ -7,6 +7,8 @@ public static class PerfectNumberConstants public const ulong BiggestKnownEvenPerfectP = 136279841UL; public const int ConsoleInterval = 100_000; public const int DefaultFactorsBuffer = 32; + public const int DefaultSmallPrimeFactorSlotCount = 64; + public const int DefaultSpecialMaxFactorCapacity = 1024; public const ulong ExtraDivisorCycleSearchLimit = 64UL; public const int MaxQForDivisorCycles = 4_000_000; public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000; diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 62cd3db..3ca3c2e 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -53,10 +53,10 @@ internal static partial class PrimeOrderCalculator ulong phi = prime - 1UL; - Pow2MontgomeryAccelerator gpu = Pow2MontgomeryAccelerator.Rent(1); + PrimeOrderCalculatorAccelerator gpu = PrimeOrderCalculatorAccelerator.Rent(1); if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out ulong gpuOrder)) { - Pow2MontgomeryAccelerator.Return(gpu); + PrimeOrderCalculatorAccelerator.Return(gpu); return gpuOrder; } @@ -67,18 +67,18 @@ internal static partial class PrimeOrderCalculator { result = CalculateByFactorizationGpu(gpu, prime, divisorData); phiFactors.Dispose(); - Pow2MontgomeryAccelerator.Return(gpu); + PrimeOrderCalculatorAccelerator.Return(gpu); return result; } result = RunHeuristicPipelineCpu(gpu, prime, previousOrder, config, divisorData, phi, phiFactors); phiFactors.Dispose(); - Pow2MontgomeryAccelerator.Return(gpu); + PrimeOrderCalculatorAccelerator.Return(gpu); return result; } private static ulong RunHeuristicPipelineCpu( - Pow2MontgomeryAccelerator gpu, + PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong? previousOrder, in PrimeOrderSearchConfig config, @@ -119,7 +119,7 @@ internal static partial class PrimeOrderCalculator } // private static ulong specialMaxHits; - private static bool TrySpecialMaxCpu(Pow2MontgomeryAccelerator gpu, ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) + private static bool TrySpecialMaxCpu(PrimeOrderCalculatorAccelerator gpu, ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) { int length = factors.Count; // The phi factorization on the scanner path always yields at least one entry for phi >= 2, @@ -129,7 +129,7 @@ internal static partial class PrimeOrderCalculator // return true; // } - ReadOnlySpan factorSpan = new(factors.Factors, 0, length); + ReadOnlySpan factorSpan = new(factors.Factors, 0, length); Span stackBuffer = stackalloc ulong[length]; @@ -138,18 +138,17 @@ internal static partial class PrimeOrderCalculator return EvaluateSpecialMaxCandidates(stackBuffer, factorSpan, phi, prime, divisorData); } - // Atomic.Add(ref specialMaxHits, 1); - // Console.WriteLine($"Special max GPU hit {specialMaxHits} ({length})"); - return EvaluateSpecialMaxCandidatesGpu(gpu, stackBuffer, factorSpan, phi, prime, divisorData); + return EvaluateSpecialMaxCandidatesGpu(gpu, factorSpan, phi, prime, divisorData); } - private static bool EvaluateSpecialMaxCandidates(Span buffer, ReadOnlySpan factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData) + private static bool EvaluateSpecialMaxCandidates(Span buffer, ReadOnlySpan factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData) { int actual = 0; int factorCount = factors.Length; for (int i = 0; i < factorCount; i++) { - ulong factor = factors[i].Value; + ulong factor = factors[i]; + // The partial factor pipeline never feeds zero or oversized factors while scanning candidate orders. // if (factor == 0UL || factor > phi) // { @@ -196,7 +195,7 @@ internal static partial class PrimeOrderCalculator return true; } - private static ulong InitializeStartingOrderCpu(Pow2MontgomeryAccelerator gpu, ulong prime, ulong phi, in MontgomeryDivisorData divisorData) + private static ulong InitializeStartingOrderCpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong phi, in MontgomeryDivisorData divisorData) { ulong order = phi; if ((prime & 7UL) == 1UL || (prime & 7UL) == 7UL) @@ -213,22 +212,29 @@ internal static partial class PrimeOrderCalculator private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) { - ArrayPool pool = ThreadStaticPools.FactorEntryPool; + ArrayPool ulongPool = ThreadStaticPools.UlongPool; + ArrayPool intPool = ThreadStaticPools.IntPool; + + ReadOnlySpan factorSpan = factors.Factors; + ReadOnlySpan exponentsSpan = factors.Exponents; - ReadOnlySpan factorSpan = factors.Factors; int length = factors.Count; - FactorEntry[] tempArray = pool.Rent(length + 1); + ulong[] tempFactorsArray = ulongPool.Rent(length + 1); + int[] tempExponentsArray = intPool.Rent(length + 1); - Span buffer = tempArray; - factorSpan.CopyTo(buffer); + Span ulongBuffer = tempFactorsArray; + factorSpan.CopyTo(ulongBuffer); + Span intBuffer = tempExponentsArray; + exponentsSpan.CopyTo(intBuffer); if (!factors.FullyFactored && factors.Cofactor > 1UL && factors.CofactorIsPrime) { - buffer[length] = new FactorEntry(factors.Cofactor, 1, true); + ulongBuffer[length] = factors.Cofactor; + intBuffer[length] = 1; length++; } - buffer[..length].Sort(static (a, b) => a.Value.CompareTo(b.Value)); + Array.Sort(tempFactorsArray, tempExponentsArray, 0, length); ExponentRemainderStepperCpu stepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); @@ -243,8 +249,8 @@ internal static partial class PrimeOrderCalculator for (int i = 0; i < length; i++) { - ulong primeFactor = buffer[i].Value; - int exponent = buffer[i].Exponent; + ulong primeFactor = ulongBuffer[i]; + int exponent = intBuffer[i]; // Factor exponents produced by Pollard-Rho and trial division are always positive in the scanner flow. // if (exponent <= 0) // { @@ -275,7 +281,7 @@ internal static partial class PrimeOrderCalculator ThreadStaticPools.BoolPool.Return(heapEvaluationArray!); } - pool.Return(tempArray, clearArray: false); + ulongPool.Return(tempFactorsArray, clearArray: false); return order; } @@ -330,7 +336,7 @@ internal static partial class PrimeOrderCalculator } private static bool TryConfirmOrderCpu( - Pow2MontgomeryAccelerator gpu, + PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong order, in MontgomeryDivisorData divisorData, @@ -385,7 +391,9 @@ internal static partial class PrimeOrderCalculator in MontgomeryDivisorData divisorData, PartialFactorResult factorization) { - ReadOnlySpan span = factorization.Factors!; + ReadOnlySpan factorsSpan = factorization.Factors!; + ReadOnlySpan exponentsSpan = factorization.Exponents!; + int length = factorization.Count; const int StackExponentCapacity = 32; @@ -399,8 +407,9 @@ internal static partial class PrimeOrderCalculator for (int i = 0; i < length; i++) { - ulong primeFactor = span[i].Value; - int exponent = span[i].Exponent; + ulong primeFactor = factorsSpan[i]; + int exponent = exponentsSpan[i]; + // Factor exponents emitted by TryConfirmCandidateCpu stay positive for production workloads. // if (exponent <= 0) // { @@ -490,7 +499,7 @@ internal static partial class PrimeOrderCalculator } private static bool TryHeuristicFinishCpu( - Pow2MontgomeryAccelerator gpu, + PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong order, ulong? previousOrder, @@ -523,7 +532,6 @@ internal static partial class PrimeOrderCalculator return false; } - // if (!PrimeTester.IsPrimeCpu(orderFactors.Cofactor, CancellationToken.None)) if (!orderFactors.CofactorIsPrime) { return false; @@ -536,12 +544,13 @@ internal static partial class PrimeOrderCalculator int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecks << 2; List candidates = ThreadStaticPools.RentUlongList(capacity); - FactorEntry[] factorArray = orderFactors.Factors!; + candidates.Clear(); + ulong[] factorArray = orderFactors.Factors!; + int[] exponentsArray = orderFactors.Exponents!; // DebugLog("Building candidates list"); - BuildCandidates(order, factorArray, orderFactors.Count, candidates, capacity); + BuildCandidates(order, factorArray, exponentsArray, orderFactors.Count, candidates, capacity); if (candidates.Count == 0) { - candidates.Clear(); ThreadStaticPools.ReturnUlongList(candidates); return false; } @@ -798,19 +807,21 @@ internal static partial class PrimeOrderCalculator return 0; } - private static void BuildCandidates(ulong order, FactorEntry[] factors, int count, List candidates, int limit) + private static readonly Comparer _comparer = Comparer.Default; + + private static void BuildCandidates(ulong order, ulong[] factors, int[] exponents, int count, List candidates, int limit) { if (count == 0) { return; } - Span buffer = factors.AsSpan(0, count); - buffer.Sort(static (a, b) => a.Value.CompareTo(b.Value)); - BuildCandidatesRecursive(order, buffer, 0, 1UL, candidates, limit); + Array.Sort(factors, exponents, 0, count); + BuildCandidatesRecursive(order, factors, exponents, 0, 1UL, candidates, limit); } - private static void BuildCandidatesRecursive(ulong order, in ReadOnlySpan factors, int index, ulong divisorProduct, List candidates, int limit) + // private static void BuildCandidatesRecursive(ulong order, in ReadOnlySpan factors, int index, ulong divisorProduct, List candidates, int limit) + private static void BuildCandidatesRecursive(ulong order, in ulong[] factors, in int[] exponents, int index, ulong divisorProduct, List candidates, int limit) { if (candidates.Count >= limit) { @@ -833,11 +844,15 @@ internal static partial class PrimeOrderCalculator return; } - FactorEntry factor = factors[index]; - int factorExponent = factor.Exponent; - ulong primeFactor = factor.Value; + // FactorEntry factor = factors[index]; + // int factorExponent = factor.Exponent; + // ulong primeFactor = factor.Value; + + ulong factor = factors[index]; + int factorExponent = exponents[index]; + ulong contribution = 1UL; - ulong contributionLimit = factorExponent == 0 ? 0UL : order / primeFactor; + ulong contributionLimit = factorExponent == 0 ? 0UL : order / factor; for (int exponent = 0; exponent <= factorExponent; exponent++) { ulong nextDivisor = divisorProduct * contribution; @@ -846,7 +861,7 @@ internal static partial class PrimeOrderCalculator break; } - BuildCandidatesRecursive(order, factors, index + 1, nextDivisor, candidates, limit); + BuildCandidatesRecursive(order, factors, exponents, index + 1, nextDivisor, candidates, limit); if (candidates.Count >= limit) { return; @@ -862,11 +877,11 @@ internal static partial class PrimeOrderCalculator break; } - contribution *= primeFactor; + contribution *= factor; } } - private static bool TryConfirmCandidateCpu(Pow2MontgomeryAccelerator gpu, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget) + private static bool TryConfirmCandidateCpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget) { PartialFactorResult factorization = PartialFactor(gpu, candidate, config); try @@ -893,7 +908,9 @@ internal static partial class PrimeOrderCalculator factorization = extended; } - ReadOnlySpan span = factorization.Factors; + // ReadOnlySpan span = factorization.Factors; + ReadOnlySpan factorsSpan = factorization.Factors; + ReadOnlySpan exponentsSpan = factorization.Exponents; int length = factorization.Count; const int StackExponentCapacity = 32; @@ -906,8 +923,11 @@ internal static partial class PrimeOrderCalculator for (int i = 0; i < length; i++) { - ulong primeFactor = span[i].Value; - int exponent = span[i].Exponent; + // ulong primeFactor = factorsSpan[i].Value; + // int exponent = factorsSpan[i].Exponent; + ulong primeFactor = factorsSpan[i]; + int exponent = exponentsSpan[i]; + // Factorization entries for candidate confirmation are always positive here. // if (exponent <= 0) // { @@ -1036,7 +1056,7 @@ internal static partial class PrimeOrderCalculator private static ulong _pow2GpuHits; private static ulong _pow2CpuHits; - private static bool Pow2EqualsOneCpu(Pow2MontgomeryAccelerator gpu, ulong exponent, ulong prime, in MontgomeryDivisorData divisorData) + private static bool Pow2EqualsOneCpu(PrimeOrderCalculatorAccelerator gpu, ulong exponent, ulong prime, in MontgomeryDivisorData divisorData) { if (exponent <= (PerfectNumberConstants.MaxQForDivisorCycles) || prime <= (PerfectNumberConstants.MaxQForDivisorCycles)) { @@ -1059,7 +1079,7 @@ internal static partial class PrimeOrderCalculator // private static ulong _partialFactorPendingHits; // private static ulong _partialFactorCofactorHits; - private static PartialFactorResult PartialFactor(Pow2MontgomeryAccelerator gpu, ulong value, in PrimeOrderSearchConfig config) + private static PartialFactorResult PartialFactor(PrimeOrderCalculatorAccelerator gpu, ulong value, in PrimeOrderSearchConfig config) { if (value <= 1UL) { @@ -1262,17 +1282,17 @@ internal static partial class PrimeOrderCalculator GpuPrimeWorkLimiter.Release(); - ArrayPool pool = ThreadStaticPools.FactorEntryPool; + // ArrayPool pool = ThreadStaticPools.FactorEntryPool; bool temp; if (factorCount == 0) { if (cofactor == value) { - result = PartialFactorResult.Rent(null, cofactor, false, 0, cofactorIsPrime); + result = PartialFactorResult.Rent(cofactor, false, 0, cofactorIsPrime); goto ReturnResult; } - result = PartialFactorResult.Rent(null, cofactor, cofactor == 1UL, 0, cofactorIsPrime); + result = PartialFactorResult.Rent(cofactor, cofactor == 1UL, 0, cofactorIsPrime); goto ReturnResult; } @@ -1289,12 +1309,21 @@ internal static partial class PrimeOrderCalculator // goto ReturnResult; // } - FactorEntry[] array = pool.Rent(factorCount); + ArrayPool ulongPool = ThreadStaticPools.UlongPool; + ArrayPool intPool = ThreadStaticPools.IntPool; + // ArrayPool boolPool = ThreadStaticPools.BoolPool; + + // FactorEntry[] array = pool.Rent(factorCount); + ulong[] factorsArray = ulongPool.Rent(factorCount); + int[] exponentsArray = intPool.Rent(factorCount); + // bool[] cofactorIsPrimesArray = boolPool.Rent(factorCount); + int arrayIndex = 0; for (int i = 0; i < factorCount; i++) { ulong primeValue = primeSlots[i]; int exponentValue = exponentSlots[i]; + // This will never happen on the execution path from production code // if (primeValue == 0UL || exponentValue == 0) // { @@ -1302,14 +1331,20 @@ internal static partial class PrimeOrderCalculator // continue; // } - array[arrayIndex] = new FactorEntry(primeValue, exponentValue, true); + // factorsArray[arrayIndex] = new FactorEntry(primeValue, exponentValue, true); + factorsArray[arrayIndex] = primeValue; + exponentsArray[arrayIndex] = exponentValue; + // cofactorIsPrimesArray[arrayIndex] = true; arrayIndex++; } - Span arraySpan = array.AsSpan(0, arrayIndex); - arraySpan.Sort(static (a, b) => a.Value.CompareTo(b.Value)); + // Span arraySpan = factorsArray.AsSpan(0, arrayIndex); + // Span arraySpan = factorsArray.AsSpan(0, arrayIndex); + // arraySpan.Sort(static (a, b) => a.CompareTo(b)); + Array.Sort(factorsArray, exponentsArray, 0, arrayIndex); temp = cofactor == 1UL; - result = PartialFactorResult.Rent(array, cofactor, temp, arrayIndex, cofactorIsPrime); + // result = PartialFactorResult.Rent(factorsArray, exponentsArray, cofactorIsPrimesArray, cofactor, temp, arrayIndex, cofactorIsPrime); + result = PartialFactorResult.Rent(factorsArray, exponentsArray, cofactor, temp, arrayIndex, cofactorIsPrime); ReturnResult: if (primeSlotArray is not null) @@ -1665,7 +1700,7 @@ internal static partial class PrimeOrderCalculator exponents = newExponentSpan; } - private static ulong CalculateByFactorizationCpu(Pow2MontgomeryAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) + private static ulong CalculateByFactorizationCpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; Dictionary counts = new(capacity: 8); diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index d11d9b6..b258337 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -1,10 +1,6 @@ -using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ILGPU; using ILGPU.Runtime; -using ILGPU.Util; -using Open.Collections; using PerfectNumbers.Core.Gpu; using PerfectNumbers.Core.Gpu.Accelerators; @@ -16,7 +12,7 @@ internal static partial class PrimeOrderCalculator private const int GpuSmallPrimeFactorSlots = 64; - private static ulong CalculateByFactorizationGpu(Pow2MontgomeryAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) + private static ulong CalculateByFactorizationGpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; Dictionary counts = gpu.Pow2ModEntriesToTestOnHost; @@ -59,7 +55,7 @@ internal static partial class PrimeOrderCalculator return order; } - private static bool TryPopulateSmallPrimeFactorsGpu(ulong value, uint limit, Dictionary counts, out int factorCount, out ulong remaining) + private static bool TryPopulateSmallPrimeFactorsGpu(PrimeOrderCalculatorAccelerator gpu, ulong value, uint limit, Dictionary counts, out int factorCount, out ulong remaining) { var primeBufferArray = ThreadStaticPools.UlongPool.Rent(GpuSmallPrimeFactorSlots); var exponentBufferArray = ThreadStaticPools.IntPool.Rent(GpuSmallPrimeFactorSlots); @@ -67,8 +63,8 @@ internal static partial class PrimeOrderCalculator Span exponentBuffer = exponentBufferArray.AsSpan(0, GpuSmallPrimeFactorSlots); remaining = value; - Accelerator accelerator = AcceleratorPool.Shared.Rent(); - GpuScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, GpuSmallPrimeFactorSlots, 0); + gpu.EnsureSmallPrimeFactorSlotsCapacity(GpuSmallPrimeFactorSlots); + var accelerator = gpu.Accelerator; GpuPrimeWorkLimiter.Acquire(); var stream = AcceleratorStreamPool.Rent(accelerator); @@ -78,10 +74,10 @@ internal static partial class PrimeOrderCalculator var kernel = kernels.SmallPrimeFactor!; var kernelLauncher = kernel.CreateLauncherDelegate, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); - ArrayView1D smallPrimeFactorCountSlotView = scratch.SmallPrimeFactorCountSlot.View; - ArrayView1D smallPrimeFactorRemainingSlotView = scratch.SmallPrimeFactorRemainingSlot.View; - ArrayView1D smallPrimeFactorPrimeSlotsView = scratch.SmallPrimeFactorPrimeSlots.View; - ArrayView1D smallPrimeFactorExponentSlotsView = scratch.SmallPrimeFactorExponentSlots.View; + ArrayView1D smallPrimeFactorCountSlotView = gpu.SmallPrimeFactorCountSlot.View; + ArrayView1D smallPrimeFactorRemainingSlotView = gpu.SmallPrimeFactorRemainingSlot.View; + ArrayView1D smallPrimeFactorPrimeSlotsView = gpu.SmallPrimeFactorPrimeSlots.View; + ArrayView1D smallPrimeFactorExponentSlotsView = gpu.SmallPrimeFactorExponentSlots.View; ArrayView1D smallPrimeFactorsPrimesView = kernels.SmallPrimeFactorsPrimes!.View; kernelLauncher( @@ -117,7 +113,6 @@ internal static partial class PrimeOrderCalculator stream.Synchronize(); AcceleratorStreamPool.Return(stream); - GpuScratchBufferPool.Return(scratch); GpuPrimeWorkLimiter.Release(); for (int i = 0; i < factorCount; i++) @@ -131,38 +126,26 @@ internal static partial class PrimeOrderCalculator } private static bool EvaluateSpecialMaxCandidatesGpu( - Pow2MontgomeryAccelerator gpu, - Span buffer, - ReadOnlySpan factors, + PrimeOrderCalculatorAccelerator gpu, + ReadOnlySpan factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData) { - if (factors.Length == 0) - { - return true; - } - int factorCount = factors.Length; - Span factorSpan = buffer[..factorCount]; - for (int i = 0; i < factorCount; i++) - { - factorSpan[i] = factors[i].Value; - } + gpu.EnsureSpecialMaxFactorsCapacity(factorCount); + Accelerator accelerator = gpu.Accelerator; GpuPrimeWorkLimiter.Acquire(); - Accelerator accelerator = gpu.Accelerator; //AcceleratorPool.Shared.Rent(); - var stream = AcceleratorStreamPool.Rent(accelerator);// accelerator.CreateStream(); + var stream = AcceleratorStreamPool.Rent(accelerator); var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.EvaluateSpecialMaxCandidatesKernel); - GpuScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, 0, factorCount); - - ArrayView1D specialMaxFactorsView = scratch.SpecialMaxFactors.View; - specialMaxFactorsView.SubView(0, factorCount).CopyFromCPU(stream, ref MemoryMarshal.GetReference(factorSpan), factorCount); + ArrayView1D specialMaxFactorsView = gpu.SpecialMaxFactors.View; + specialMaxFactorsView.SubView(0, factorCount).CopyFromCPU(stream, ref MemoryMarshal.GetReference(factors), factorCount); var kernel = kernels.SpecialMax!; - ArrayView1D specialMaxResultView = scratch.SpecialMaxResult.View; + ArrayView1D specialMaxResultView = gpu.SpecialMaxResult.View; kernel( stream, @@ -171,28 +154,20 @@ internal static partial class PrimeOrderCalculator specialMaxFactorsView, factorCount, divisorData, - scratch.SpecialMaxCandidates.View, + gpu.SpecialMaxCandidates.View, specialMaxResultView); - // Span result = stackalloc ulong[1]; ulong result = 0UL; - // specialMaxResultView.CopyToCPU(stream, ref result, 1); specialMaxResultView.CopyToCPU(stream, ref result, 1); stream.Synchronize(); AcceleratorStreamPool.Return(stream); - // stream.Dispose(); - - // stream.Synchronize(); - // return result != 0; - GpuScratchBufferPool.Return(scratch); GpuPrimeWorkLimiter.Release(); return result != 0; } - private static bool TryPollardRhoGpu(Pow2MontgomeryAccelerator gpu, ulong n, out ulong factor) + private static bool TryPollardRhoGpu(PrimeOrderCalculatorAccelerator gpu, ulong n, out ulong factor) { - // var stream = gpu.Stream!; Span randomStateSpan = stackalloc ulong[1]; randomStateSpan[0] = ThreadStaticDeterministicRandomGpu.Exclusive.State; diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 475a5a7..58e2750 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -93,10 +93,7 @@ public sealed class PrimeTester [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsPrimeGpu(ulong n) { - var gpu = Pow2MontgomeryAccelerator.Rent(1); - - // Span flag = stackalloc byte[1]; - + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); var inputView = gpu.PrimeTestInput.View; var outputView = gpu.OutputByte.View; @@ -128,15 +125,14 @@ public sealed class PrimeTester stream.Synchronize(); AcceleratorStreamPool.Return(stream); - // gpu.Dispose(); - Pow2MontgomeryAccelerator.Return(gpu); + PrimeOrderCalculatorAccelerator.Return(gpu); GpuPrimeWorkLimiter.Release(); return flag != 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsPrimeGpu(Pow2MontgomeryAccelerator gpu, ulong n) + public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n) { var input = gpu.PrimeTestInput; var inputView = input.View; @@ -191,7 +187,7 @@ public sealed class PrimeTester return; } - Pow2MontgomeryAccelerator.WarmUp(); + PrimeOrderCalculatorAccelerator.WarmUp(); WarmedGpuLeaseCount = target; } } @@ -200,9 +196,8 @@ public sealed class PrimeTester public static void IsPrimeBatchGpu(ReadOnlySpan values, Span results) { GpuPrimeWorkLimiter.Acquire(); - var gpu = Pow2MontgomeryAccelerator.Rent(GpuBatchSize); + var gpu = PrimeOrderCalculatorAccelerator.Rent(GpuBatchSize); var accelerator = gpu.Accelerator; - // var stream = gpu.Stream!; var kernel = gpu.SmallPrimeSieveKernel!; int totalLength = values.Length; int batchSize = GpuBatchSize; @@ -244,20 +239,19 @@ public sealed class PrimeTester stream.Synchronize(); AcceleratorStreamPool.Return(stream); - Pow2MontgomeryAccelerator.Return(gpu); - // gpu.Dispose(); + PrimeOrderCalculatorAccelerator.Return(gpu); GpuPrimeWorkLimiter.Release(); } // Expose cache clearing for accelerator disposal coordination public static void ClearGpuCaches() { - Pow2MontgomeryAccelerator.Clear(); + PrimeOrderCalculatorAccelerator.Clear(); } internal static void DisposeGpuContexts() { - Pow2MontgomeryAccelerator.DisposeAll(); + PrimeOrderCalculatorAccelerator.DisposeAll(); lock (GpuWarmUpLock) { WarmedGpuLeaseCount = 0; @@ -282,9 +276,8 @@ public sealed class PrimeTester // TODO: Route this batch helper through the shared GPU kernel pool from // GpuUInt128BinaryGcdBenchmarks so we reuse cached kernels, pinned host buffers, // and divisor-cycle staging instead of allocating new device buffers per call. - var gpu = Pow2MontgomeryAccelerator.Rent(1); + var gpu = PrimeOrderCalculatorAccelerator.Rent(1); var accelerator = gpu.Accelerator; - // var stream = gpu.Stream!; int length = values.Length; ArrayPool pool = ThreadStaticPools.UlongPool; @@ -294,11 +287,8 @@ public sealed class PrimeTester MemoryBuffer1D? inputBuffer; MemoryBuffer1D? resultBuffer; - // lock (accelerator) - { - inputBuffer = accelerator.Allocate1D(length); - resultBuffer = accelerator.Allocate1D(length); - } + inputBuffer = accelerator.Allocate1D(length); + resultBuffer = accelerator.Allocate1D(length); AcceleratorStream stream = AcceleratorStreamPool.Rent(accelerator); inputBuffer.View.CopyFromCPU(stream, ref temp[0], length); @@ -311,8 +301,7 @@ public sealed class PrimeTester pool.Return(temp, clearArray: false); resultBuffer.Dispose(); inputBuffer.Dispose(); - Pow2MontgomeryAccelerator.Return(gpu); - // gpu.Dispose(); + PrimeOrderCalculatorAccelerator.Return(gpu); } private static Action, ArrayView> GetSharesFactorKernel(Accelerator accelerator) diff --git a/PerfectNumbers.Core/ULongExtensions.Gpu.cs b/PerfectNumbers.Core/ULongExtensions.Gpu.cs index 01d3f76..835fea6 100644 --- a/PerfectNumbers.Core/ULongExtensions.Gpu.cs +++ b/PerfectNumbers.Core/ULongExtensions.Gpu.cs @@ -323,7 +323,7 @@ public static partial class ULongExtensions => Pow2MontgomeryGpuCalculator.CalculateConvert(exponent, divisor); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong Pow2MontgomeryModWindowedConvertGpu(this ulong exponent, Pow2MontgomeryAccelerator gpu, in MontgomeryDivisorData divisor) + public static ulong Pow2MontgomeryModWindowedConvertGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor) => Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, exponent, divisor); [MethodImpl(MethodImplOptions.AggressiveInlining)]