diff --git a/PerfectNumbers.Core/Gpu/AcceleratorPool.cs b/PerfectNumbers.Core/Gpu/AcceleratorPool.cs index a796464..3ad835d 100644 --- a/PerfectNumbers.Core/Gpu/AcceleratorPool.cs +++ b/PerfectNumbers.Core/Gpu/AcceleratorPool.cs @@ -28,7 +28,7 @@ namespace PerfectNumbers.Core.Gpu // Intentionally left empty - there's nothing to do here. We're not really renting anything. } - public static readonly AcceleratorPool Shared = new(PerfectNumberConstants.RollingAccelerators); + public static readonly AcceleratorPool Shared = new(1); private readonly int Capacity; public AcceleratorPool(int capacity) @@ -38,7 +38,20 @@ namespace PerfectNumbers.Core.Gpu var device = context.GetPreferredDevice(false); Context = context; Device = device; - Accelerators = [.. Enumerable.Range(0, capacity).Select(_ => device.CreateAccelerator(context))]; + int acceleratorsInContext = 0; + Accelerators = [.. Enumerable.Range(0, capacity).Select(_ => + { + Accelerator accelerator = device.CreateAccelerator(context); + acceleratorsInContext++; + if (acceleratorsInContext >= 128) + { + context = Context.CreateDefault(); + device = context.GetPreferredDevice(false); + acceleratorsInContext = 0; + } + + return accelerator; + })]; } } } \ No newline at end of file diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs index 8994784..148a405 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs @@ -6,7 +6,7 @@ namespace PerfectNumbers.Core.Gpu.Accelerators; internal sealed class HeuristicGpuTables { - private static readonly ConcurrentDictionary _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators); + private static readonly ConcurrentDictionary _sharedTables = new(20_480, 1); internal static HeuristicGpuTables EnsureStaticTables(Accelerator accelerator, AcceleratorStream stream) => _sharedTables.GetOrAdd(accelerator, _ => new HeuristicGpuTables(accelerator, stream)); diff --git a/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs index 3ff4ea5..f36f112 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs @@ -6,7 +6,7 @@ namespace PerfectNumbers.Core.Gpu.Accelerators; internal sealed class LastDigitGpuTables { - private static readonly ConcurrentDictionary _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators); + private static readonly ConcurrentDictionary _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators * 2); internal static LastDigitGpuTables EnsureStaticTables(Accelerator accelerator, AcceleratorStream stream) => _sharedTables.GetOrAdd(accelerator, _ => new LastDigitGpuTables(accelerator, stream)); diff --git a/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs index 7b63b86..ea74b93 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs @@ -12,20 +12,21 @@ public sealed class Pow2MontgomeryAccelerator [ThreadStatic] private static Queue? _pool; - private static AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators); + private static AcceleratorPool _accelerators = new(PerfectNumberConstants.RollingAccelerators << 1); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Pow2MontgomeryAccelerator Rent() + public static Pow2MontgomeryAccelerator Rent(int primeTesterCapacity) { var pool = _pool ??= []; if (!pool.TryDequeue(out var gpu)) { Accelerator accelerator = _accelerators.Rent(); - gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer); + gpu = new(accelerator, PerfectNumberConstants.DefaultFactorsBuffer, primeTesterCapacity); } else { gpu.Stream = gpu.Accelerator.CreateStream(); + gpu.EnsureCapacity(PerfectNumberConstants.DefaultFactorsBuffer,primeTesterCapacity); } return gpu; @@ -39,13 +40,69 @@ public sealed class Pow2MontgomeryAccelerator _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; @@ -53,7 +110,23 @@ public sealed class Pow2MontgomeryAccelerator public readonly Kernel CheckFactorsKernel; - public void EnsureCapacity(int factorsCount) + [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) { @@ -61,16 +134,45 @@ public sealed class Pow2MontgomeryAccelerator 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) + public Pow2MontgomeryAccelerator(Accelerator accelerator, int factorsCount, int primeTesterCapacity) { Accelerator = accelerator; - Stream = accelerator.CreateStream(); + 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); @@ -84,6 +186,8 @@ public sealed class Pow2MontgomeryAccelerator var checkFactorsKernel = accelerator.LoadStreamKernel, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D>(PrimeOrderGpuHeuristics.CheckFactorsKernel); CheckFactorsKernel = KernelUtil.GetKernel(checkFactorsKernel); + + SmallPrimeSieveKernel = (Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>)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>>().Clone(); } public void Dispose() @@ -92,7 +196,17 @@ public sealed class Pow2MontgomeryAccelerator Output.Dispose(); Stream?.Dispose(); Pow2ModEntriesToTestOnDevice.Dispose(); - // Accelerator is shared with other threads + 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(); } } \ No newline at end of file diff --git a/PerfectNumbers.Core/Gpu/Accelerators/PrimeTesterByLastDigitAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/PrimeTesterByLastDigitAccelerator.cs deleted file mode 100644 index 4987082..0000000 --- a/PerfectNumbers.Core/Gpu/Accelerators/PrimeTesterByLastDigitAccelerator.cs +++ /dev/null @@ -1,192 +0,0 @@ -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(); - } -} diff --git a/PerfectNumbers.Core/Gpu/HeuristicGpuDivisorTables.cs b/PerfectNumbers.Core/Gpu/HeuristicGpuDivisorTables.cs index 778dc7b..6322bec 100644 --- a/PerfectNumbers.Core/Gpu/HeuristicGpuDivisorTables.cs +++ b/PerfectNumbers.Core/Gpu/HeuristicGpuDivisorTables.cs @@ -4,7 +4,7 @@ using ILGPU.Runtime; namespace PerfectNumbers.Core.Gpu; -internal readonly struct HeuristicGpuDivisorTables +public readonly struct HeuristicGpuDivisorTables { public readonly ArrayView1D CombinedDivisorsEnding1; public readonly ArrayView1D CombinedDivisorsEnding3; diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs index 377e902..a3eabff 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs @@ -5,7 +5,7 @@ using ILGPU.Runtime; namespace PerfectNumbers.Core.Gpu; -internal enum HeuristicGpuDivisorTableKind : byte +public enum HeuristicGpuDivisorTableKind : byte { GroupA = 0, GroupBEnding1 = 1, diff --git a/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs b/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs index 3838a71..e5736b2 100644 --- a/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs +++ b/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs @@ -10,9 +10,8 @@ public static class Pow2MontgomeryGpuCalculator [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong CalculateKeep(ulong exponent, in MontgomeryDivisorData divisor) { - GpuPrimeWorkLimiter.Acquire(); - var gpu = Pow2MontgomeryAccelerator.Rent(); + var gpu = Pow2MontgomeryAccelerator.Rent(1); Accelerator? accelerator = gpu.Accelerator; AcceleratorStream stream = gpu.Stream!; MemoryBuffer1D exponentBuffer = gpu.Input; @@ -23,23 +22,24 @@ public static class Pow2MontgomeryGpuCalculator // resultBuffer.MemSetToZero(stream); var kernel = gpu.KeepMontgomeryKernel; + + GpuPrimeWorkLimiter.Acquire(); kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View); ulong result = 0UL; resultBuffer.View.CopyToCPU(stream, ref result, 1); stream.Synchronize(); + GpuPrimeWorkLimiter.Release(); Pow2MontgomeryAccelerator.Return(gpu); - GpuPrimeWorkLimiter.Release(); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong CalculateConvert(ulong exponent, in MontgomeryDivisorData divisor) { - GpuPrimeWorkLimiter.Acquire(); - var gpu = Pow2MontgomeryAccelerator.Rent(); + var gpu = Pow2MontgomeryAccelerator.Rent(1); AcceleratorStream stream = gpu.Stream!; MemoryBuffer1D exponentBuffer = gpu.Input; MemoryBuffer1D resultBuffer = gpu.Output; @@ -49,14 +49,16 @@ public static class Pow2MontgomeryGpuCalculator // resultBuffer.MemSetToZero(stream); var kernel = gpu.ConvertToStandardKernel; + + GpuPrimeWorkLimiter.Acquire(); kernel(stream, 1, exponentBuffer.View, divisor, resultBuffer.View); ulong result = 0UL; resultBuffer.View.CopyToCPU(stream, ref result, 1); stream.Synchronize(); + GpuPrimeWorkLimiter.Release(); Pow2MontgomeryAccelerator.Return(gpu); - GpuPrimeWorkLimiter.Release(); return result; } diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index cec27ef..ae75c78 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -52,14 +52,14 @@ internal static partial class PrimeOrderCalculator ulong phi = prime - 1UL; - Pow2MontgomeryAccelerator gpu = Pow2MontgomeryAccelerator.Rent(); + Pow2MontgomeryAccelerator gpu = Pow2MontgomeryAccelerator.Rent(1); if (IsGpuHeuristicDevice && PrimeOrderGpuHeuristics.TryCalculateOrder(prime, previousOrder, config, divisorData, out ulong gpuOrder)) { Pow2MontgomeryAccelerator.Return(gpu); return gpuOrder; } - PartialFactorResult phiFactors = PartialFactor(phi, config); + PartialFactorResult phiFactors = PartialFactor(gpu, phi, config); ulong result; if (phiFactors.Factors is null) @@ -106,7 +106,7 @@ internal static partial class PrimeOrderCalculator return CalculateByFactorizationGpu(gpu, prime, divisorData); } - if (TryHeuristicFinishCpu(prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order)) + if (TryHeuristicFinishCpu(gpu, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order)) { GpuPrimeWorkLimiter.Release(); return order; @@ -347,7 +347,7 @@ internal static partial class PrimeOrderCalculator return false; } - PartialFactorResult factorization = PartialFactor(order, config); + PartialFactorResult factorization = PartialFactor(gpu, order, config); if (factorization.Factors is null) { factorization.Dispose(); @@ -488,6 +488,7 @@ internal static partial class PrimeOrderCalculator } private static bool TryHeuristicFinishCpu( + Pow2MontgomeryAccelerator gpu, ulong prime, ulong order, ulong? previousOrder, @@ -505,7 +506,7 @@ internal static partial class PrimeOrderCalculator } // Reuse the partial factorization from TryConfirmOrderCpu when available. - PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactor(order, config); + PartialFactorResult orderFactors = cachedOrderFactors ?? PartialFactor(gpu, order, config); try { if (orderFactors.Factors is null) @@ -621,7 +622,7 @@ internal static partial class PrimeOrderCalculator bool equalsOne = powStepper.InitializeCpuIsUnity(candidate); powStepperInitialized = true; - if (equalsOne && TryConfirmCandidateCpu(prime, candidate, divisorData, config, ref powUsed, powBudget)) + if (equalsOne && TryConfirmCandidateCpu(gpu, prime, candidate, divisorData, config, ref powUsed, powBudget)) { if (gpuPool is not null) { @@ -658,7 +659,7 @@ internal static partial class PrimeOrderCalculator continue; } - if (!TryConfirmCandidateCpu(prime, candidate, divisorData, config, ref powUsed, powBudget)) + if (!TryConfirmCandidateCpu(gpu, prime, candidate, divisorData, config, ref powUsed, powBudget)) { continue; } @@ -863,9 +864,9 @@ internal static partial class PrimeOrderCalculator } } - private static bool TryConfirmCandidateCpu(ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget) + private static bool TryConfirmCandidateCpu(Pow2MontgomeryAccelerator gpu, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config, ref int powUsed, int powBudget) { - PartialFactorResult factorization = PartialFactor(candidate, config); + PartialFactorResult factorization = PartialFactor(gpu, candidate, config); try { if (factorization.Factors is null) @@ -1056,7 +1057,7 @@ internal static partial class PrimeOrderCalculator // private static ulong _partialFactorPendingHits; // private static ulong _partialFactorCofactorHits; - private static PartialFactorResult PartialFactor(ulong value, in PrimeOrderSearchConfig config) + private static PartialFactorResult PartialFactor(Pow2MontgomeryAccelerator gpu, ulong value, in PrimeOrderSearchConfig config) { if (value <= 1UL) { @@ -1125,6 +1126,8 @@ internal static partial class PrimeOrderCalculator bool pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp; if (!pollardRhoDeadlineReached) { + GpuPrimeWorkLimiter.Acquire(); + while (compositeStack.Count > 0) { ulong composite = compositeStack.Pop(); @@ -1141,7 +1144,7 @@ internal static partial class PrimeOrderCalculator // Console.WriteLine($"Partial factor hits {Volatile.Read(ref _partialFactorHits)}"); // bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None); - bool isPrime = PrimeTester.IsPrime(composite); + bool isPrime = PrimeTester.IsPrimeGpu(gpu, composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); if (isPrime) @@ -1173,6 +1176,8 @@ internal static partial class PrimeOrderCalculator { pollardRhoDeadlineReached = Stopwatch.GetTimestamp() > deadlineTimestamp; } + + GpuPrimeWorkLimiter.Release(); } if (pollardRhoDeadlineReached) @@ -1188,6 +1193,7 @@ internal static partial class PrimeOrderCalculator bool cofactorContainsComposite = false; int pendingCount = pending.Count; int index = 0; + GpuPrimeWorkLimiter.Acquire(); for (; index < pendingCount; index++) { PendingEntry entry = pending[index]; @@ -1209,7 +1215,7 @@ internal static partial class PrimeOrderCalculator // Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}"); // bool isPrime = PrimeTester.IsPrimeCpu(composite, CancellationToken.None); - bool isPrime = PrimeTester.IsPrimeGpu(composite); + bool isPrime = PrimeTester.IsPrimeGpu(gpu, composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); entry = entry.WithPrimality(isPrime); @@ -1228,7 +1234,7 @@ internal static partial class PrimeOrderCalculator cofactorContainsComposite = true; } } - + bool cofactorIsPrime; if (cofactor <= 1UL) { @@ -1246,11 +1252,13 @@ internal static partial class PrimeOrderCalculator // Atomic.Add(ref _partialFactorCofactorHits, 1UL); // Console.WriteLine($"Partial factor cofactor hits {Volatile.Read(ref _partialFactorCofactorHits)}"); - cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor); + cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor); // cofactorIsPrime = PrimeTester.IsPrimeGpu(cofactor); // cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor); } + GpuPrimeWorkLimiter.Release(); + ArrayPool pool = ThreadStaticPools.FactorEntryPool; bool temp; if (factorCount == 0) diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index 7a5e088..a066b06 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -38,7 +38,7 @@ internal static partial class PrimeOrderCalculator entries.Sort(entries, static (a, b) => a.Key.CompareTo(b.Key)); AcceleratorStream stream = gpu.Stream!; - gpu.EnsureCapacity(entryCount); + gpu.EnsureCapacity(entryCount, 1); gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries); ulong order = phi; diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 8931bae..76f3bbb 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -94,17 +94,17 @@ public sealed class PrimeTester public static bool IsPrimeGpu(ulong n) { GpuPrimeWorkLimiter.Acquire(); - var gpu = PrimeTesterByLastDigitAccelerator.Rent(1); + var gpu = Pow2MontgomeryAccelerator.Rent(1); var accelerator = gpu.Accelerator; - var stream = gpu.Stream; - var kernel = PrimeTesterByLastDigitAccelerator.GetSmallPrimeSieveKernel(accelerator); + var stream = gpu.Stream!; + var kernel = gpu.SmallPrimeSieveKernel; ulong value = n; // Span flag = stackalloc byte[1]; byte flag = 0; - var input = gpu.Input; - var output = gpu.Output; + var input = gpu.PrimeTestInput; + var output = gpu.PrimeTestOutput; var inputView = input.View; var outputView = output.View; @@ -131,6 +131,42 @@ public sealed class PrimeTester return flag != 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPrimeGpu(Pow2MontgomeryAccelerator gpu, ulong n) + { + var accelerator = gpu.Accelerator; + var stream = gpu.Stream!; + Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>? kernel = gpu.SmallPrimeSieveKernel; + + ulong value = n; + // Span flag = stackalloc byte[1]; + byte flag = 0; + + var input = gpu.PrimeTestInput; + var output = gpu.PrimeTestOutput; + var inputView = input.View; + var outputView = output.View; + + inputView.CopyFromCPU(stream, ref value, 1); + kernel( + stream, + 1, + inputView, + gpu.DevicePrimesLastOne.View, + gpu.DevicePrimesLastSeven.View, + gpu.DevicePrimesLastThree.View, + gpu.DevicePrimesLastNine.View, + gpu.DevicePrimesPow2LastOne.View, + gpu.DevicePrimesPow2LastSeven.View, + gpu.DevicePrimesPow2LastThree.View, + gpu.DevicePrimesPow2LastNine.View, + outputView); + outputView.CopyToCPU(stream, ref flag, 1); + stream.Synchronize(); + + return flag != 0; + } + public static int GpuBatchSize { get; set; } = 262_144; private static readonly object GpuWarmUpLock = new(); @@ -151,7 +187,7 @@ public sealed class PrimeTester return; } - PrimeTesterByLastDigitAccelerator.WarmUp(); + Pow2MontgomeryAccelerator.WarmUp(); WarmedGpuLeaseCount = target; } } @@ -160,15 +196,15 @@ public sealed class PrimeTester public static void IsPrimeBatchGpu(ReadOnlySpan values, Span results) { GpuPrimeWorkLimiter.Acquire(); - var gpu = PrimeTesterByLastDigitAccelerator.Rent(GpuBatchSize); + var gpu = Pow2MontgomeryAccelerator.Rent(GpuBatchSize); var accelerator = gpu.Accelerator; - var stream = gpu.Stream; - var kernel = PrimeTesterByLastDigitAccelerator.GetSmallPrimeSieveKernel(accelerator); + var stream = gpu.Stream!; + var kernel = gpu.SmallPrimeSieveKernel; int totalLength = values.Length; int batchSize = GpuBatchSize; - var input = gpu.Input; - var output = gpu.Output; + var input = gpu.PrimeTestInput; + var output = gpu.PrimeTestOutput; var inputView = input.View; var outputView = output.View; @@ -211,12 +247,12 @@ public sealed class PrimeTester // Expose cache clearing for accelerator disposal coordination public static void ClearGpuCaches() { - PrimeTesterByLastDigitAccelerator.Clear(); + Pow2MontgomeryAccelerator.Clear(); } internal static void DisposeGpuContexts() { - PrimeTesterByLastDigitAccelerator.DisposeAll(); + Pow2MontgomeryAccelerator.DisposeAll(); lock (GpuWarmUpLock) { WarmedGpuLeaseCount = 0; @@ -241,9 +277,9 @@ 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 = PrimeTesterByLastDigitAccelerator.Rent(); + var gpu = Pow2MontgomeryAccelerator.Rent(1); var accelerator = gpu.Accelerator; - var stream = gpu.Stream; + var stream = gpu.Stream!; int length = values.Length; ArrayPool pool = ThreadStaticPools.UlongPool;