From 0de987152965ddbb86555e7b3e950426cdcc9bdb Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Mon, 10 Nov 2025 18:35:50 +0100 Subject: [PATCH] Functionality and classes separated. Kernel initialization made dependent on a flag. Cleanup. --- .../Gpu/GpuKernelCompilationTests.cs | 55 +- .../Gpu/AcceleratorReferenceComparer.cs | 13 + .../Gpu/Accelerators/HeuristicGpuTables.cs | 142 +++++ .../HeuristicPrimeTesterAccelerator.cs | 208 ++++++++ .../Gpu/Accelerators/LastDigitGpuTables.cs | 58 +++ .../Accelerators/Pow2MontgomeryAccelerator.cs | 14 +- .../PrimeTesterByLastDigitAccelerator.cs | 192 +++++++ PerfectNumbers.Core/Gpu/GpuKernelPool.cs | 199 ++++--- .../Gpu/GpuStaticTableInitializer.cs | 2 +- PerfectNumbers.Core/Gpu/KernelType.cs | 16 + .../Gpu/Kernels/PrimeTesterKernels.cs | 10 +- .../Gpu/MersenneNumberIncrementalGpuTester.cs | 2 +- .../Gpu/MersenneNumberOrderGpuTester.cs | 2 +- .../Gpu/MersenneNumberResidueGpuTester.cs | 2 +- .../HeuristicCombinedPrimeTester.cs | 32 +- PerfectNumbers.Core/HeuristicPrimeSieves.cs | 4 +- PerfectNumbers.Core/HeuristicPrimeTester.cs | 9 +- PerfectNumbers.Core/MersenneNumberTester.cs | 2 +- .../PrimeOrderCalculator.Gpu.cs | 10 +- PerfectNumbers.Core/PrimeTester.cs | 492 +----------------- 20 files changed, 845 insertions(+), 619 deletions(-) create mode 100644 PerfectNumbers.Core/Gpu/AcceleratorReferenceComparer.cs create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/HeuristicPrimeTesterAccelerator.cs create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/PrimeTesterByLastDigitAccelerator.cs create mode 100644 PerfectNumbers.Core/Gpu/KernelType.cs diff --git a/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs b/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs index 919ff7e..1627fd6 100644 --- a/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs +++ b/PerfectNumbers.Core.Tests/Gpu/GpuKernelCompilationTests.cs @@ -121,7 +121,7 @@ public class GpuKernelCompilationTests private static readonly Action, ArrayView1D> MersenneDivisorCyclesKernel = DivisorCycleKernels.GpuDivisorCycleKernel; - private static readonly Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> PrimeTesterSmallPrimeKernel = + private static readonly Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> PrimeTesterSmallPrimeKernel = PrimeTesterKernels.SmallPrimeSieveKernel; private static readonly Action, ArrayView> PrimeTesterSharesFactorKernel = @@ -346,43 +346,44 @@ public class GpuKernelCompilationTests _ = kernel; } - private static void CompileGpuKernelPoolKernels(Accelerator accelerator) - { + private static KernelType _kernelType = KernelType.Pow2ModKernelScan | KernelType.OrderKernelScan | KernelType.IncrementalKernelScan | KernelType.IncrementalOrderKernelScan | KernelType.Pow2ModOrderKernelScan | KernelType.EvaluateSpecialMaxCandidatesKernel | KernelType.SmallPrimeFactorKernelScan; + private static void CompileGpuKernelPoolKernels(Accelerator accelerator) + { GpuPrimeWorkLimiter.Acquire(); AcceleratorStream stream = accelerator.CreateStream(); - try + try { - var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); - _ = kernels.Order; - _ = kernels.Incremental; - _ = kernels.Pow2Mod; - _ = kernels.IncrementalOrder; + var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, _kernelType); + _ = kernels.Order; + _ = kernels.Incremental; + _ = kernels.Pow2Mod; + _ = kernels.IncrementalOrder; _ = kernels.Pow2ModOrder; - } - finally + } + finally { stream.Synchronize(); stream.Dispose(); - GpuPrimeWorkLimiter.Release(); - } + GpuPrimeWorkLimiter.Release(); + } stream = accelerator.CreateStream(); GpuPrimeWorkLimiter.Acquire(); - try - { - var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); - _ = kernels.Order; - _ = kernels.Incremental; - _ = kernels.Pow2Mod; - _ = kernels.IncrementalOrder; - _ = kernels.Pow2ModOrder; - } - finally - { + try + { + var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, _kernelType); + _ = kernels.Order; + _ = kernels.Incremental; + _ = kernels.Pow2Mod; + _ = kernels.IncrementalOrder; + _ = kernels.Pow2ModOrder; + } + finally + { stream.Synchronize(); stream.Dispose(); - GpuPrimeWorkLimiter.Release(); - } - } + GpuPrimeWorkLimiter.Release(); + } + } } diff --git a/PerfectNumbers.Core/Gpu/AcceleratorReferenceComparer.cs b/PerfectNumbers.Core/Gpu/AcceleratorReferenceComparer.cs new file mode 100644 index 0000000..5506db1 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/AcceleratorReferenceComparer.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu; + +internal sealed class AcceleratorReferenceComparer : IEqualityComparer +{ + internal static AcceleratorReferenceComparer Instance { get; } = new(); + + public bool Equals(Accelerator? x, Accelerator? y) => ReferenceEquals(x, y); + + public int GetHashCode(Accelerator obj) => RuntimeHelpers.GetHashCode(obj); +} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs new file mode 100644 index 0000000..8994784 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicGpuTables.cs @@ -0,0 +1,142 @@ +using System.Collections.Concurrent; +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu.Accelerators; + +internal sealed class HeuristicGpuTables +{ + private static readonly ConcurrentDictionary _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators); + + internal static HeuristicGpuTables EnsureStaticTables(Accelerator accelerator, AcceleratorStream stream) + => _sharedTables.GetOrAdd(accelerator, _ => new HeuristicGpuTables(accelerator, stream)); + + internal HeuristicGpuTables(Accelerator accelerator, AcceleratorStream stream) + { + // lock (accelerator) + { + var heuristicGroupA = HeuristicPrimeSieves.GroupADivisorsStorage; + HeuristicGroupADivisors = CopySpanToDevice(accelerator, stream, heuristicGroupA); + + var heuristicGroupASquares = HeuristicPrimeSieves.GroupADivisorSquaresStorage; + HeuristicGroupADivisorSquares = CopySpanToDevice(accelerator, stream, heuristicGroupASquares); + + HeuristicGroupBDivisorsEnding1 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastOneWithoutLastThree); + HeuristicGroupBDivisorSquaresEnding1 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree); + + HeuristicGroupBDivisorsEnding7 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree); + HeuristicGroupBDivisorSquaresEnding7 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree); + + HeuristicGroupBDivisorsEnding9 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastNineWithoutLastThree); + HeuristicGroupBDivisorSquaresEnding9 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree); + + var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1; + HeuristicCombinedDivisorsEnding1 = CopySpanToDevice(accelerator, stream, combinedEnding1); + var combinedSquaresEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Squares; + HeuristicCombinedDivisorSquaresEnding1 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding1); + + var combinedEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3; + HeuristicCombinedDivisorsEnding3 = CopySpanToDevice(accelerator, stream, combinedEnding3); + var combinedSquaresEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Squares; + HeuristicCombinedDivisorSquaresEnding3 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding3); + + var combinedEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7; + HeuristicCombinedDivisorsEnding7 = CopySpanToDevice(accelerator, stream, combinedEnding7); + var combinedSquaresEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Squares; + HeuristicCombinedDivisorSquaresEnding7 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding7); + + var combinedEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9; + HeuristicCombinedDivisorsEnding9 = CopySpanToDevice(accelerator, stream, combinedEnding9); + var combinedSquaresEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Squares; + HeuristicCombinedDivisorSquaresEnding9 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding9); + } + } + + internal readonly MemoryBuffer1D HeuristicGroupADivisors; + internal readonly MemoryBuffer1D HeuristicGroupADivisorSquares; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding1; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding1; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding7; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding7; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorsEnding9; + internal readonly MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding9; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding1; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding1; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding3; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding3; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding7; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding7; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding9; + internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9; + + internal HeuristicGpuDivisorTables CreateHeuristicDivisorTables() => new( + HeuristicCombinedDivisorsEnding1.View, + HeuristicCombinedDivisorsEnding3.View, + HeuristicCombinedDivisorsEnding7.View, + HeuristicCombinedDivisorsEnding9.View, + HeuristicCombinedDivisorSquaresEnding1.View, + HeuristicCombinedDivisorSquaresEnding3.View, + HeuristicCombinedDivisorSquaresEnding7.View, + HeuristicCombinedDivisorSquaresEnding9.View, + HeuristicGroupADivisors.View, + HeuristicGroupADivisorSquares.View, + HeuristicGroupBDivisorsEnding1.View, + HeuristicGroupBDivisorSquaresEnding1.View, + HeuristicGroupBDivisorsEnding7.View, + HeuristicGroupBDivisorSquaresEnding7.View, + HeuristicGroupBDivisorsEnding9.View, + HeuristicGroupBDivisorSquaresEnding9.View); + + private static MemoryBuffer1D CopySpanToDevice(Accelerator accelerator, AcceleratorStream stream, in ulong[] span) + { + MemoryBuffer1D? buffer; + // lock (accelerator) + { + buffer = accelerator.Allocate1D(stream, span); + } + + return buffer; + } + + private static MemoryBuffer1D CopyUintSpanToDevice(Accelerator accelerator, AcceleratorStream stream, ReadOnlySpan span) + { + MemoryBuffer1D? buffer; + // lock (accelerator) + { + buffer = accelerator.Allocate1D(span.Length); + } + + if (!span.IsEmpty) + { + var converted = new ulong[span.Length]; + for (int i = 0; i < span.Length; i++) + { + converted[i] = span[i]; + } + + buffer.View.CopyFromCPU(stream, converted); + } + + return buffer; + } + + internal void Dispose() + { + HeuristicGroupADivisors.Dispose(); + HeuristicGroupADivisorSquares.Dispose(); + HeuristicGroupBDivisorsEnding1.Dispose(); + HeuristicGroupBDivisorSquaresEnding1.Dispose(); + HeuristicGroupBDivisorsEnding7.Dispose(); + HeuristicGroupBDivisorSquaresEnding7.Dispose(); + HeuristicGroupBDivisorsEnding9.Dispose(); + HeuristicGroupBDivisorSquaresEnding9.Dispose(); + HeuristicCombinedDivisorsEnding1.Dispose(); + HeuristicCombinedDivisorSquaresEnding1.Dispose(); + HeuristicCombinedDivisorsEnding3.Dispose(); + HeuristicCombinedDivisorSquaresEnding3.Dispose(); + HeuristicCombinedDivisorsEnding7.Dispose(); + HeuristicCombinedDivisorSquaresEnding7.Dispose(); + HeuristicCombinedDivisorsEnding9.Dispose(); + HeuristicCombinedDivisorSquaresEnding9.Dispose(); + } +} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicPrimeTesterAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicPrimeTesterAccelerator.cs new file mode 100644 index 0000000..2654e7d --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicPrimeTesterAccelerator.cs @@ -0,0 +1,208 @@ +using System.Runtime.CompilerServices; +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu.Accelerators; + +internal sealed class HeuristicPrimeTesterAccelerator +{ + #region Pool + + [ThreadStatic] + private static Queue? Pool; + + 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 heuristic accelerator {i}..."); + var accelerator = accelerators[i]; + AcceleratorStream stream = accelerator.CreateStream(); + Accelerators.HeuristicGpuTables.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($"Heuristic accelerator {i} is ready"); + } + } + + internal static HeuristicPrimeTesterAccelerator Rent(int minBufferCapacity = 1) + { + var pool = Pool ??= new(); + if (!pool.TryDequeue(out var lease)) + { + lease = new HeuristicPrimeTesterAccelerator(minBufferCapacity); + } + else + { + lease.EnsureCapacity(minBufferCapacity); + } + + return lease; + } + + internal static void Return(HeuristicPrimeTesterAccelerator 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 HeuristicGpuDivisorTables _heuristicDivisorTables; + internal readonly Context Context; + public readonly Accelerator Accelerator; + public AcceleratorStream? Stream; + + [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; + + internal static Action, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables> GetHeuristicTrialDivisionKernel(Accelerator accelerator) + { + var pool = HeuristicTrialDivisionKernel ??= []; + if (pool.TryGetValue(accelerator, out var cached)) + { + return cached; + } + + cached = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel)).CreateLauncherDelegate, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>>(); + pool[accelerator] = cached; + return cached; + } + + + public MemoryBuffer1D Input = null!; + public MemoryBuffer1D Output = null!; + public MemoryBuffer1D HeuristicFlag = null!; + public int BufferCapacity; + + private HeuristicGpuTables _sharedTables; + public MemoryBuffer1D HeuristicGroupADivisors => _sharedTables.HeuristicGroupADivisors; + public MemoryBuffer1D HeuristicGroupADivisorSquares => _sharedTables.HeuristicGroupADivisorSquares; + public MemoryBuffer1D HeuristicGroupBDivisorsEnding1 => _sharedTables.HeuristicGroupBDivisorsEnding1; + public MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding1 => _sharedTables.HeuristicGroupBDivisorSquaresEnding1; + public MemoryBuffer1D HeuristicGroupBDivisorsEnding7 => _sharedTables.HeuristicGroupBDivisorsEnding7; + public MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding7 => _sharedTables.HeuristicGroupBDivisorSquaresEnding7; + public MemoryBuffer1D HeuristicGroupBDivisorsEnding9 => _sharedTables.HeuristicGroupBDivisorsEnding9; + public MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding9 => _sharedTables.HeuristicGroupBDivisorSquaresEnding9; + public MemoryBuffer1D HeuristicCombinedDivisorsEnding1 => _sharedTables.HeuristicCombinedDivisorsEnding1; + public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding1 => _sharedTables.HeuristicCombinedDivisorSquaresEnding1; + public MemoryBuffer1D HeuristicCombinedDivisorsEnding3 => _sharedTables.HeuristicCombinedDivisorsEnding3; + public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding3 => _sharedTables.HeuristicCombinedDivisorSquaresEnding3; + public MemoryBuffer1D HeuristicCombinedDivisorsEnding7 => _sharedTables.HeuristicCombinedDivisorsEnding7; + public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding7 => _sharedTables.HeuristicCombinedDivisorSquaresEnding7; + public MemoryBuffer1D HeuristicCombinedDivisorsEnding9 => _sharedTables.HeuristicCombinedDivisorsEnding9; + public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9 => _sharedTables.HeuristicCombinedDivisorSquaresEnding9; + + public HeuristicGpuDivisorTables HeuristicGpuTables => _heuristicDivisorTables; + + internal HeuristicPrimeTesterAccelerator(int minBufferCapacity) + { + Accelerator accelerator = AcceleratorPool.Shared.Rent(); + Context = accelerator.Context; + + Accelerator = accelerator; + AcceleratorStream stream = accelerator.CreateStream(); + // var kernels = _kernels[index]; //GpuKernelPool.Kernels; + // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); + + _sharedTables = Accelerators.HeuristicGpuTables.EnsureStaticTables(accelerator, stream); + var heuristicDivisorTables = _sharedTables.CreateHeuristicDivisorTables(); + _heuristicDivisorTables = heuristicDivisorTables; + + 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/Accelerators/LastDigitGpuTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs new file mode 100644 index 0000000..3ff4ea5 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/LastDigitGpuTables.cs @@ -0,0 +1,58 @@ +using System.Collections.Concurrent; +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu.Accelerators; + +internal sealed class LastDigitGpuTables +{ + private static readonly ConcurrentDictionary _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators); + + internal static LastDigitGpuTables EnsureStaticTables(Accelerator accelerator, AcceleratorStream stream) + => _sharedTables.GetOrAdd(accelerator, _ => new LastDigitGpuTables(accelerator, stream)); + + internal LastDigitGpuTables(Accelerator accelerator, AcceleratorStream stream) + { + var primesLastOne = DivisorGenerator.SmallPrimesLastOne; + var primesLastSeven = DivisorGenerator.SmallPrimesLastSeven; + var primesLastThree = DivisorGenerator.SmallPrimesLastThree; + var primesLastNine = DivisorGenerator.SmallPrimesLastNine; + var primesPow2LastOne = DivisorGenerator.SmallPrimesPow2LastOne; + 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); + } + } + + internal readonly MemoryBuffer1D DevicePrimesLastOne; + internal readonly MemoryBuffer1D DevicePrimesLastSeven; + internal readonly MemoryBuffer1D DevicePrimesLastThree; + internal readonly MemoryBuffer1D DevicePrimesLastNine; + internal readonly MemoryBuffer1D DevicePrimesPow2LastOne; + internal readonly MemoryBuffer1D DevicePrimesPow2LastSeven; + internal readonly MemoryBuffer1D DevicePrimesPow2LastThree; + internal readonly MemoryBuffer1D DevicePrimesPow2LastNine; + + internal void Dispose() + { + DevicePrimesLastOne.Dispose(); + DevicePrimesLastSeven.Dispose(); + DevicePrimesLastThree.Dispose(); + DevicePrimesLastNine.Dispose(); + DevicePrimesPow2LastOne.Dispose(); + DevicePrimesPow2LastSeven.Dispose(); + DevicePrimesPow2LastThree.Dispose(); + DevicePrimesPow2LastNine.Dispose(); + } +} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs index 1bd5cdb..7b63b86 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/Pow2MontgomeryAccelerator.cs @@ -44,8 +44,8 @@ public sealed class Pow2MontgomeryAccelerator public readonly Accelerator Accelerator; public readonly MemoryBuffer1D Input; public readonly MemoryBuffer1D Output; - public MemoryBuffer1D, Stride1D.Dense> ToTestOnDevice; - public Dictionary ToTestOnHost = []; + public MemoryBuffer1D, Stride1D.Dense> Pow2ModEntriesToTestOnDevice; + public Dictionary Pow2ModEntriesToTestOnHost = []; public AcceleratorStream? Stream; public readonly Action, MontgomeryDivisorData, ArrayView1D> ConvertToStandardKernel; @@ -55,11 +55,11 @@ public sealed class Pow2MontgomeryAccelerator public void EnsureCapacity(int factorsCount) { - if (factorsCount > ToTestOnDevice.Length) + if (factorsCount > Pow2ModEntriesToTestOnDevice.Length) { Accelerator accelerator = Accelerator; - ToTestOnDevice.Dispose(); - ToTestOnDevice = accelerator.Allocate1D>(factorsCount); + Pow2ModEntriesToTestOnDevice.Dispose(); + Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D>(factorsCount); } } @@ -70,7 +70,7 @@ public sealed class Pow2MontgomeryAccelerator Input = accelerator.Allocate1D(1); Output = accelerator.Allocate1D(1); - ToTestOnDevice = accelerator.Allocate1D>(factorsCount); + Pow2ModEntriesToTestOnDevice = accelerator.Allocate1D>(factorsCount); var keepKernel = accelerator.LoadAutoGroupedStreamKernel, MontgomeryDivisorData, ArrayView1D>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery); var keepLauncher = KernelUtil.GetKernel(keepKernel); @@ -91,7 +91,7 @@ public sealed class Pow2MontgomeryAccelerator Input.Dispose(); Output.Dispose(); Stream?.Dispose(); - ToTestOnDevice.Dispose(); + Pow2ModEntriesToTestOnDevice.Dispose(); // Accelerator is shared with other threads // Accelerator.Dispose(); } diff --git a/PerfectNumbers.Core/Gpu/Accelerators/PrimeTesterByLastDigitAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/PrimeTesterByLastDigitAccelerator.cs new file mode 100644 index 0000000..4987082 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/PrimeTesterByLastDigitAccelerator.cs @@ -0,0 +1,192 @@ +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/GpuKernelPool.cs b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs index a7cc055..3b5a533 100644 --- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs @@ -19,91 +19,156 @@ public class GpuKernelPool return Interlocked.CompareExchange(ref target, newValue, null) ?? newValue; } - internal static KernelContainer GetOrAddKernels(Accelerator accelerator, AcceleratorStream stream) + internal static KernelContainer GetOrAddKernels(Accelerator accelerator, AcceleratorStream stream, KernelType kernelType) { var pool = _pool ??= []; - if (pool.TryGetValue(accelerator, out var kernelContainer)) + if (!pool.TryGetValue(accelerator, out var kernels)) { - return kernelContainer; + kernels = new KernelContainer(); } - var kernels = new KernelContainer(); - - InitOnce(ref kernels.Order, () => + if (kernelType.HasFlag(KernelType.OrderKernelScan)) { - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView>(OrderKernels.OrderKernelScan); + InitOrderKernelScan(accelerator, kernels); + } - var kernel = KernelUtil.GetKernel(loaded); - - return kernel.CreateLauncherDelegate, ArrayView>>(); - }); - - InitOnce(ref kernels.Pow2Mod, () => + if (kernelType.HasFlag(KernelType.Pow2ModKernelScan)) { - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(Pow2ModKernels.Pow2ModKernelScan); + InitPow2ModKernelScan(accelerator, kernels); + } - var kernel = KernelUtil.GetKernel(loaded); - - return kernel.CreateLauncherDelegate, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); - }); - - InitOnce(ref kernels.Incremental, () => + if (kernelType.HasFlag(KernelType.IncrementalKernelScan)) { - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalKernelScan); + InitIncrementalKernelScan(accelerator, kernels); + } - var kernel = KernelUtil.GetKernel(loaded); - - return kernel.CreateLauncherDelegate, ArrayView1D>>(); - }); - - InitOnce(ref kernels.IncrementalOrder, () => + if (kernelType.HasFlag(KernelType.IncrementalOrderKernelScan)) { - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalOrderKernelScan); + IncrementalOrderKernelScan(accelerator, kernels); + } - var kernel = KernelUtil.GetKernel(loaded); - - return kernel.CreateLauncherDelegate, ArrayView1D>>(); - }); - - InitOnce(ref kernels.Pow2ModOrder, () => + if (kernelType.HasFlag(KernelType.Pow2ModOrderKernelScan)) { - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D>(Pow2ModKernels.Pow2ModOrderKernelScan); + InitPow2ModOrderKernelScan(accelerator, kernels); + } - var kernel = KernelUtil.GetKernel(loaded); - - return kernel.CreateLauncherDelegate, ArrayView1D>>(); - }); - - InitOnce(ref kernels.SpecialMax, () => + if (kernelType.HasFlag(KernelType.EvaluateSpecialMaxCandidatesKernel)) { - var loaded = accelerator.LoadAutoGroupedStreamKernel, int, MontgomeryDivisorData, ArrayView1D, ArrayView1D>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel); + InitEvaluateSpecialMaxCandidatesKernel(accelerator, kernels); + } - var kernel = KernelUtil.GetKernel(loaded); - - return kernel.CreateLauncherDelegate, int, MontgomeryDivisorData, ArrayView1D, ArrayView1D>>(); - }); - - InitOnce(ref kernels.SmallPrimeFactor, () => + if (kernelType.HasFlag(KernelType.SmallPrimeFactorKernelScan)) { - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan); + InitSmallPrimeFactorKernelScan(accelerator, kernels); + } - var kernel = KernelUtil.GetKernel(loaded); - - return kernel.CreateLauncherDelegate, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); - }); - - PreloadStaticTables(accelerator, kernels, stream); + PreloadStaticTables(accelerator, kernels, stream, kernelType); pool[accelerator] = kernels; return kernels; + + static void InitOrderKernelScan(Accelerator accelerator, KernelContainer kernels) + { + InitOnce(ref kernels.Order, () => + { + var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView>(OrderKernels.OrderKernelScan); + + var kernel = KernelUtil.GetKernel(loaded); + + return kernel.CreateLauncherDelegate, ArrayView>>(); + }); + } + + static void InitPow2ModKernelScan(Accelerator accelerator, KernelContainer kernels) + { + InitOnce(ref kernels.Pow2Mod, () => + { + var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(Pow2ModKernels.Pow2ModKernelScan); + + var kernel = KernelUtil.GetKernel(loaded); + + return kernel.CreateLauncherDelegate, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); + }); + } + + static void InitIncrementalKernelScan(Accelerator accelerator, KernelContainer kernels) + { + InitOnce(ref kernels.Incremental, () => + { + var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalKernelScan); + + var kernel = KernelUtil.GetKernel(loaded); + + return kernel.CreateLauncherDelegate, ArrayView1D>>(); + }); + } + + static void IncrementalOrderKernelScan(Accelerator accelerator, KernelContainer kernels) + { + InitOnce(ref kernels.IncrementalOrder, () => + { + var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D>(IncrementalKernels.IncrementalOrderKernelScan); + + var kernel = KernelUtil.GetKernel(loaded); + + return kernel.CreateLauncherDelegate, ArrayView1D>>(); + }); + } + + static void InitPow2ModOrderKernelScan(Accelerator accelerator, KernelContainer kernels) + { + InitOnce(ref kernels.Pow2ModOrder, () => + { + var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D>(Pow2ModKernels.Pow2ModOrderKernelScan); + + var kernel = KernelUtil.GetKernel(loaded); + + return kernel.CreateLauncherDelegate, ArrayView1D>>(); + }); + } + + static void InitEvaluateSpecialMaxCandidatesKernel(Accelerator accelerator, KernelContainer kernels) + { + InitOnce(ref kernels.SpecialMax, () => + { + var loaded = accelerator.LoadAutoGroupedStreamKernel, int, MontgomeryDivisorData, ArrayView1D, ArrayView1D>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel); + + var kernel = KernelUtil.GetKernel(loaded); + + return kernel.CreateLauncherDelegate, int, MontgomeryDivisorData, ArrayView1D, ArrayView1D>>(); + }); + } + + static void InitSmallPrimeFactorKernelScan(Accelerator accelerator, KernelContainer kernels) + { + InitOnce(ref kernels.SmallPrimeFactor, () => + { + var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan); + + var kernel = KernelUtil.GetKernel(loaded); + + return kernel.CreateLauncherDelegate, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>>(); + }); + } } - internal static void PreloadStaticTables(Accelerator accelerator, KernelContainer kernels, AcceleratorStream stream) + internal static void PreloadStaticTables(Accelerator accelerator, KernelContainer kernels, AcceleratorStream stream, KernelType kernelType) { - EnsureSmallCyclesOnDevice(accelerator, kernels, stream); - EnsureSmallPrimesOnDevice(accelerator, kernels, stream); - EnsureSmallPrimeFactorTables(accelerator, kernels, stream); + if (kernelType.HasFlag(KernelType.SmallCycles)) + { + EnsureSmallCyclesOnDevice(accelerator, kernels, stream); + } + + if (kernelType.HasFlag(KernelType.SmallPrimes)) + { + EnsureSmallPrimesOnDevice(accelerator, kernels, stream); + } + + if (kernelType.HasFlag(KernelType.SmallPrimeFactorKernelScan)) + { + EnsureSmallPrimeFactorTables(accelerator, kernels, stream); + } } public static ArrayView1D GetSmallCyclesOnDevice(KernelContainer kernels) => kernels.SmallCycles!; @@ -166,12 +231,12 @@ public class GpuKernelPool // lock (accelerator) // { - deviceLastOne = accelerator.Allocate1D(hostLastOne.Length); - deviceLastSeven = accelerator.Allocate1D(hostLastSeven.Length); - deviceLastOnePow2 = accelerator.Allocate1D(hostLastOnePow2.Length); - deviceLastSevenPow2 = accelerator.Allocate1D(hostLastSevenPow2.Length); + deviceLastOne = accelerator.Allocate1D(hostLastOne.Length); + deviceLastSeven = accelerator.Allocate1D(hostLastSeven.Length); + deviceLastOnePow2 = accelerator.Allocate1D(hostLastOnePow2.Length); + deviceLastSevenPow2 = accelerator.Allocate1D(hostLastSevenPow2.Length); // } - + deviceLastOne.View.CopyFromCPU(stream, hostLastOne); deviceLastSeven.View.CopyFromCPU(stream, hostLastSeven); deviceLastOnePow2.View.CopyFromCPU(stream, hostLastOnePow2); @@ -186,7 +251,7 @@ public class GpuKernelPool kernels.SmallPrimeFactorsPrimes!, kernels.SmallPrimeFactorsSquares! ); - + private static void EnsureSmallPrimeFactorTables(Accelerator accelerator, KernelContainer kernels, AcceleratorStream stream) { if (kernels.SmallPrimeFactorsPrimes is { } && kernels.SmallPrimeFactorsSquares is { }) @@ -206,7 +271,7 @@ public class GpuKernelPool devicePrimes = accelerator.Allocate1D(length); deviceSquares = accelerator.Allocate1D(length); } - + devicePrimes.View.CopyFromCPU(stream, hostPrimes); deviceSquares.View.CopyFromCPU(stream, hostSquares); diff --git a/PerfectNumbers.Core/Gpu/GpuStaticTableInitializer.cs b/PerfectNumbers.Core/Gpu/GpuStaticTableInitializer.cs index 0f44b69..5046c8c 100644 --- a/PerfectNumbers.Core/Gpu/GpuStaticTableInitializer.cs +++ b/PerfectNumbers.Core/Gpu/GpuStaticTableInitializer.cs @@ -8,7 +8,7 @@ internal static class GpuStaticTableInitializer internal static void EnsureStaticTables(Accelerator accelerator, KernelContainer kernels, AcceleratorStream stream) { // PrimeTester.PrimeTesterGpuContextPool.EnsureStaticTables(accelerator); - GpuKernelPool.PreloadStaticTables(accelerator, kernels, stream); + GpuKernelPool.PreloadStaticTables(accelerator, kernels, stream, KernelType.None); // PrimeOrderGpuHeuristics.PreloadStaticTables(accelerator, stream); } diff --git a/PerfectNumbers.Core/Gpu/KernelType.cs b/PerfectNumbers.Core/Gpu/KernelType.cs new file mode 100644 index 0000000..683530f --- /dev/null +++ b/PerfectNumbers.Core/Gpu/KernelType.cs @@ -0,0 +1,16 @@ +namespace PerfectNumbers.Core.Gpu; + +[Flags] +public enum KernelType +{ + None, + SmallCycles, + SmallPrimes, + Pow2ModKernelScan, + OrderKernelScan, + IncrementalKernelScan, + IncrementalOrderKernelScan, + Pow2ModOrderKernelScan, + EvaluateSpecialMaxCandidatesKernel, + SmallPrimeFactorKernelScan +} diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs index b4988a0..377e902 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeTesterKernels.cs @@ -20,12 +20,10 @@ internal static class PrimeTesterKernels public static void SmallPrimeSieveKernel( Index1D index, ArrayView numbers, - ArrayView smallPrimes, ArrayView smallPrimesLastOne, ArrayView smallPrimesLastSeven, ArrayView smallPrimesLastThree, ArrayView smallPrimesLastNine, - ArrayView smallPrimesPow2, ArrayView smallPrimesPow2LastOne, ArrayView smallPrimesPow2LastSeven, ArrayView smallPrimesPow2LastThree, @@ -35,8 +33,8 @@ internal static class PrimeTesterKernels ulong n = numbers[index]; byte result = 1; - ArrayView primes = smallPrimes; - ArrayView primeSquares = smallPrimesPow2; + ArrayView primes; + ArrayView primeSquares; ulong lastDigit = n % 10UL; switch (lastDigit) { @@ -55,7 +53,9 @@ internal static class PrimeTesterKernels case 9UL: primes = smallPrimesLastNine; primeSquares = smallPrimesPow2LastNine; - break; + break; + default: + return; } int length = (int)primes.Length; diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs index fbed3c1..4a1e401 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberIncrementalGpuTester.cs @@ -24,7 +24,7 @@ public class MersenneNumberIncrementalGpuTester(GpuKernelType kernelType, bool u ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL; byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters - KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); + KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.Pow2ModKernelScan | KernelType.IncrementalKernelScan); var pow2Kernel = kernels.Pow2Mod!; var incKernel = kernels.Incremental!; ulong step10 = (exponent.Mod10() << 1).Mod10(); diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs index bdfe669..4631fa1 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberOrderGpuTester.cs @@ -18,7 +18,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO ulong divMul = (ulong)((((UInt128)1 << 64) - UInt128.One) / exponent) + 1UL; byte last = lastDigit == LastDigit.Seven ? (byte)1 : (byte)0; // ILGPU kernels do not support bool parameters - KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); + KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.Pow2ModOrderKernelScan | KernelType.IncrementalOrderKernelScan); var kernel = _kernelType switch { GpuKernelType.Pow2Mod => kernels.Pow2ModOrder!, diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs index a7232ba..fc02b90 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs @@ -33,7 +33,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) var stream = accelerator.CreateStream(); var kernel = GetPow2ModKernel(accelerator); var resources = RentResources(accelerator, GpuConstants.ScanBatchSize); - var gpuKernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); + var gpuKernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.SmallCycles | KernelType.SmallPrimes); var orderBuffer = resources.OrderBuffer; ulong[] orderArray = resources.OrderArray; diff --git a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs index 20f9b16..d53db13 100644 --- a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs @@ -2,6 +2,7 @@ using Open.Numeric.Primes; using ILGPU.Runtime; using System.Runtime.CompilerServices; using PerfectNumbers.Core.Gpu; +using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core; @@ -77,10 +78,10 @@ public sealed class HeuristicCombinedPrimeTester private static readonly ulong[] CombinedDivisorsEnding7ThreeATwoB; private static readonly ulong[] CombinedDivisorsEnding9ThreeATwoB; - private static readonly ulong[] CombinedDivisorsEnding1; - private static readonly ulong[] CombinedDivisorsEnding3; - private static readonly ulong[] CombinedDivisorsEnding7; - private static readonly ulong[] CombinedDivisorsEnding9; + internal static readonly ulong[] CombinedDivisorsEnding1; + internal static readonly ulong[] CombinedDivisorsEnding3; + internal static readonly ulong[] CombinedDivisorsEnding7; + internal static readonly ulong[] CombinedDivisorsEnding9; private static readonly ulong[] CombinedDivisorsEnding1TwoAOneBSquares; private static readonly ulong[] CombinedDivisorsEnding3TwoAOneBSquares; @@ -102,10 +103,10 @@ public sealed class HeuristicCombinedPrimeTester private static readonly ulong[] CombinedDivisorsEnding7ThreeATwoBSquares; private static readonly ulong[] CombinedDivisorsEnding9ThreeATwoBSquares; - private static readonly ulong[] CombinedDivisorsEnding1Squares; - private static readonly ulong[] CombinedDivisorsEnding3Squares; - private static readonly ulong[] CombinedDivisorsEnding7Squares; - private static readonly ulong[] CombinedDivisorsEnding9Squares; + internal static readonly ulong[] CombinedDivisorsEnding1Squares; + internal static readonly ulong[] CombinedDivisorsEnding3Squares; + internal static readonly ulong[] CombinedDivisorsEnding7Squares; + internal static readonly ulong[] CombinedDivisorsEnding9Squares; private static readonly ulong[] HeuristicSmallCycleSnapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); @@ -223,11 +224,10 @@ public sealed class HeuristicCombinedPrimeTester private static bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10) { - GpuPrimeWorkLimiter.Acquire(); - var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1); + var gpu = HeuristicPrimeTesterAccelerator.Rent(1); var accelerator = gpu.Accelerator; var stream = gpu.Stream; - var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.GetHeuristicTrialDivisionKernel(accelerator); + var kernel = HeuristicPrimeTesterAccelerator.GetHeuristicTrialDivisionKernel(accelerator); var flagView1D = gpu.HeuristicFlag.View; var flagView = flagView1D.AsContiguous(); @@ -236,6 +236,10 @@ public sealed class HeuristicCombinedPrimeTester int compositeFlag = 0; flagView1D.CopyFromCPU(stream, ref compositeFlag, 1); + stream!.Synchronize(); + + GpuPrimeWorkLimiter.Acquire(); + kernel( stream, divisorLength, @@ -247,10 +251,10 @@ public sealed class HeuristicCombinedPrimeTester flagView1D.CopyToCPU(stream, ref compositeFlag, 1); stream.Synchronize(); - compositeDetected = compositeFlag != 0; - - gpu.Dispose(); GpuPrimeWorkLimiter.Release(); + + compositeDetected = compositeFlag != 0; + HeuristicPrimeTesterAccelerator.Return(gpu); return compositeDetected; } diff --git a/PerfectNumbers.Core/HeuristicPrimeSieves.cs b/PerfectNumbers.Core/HeuristicPrimeSieves.cs index 3fb19ba..43008de 100644 --- a/PerfectNumbers.Core/HeuristicPrimeSieves.cs +++ b/PerfectNumbers.Core/HeuristicPrimeSieves.cs @@ -15,8 +15,8 @@ public static class HeuristicPrimeSieves private static readonly ushort[] GroupBResiduesEnding7 = [17, 37, 47, 67, 97, 107, 127, 137, 157, 167, 187, 197]; private static readonly ushort[] GroupBResiduesEnding9 = [19, 29, 59, 79, 89, 109, 139, 149, 169, 179, 199, 209]; - private static readonly ulong[] GroupADivisorsStorage; - private static readonly ulong[] GroupADivisorSquaresStorage; + internal static readonly ulong[] GroupADivisorsStorage; + internal static readonly ulong[] GroupADivisorSquaresStorage; private static readonly ulong[] GroupBDivisorsEnding1Storage; private static readonly ulong[] GroupBDivisorSquaresEnding1Storage; diff --git a/PerfectNumbers.Core/HeuristicPrimeTester.cs b/PerfectNumbers.Core/HeuristicPrimeTester.cs index 5b17183..1d9a6dc 100644 --- a/PerfectNumbers.Core/HeuristicPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicPrimeTester.cs @@ -2,6 +2,7 @@ using Open.Numeric.Primes; using System.Runtime.CompilerServices; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu; +using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core; @@ -241,10 +242,10 @@ public sealed class HeuristicPrimeTester private bool HeuristicTrialDivisionGpuDetectsDivisor(ulong n, ulong maxDivisorSquare, byte nMod10) { GpuPrimeWorkLimiter.Acquire(); - var gpu = PrimeTester.PrimeTesterGpuContextPool.Rent(1); + var gpu = HeuristicPrimeTesterAccelerator.Rent(1); var accelerator = gpu.Accelerator; var stream = gpu.Stream; - var kernel = PrimeTester.PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.GetHeuristicTrialDivisionKernel(accelerator); + var kernel = HeuristicPrimeTesterAccelerator.GetHeuristicTrialDivisionKernel(accelerator); var flagView1D = gpu.HeuristicFlag.View; var flagView = flagView1D.AsContiguous(); @@ -255,7 +256,7 @@ public sealed class HeuristicPrimeTester flagView1D.CopyFromCPU(stream, ref compositeFlag, 1); kernel( - stream, + stream!, groupALength, flagView, n, @@ -264,7 +265,7 @@ public sealed class HeuristicPrimeTester gpu.HeuristicGpuTables); flagView1D.CopyToCPU(stream, ref compositeFlag, 1); - stream.Synchronize(); + stream!.Synchronize(); compositeDetected = compositeFlag != 0; diff --git a/PerfectNumbers.Core/MersenneNumberTester.cs b/PerfectNumbers.Core/MersenneNumberTester.cs index c7b746a..554f780 100644 --- a/PerfectNumbers.Core/MersenneNumberTester.cs +++ b/PerfectNumbers.Core/MersenneNumberTester.cs @@ -150,7 +150,7 @@ public sealed class MersenneNumberTester( GpuPrimeWorkLimiter.Acquire(); var accelerator = AcceleratorPool.Shared.Rent(); var stream = accelerator.CreateStream(); - var orderKernel = GpuKernelPool.GetOrAddKernels(accelerator, stream).Order!; + var orderKernel = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.OrderKernelScan).Order!; // Guard long-running kernels by chunking the warm-up across batches. // Reuse the same ScanBatchSize knob used for scanning. diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index 45fef63..7a5e088 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -17,7 +17,7 @@ internal static partial class PrimeOrderCalculator private static ulong CalculateByFactorizationGpu(Pow2MontgomeryAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; - Dictionary counts = gpu.ToTestOnHost; + Dictionary counts = gpu.Pow2ModEntriesToTestOnHost; counts.Clear(); FactorCompletelyCpu(phi, counts); @@ -39,7 +39,7 @@ internal static partial class PrimeOrderCalculator AcceleratorStream stream = gpu.Stream!; gpu.EnsureCapacity(entryCount); - gpu.ToTestOnDevice.View.CopyFromCPU(stream, entries); + gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries); ulong order = phi; var kernel = gpu.CheckFactorsKernel; @@ -47,7 +47,7 @@ internal static partial class PrimeOrderCalculator GpuPrimeWorkLimiter.Acquire(); - kernel.Launch(stream, 1, entryCount, phi, gpu.ToTestOnDevice.View, divisorData, gpu.Output.View); + kernel.Launch(stream, 1, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.Output.View); gpu.Output.View.CopyToCPU(stream, ref order, 1); stream.Synchronize(); @@ -66,7 +66,7 @@ internal static partial class PrimeOrderCalculator GpuPrimeWorkLimiter.Acquire(); Accelerator accelerator = AcceleratorPool.Shared.Rent(); var stream = accelerator.CreateStream(); - KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); + KernelContainer kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.SmallPrimeFactorKernelScan); ScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, GpuSmallPrimeFactorSlots, 0); @@ -152,7 +152,7 @@ internal static partial class PrimeOrderCalculator GpuPrimeWorkLimiter.Acquire(); Accelerator accelerator = AcceleratorPool.Shared.Rent(); var stream = accelerator.CreateStream(); - var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream); + var kernels = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.EvaluateSpecialMaxCandidatesKernel); ScratchBuffer scratch = GpuScratchBufferPool.Rent(accelerator, 0, factorCount); diff --git a/PerfectNumbers.Core/PrimeTester.cs b/PerfectNumbers.Core/PrimeTester.cs index 773b27d..8931bae 100644 --- a/PerfectNumbers.Core/PrimeTester.cs +++ b/PerfectNumbers.Core/PrimeTester.cs @@ -1,13 +1,11 @@ -using System; using System.Buffers; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ILGPU; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu; +using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core; @@ -96,10 +94,10 @@ public sealed class PrimeTester public static bool IsPrimeGpu(ulong n) { GpuPrimeWorkLimiter.Acquire(); - var gpu = PrimeTesterGpuContextPool.Rent(1); + var gpu = PrimeTesterByLastDigitAccelerator.Rent(1); var accelerator = gpu.Accelerator; var stream = gpu.Stream; - var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.GetSmallPrimeSieveKernel(accelerator); + var kernel = PrimeTesterByLastDigitAccelerator.GetSmallPrimeSieveKernel(accelerator); ulong value = n; // Span flag = stackalloc byte[1]; @@ -115,12 +113,10 @@ public sealed class PrimeTester stream, 1, inputView, - gpu.DevicePrimesDefault.View, gpu.DevicePrimesLastOne.View, gpu.DevicePrimesLastSeven.View, gpu.DevicePrimesLastThree.View, gpu.DevicePrimesLastNine.View, - gpu.DevicePrimesPow2Default.View, gpu.DevicePrimesPow2LastOne.View, gpu.DevicePrimesPow2LastSeven.View, gpu.DevicePrimesPow2LastThree.View, @@ -155,7 +151,7 @@ public sealed class PrimeTester return; } - PrimeTesterGpuContextPool.WarmUp(target, GpuBatchSize); + PrimeTesterByLastDigitAccelerator.WarmUp(); WarmedGpuLeaseCount = target; } } @@ -164,10 +160,10 @@ public sealed class PrimeTester public static void IsPrimeBatchGpu(ReadOnlySpan values, Span results) { GpuPrimeWorkLimiter.Acquire(); - var gpu = PrimeTesterGpuContextPool.Rent(GpuBatchSize); + var gpu = PrimeTesterByLastDigitAccelerator.Rent(GpuBatchSize); var accelerator = gpu.Accelerator; var stream = gpu.Stream; - var kernel = PrimeTesterGpuContextPool.PrimeTesterGpuContextLease.GetSmallPrimeSieveKernel(accelerator); + var kernel = PrimeTesterByLastDigitAccelerator.GetSmallPrimeSieveKernel(accelerator); int totalLength = values.Length; int batchSize = GpuBatchSize; @@ -190,12 +186,10 @@ public sealed class PrimeTester stream, count, inputView, - gpu.DevicePrimesDefault.View, gpu.DevicePrimesLastOne.View, gpu.DevicePrimesLastSeven.View, gpu.DevicePrimesLastThree.View, gpu.DevicePrimesLastNine.View, - gpu.DevicePrimesPow2Default.View, gpu.DevicePrimesPow2LastOne.View, gpu.DevicePrimesPow2LastSeven.View, gpu.DevicePrimesPow2LastThree.View, @@ -214,483 +208,15 @@ public sealed class PrimeTester GpuPrimeWorkLimiter.Release(); } - internal static class PrimeTesterGpuContextPool - { - [ThreadStatic] - private static Queue? Pool; - private static readonly object WarmUpLock = new(); - private static int WarmedLeaseCount; - - // internal static void EnsureStaticTables(Accelerator accelerator) - // { - // var tables = CreateSharedTables(accelerator); - // } - - internal static void WarmUp(int target, int minBufferCapacity) - { - // if (target <= 0) - // { - // return; - // } - - // lock (WarmUpLock) - // { - // if (target <= WarmedLeaseCount) - // { - // return; - // } - - // Accelerator accelerator = SharedGpuContext.Accelerator; - // AcceleratorStream stream = accelerator.CreateStream(); - // KernelContainer kernels = GpuKernelPool.Kernels; - - // GpuStaticTableInitializer.EnsureStaticTables(kernels, stream); - - // int toCreate = target - WarmedLeaseCount; - // for (int i = 0; i < toCreate; i++) - // { - // Pool.Enqueue(new PrimeTesterGpuContextLease(minBufferCapacity, stream)); - // } - - // stream.Synchronize(); - // stream.Dispose(); - // WarmedLeaseCount = target; - // } - } - - internal static PrimeTesterGpuContextLease Rent(int minBufferCapacity = 1) - { - var pool = Pool ??= new(); - if (!pool.TryDequeue(out var lease)) - { - lease = new PrimeTesterGpuContextLease(minBufferCapacity); - } - else - { - lease.EnsureCapacity(minBufferCapacity); - } - - return lease; - } - - - internal static void Return(PrimeTesterGpuContextLease lease) - { - var pool = Pool ??= new(); - pool.Enqueue(lease); - } - - internal static void DisposeAll() - { - if (Pool != null) - { - while (Pool.TryDequeue(out var lease)) - { - lease.DisposeResources(); - } - } - - WarmedLeaseCount = 0; - } - - internal static void Clear() - { - if (Pool != null) - { - lock (WarmUpLock) - { - var retained = new List(); - while (Pool.TryDequeue(out var lease)) - { - lease.DisposeResources(); - } - - WarmedLeaseCount = Pool.Count; - } - } - } - - internal sealed class PrimeTesterGpuContextLease - { - private readonly SharedHeuristicGpuTables _sharedTables; - private readonly HeuristicGpuDivisorTables _heuristicDivisorTables; - internal readonly Context AcceleratorContext; - 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, ArrayView, ArrayView>>? SmallPrimeSieveKernel; - - internal static Action, ArrayView, ArrayView, 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, ArrayView, ArrayView>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>(); - pool[accelerator] = cached; - return cached; - } - - [ThreadStatic] - public static Dictionary, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>>? HeuristicTrialDivisionKernel; - - internal static Action, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables> GetHeuristicTrialDivisionKernel(Accelerator accelerator) - { - var pool = HeuristicTrialDivisionKernel ??= []; - if (pool.TryGetValue(accelerator, out var cached)) - { - return cached; - } - - cached = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>(PrimeTesterKernels.HeuristicTrialDivisionKernel)).CreateLauncherDelegate, ulong, ulong, HeuristicGpuDivisorTableKind, HeuristicGpuDivisorTables>>(); - pool[accelerator] = cached; - return cached; - } - - - public MemoryBuffer1D Input = null!; - public MemoryBuffer1D Output = null!; - public MemoryBuffer1D HeuristicFlag = null!; - public int BufferCapacity; - - public MemoryBuffer1D DevicePrimesDefault => _sharedTables.DevicePrimesDefault; - public MemoryBuffer1D DevicePrimesLastOne => _sharedTables.DevicePrimesLastOne; - public MemoryBuffer1D DevicePrimesLastSeven => _sharedTables.DevicePrimesLastSeven; - public MemoryBuffer1D DevicePrimesLastThree => _sharedTables.DevicePrimesLastThree; - public MemoryBuffer1D DevicePrimesLastNine => _sharedTables.DevicePrimesLastNine; - public MemoryBuffer1D DevicePrimesPow2Default => _sharedTables.DevicePrimesPow2Default; - public MemoryBuffer1D DevicePrimesPow2LastOne => _sharedTables.DevicePrimesPow2LastOne; - public MemoryBuffer1D DevicePrimesPow2LastSeven => _sharedTables.DevicePrimesPow2LastSeven; - public MemoryBuffer1D DevicePrimesPow2LastThree => _sharedTables.DevicePrimesPow2LastThree; - public MemoryBuffer1D DevicePrimesPow2LastNine => _sharedTables.DevicePrimesPow2LastNine; - public MemoryBuffer1D HeuristicGroupADivisors => _sharedTables.HeuristicGroupADivisors; - public MemoryBuffer1D HeuristicGroupADivisorSquares => _sharedTables.HeuristicGroupADivisorSquares; - public MemoryBuffer1D HeuristicGroupBDivisorsEnding1 => _sharedTables.HeuristicGroupBDivisorsEnding1; - public MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding1 => _sharedTables.HeuristicGroupBDivisorSquaresEnding1; - public MemoryBuffer1D HeuristicGroupBDivisorsEnding7 => _sharedTables.HeuristicGroupBDivisorsEnding7; - public MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding7 => _sharedTables.HeuristicGroupBDivisorSquaresEnding7; - public MemoryBuffer1D HeuristicGroupBDivisorsEnding9 => _sharedTables.HeuristicGroupBDivisorsEnding9; - public MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding9 => _sharedTables.HeuristicGroupBDivisorSquaresEnding9; - public MemoryBuffer1D HeuristicCombinedDivisorsEnding1 => _sharedTables.HeuristicCombinedDivisorsEnding1; - public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding1 => _sharedTables.HeuristicCombinedDivisorSquaresEnding1; - public MemoryBuffer1D HeuristicCombinedDivisorsEnding3 => _sharedTables.HeuristicCombinedDivisorsEnding3; - public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding3 => _sharedTables.HeuristicCombinedDivisorSquaresEnding3; - public MemoryBuffer1D HeuristicCombinedDivisorsEnding7 => _sharedTables.HeuristicCombinedDivisorsEnding7; - public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding7 => _sharedTables.HeuristicCombinedDivisorSquaresEnding7; - public MemoryBuffer1D HeuristicCombinedDivisorsEnding9 => _sharedTables.HeuristicCombinedDivisorsEnding9; - public MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9 => _sharedTables.HeuristicCombinedDivisorSquaresEnding9; - - public HeuristicGpuDivisorTables HeuristicGpuTables => _heuristicDivisorTables; - - internal PrimeTesterGpuContextLease(int minBufferCapacity) - { - AcceleratorContext = SharedGpuContext.Context; - Accelerator accelerator = AcceleratorPool.Shared.Rent(); - // lock (_accelerators) - // { - // } - - Accelerator = accelerator; - AcceleratorStream stream = accelerator.CreateStream(); - // var kernels = _kernels[index]; //GpuKernelPool.Kernels; - // GpuStaticTableInitializer.EnsureStaticTables(accelerator, kernels, stream); - - var sharedTables = SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream); - _sharedTables = sharedTables; - - var heuristicDivisorTables = sharedTables.CreateHeuristicDivisorTables(); - _heuristicDivisorTables = heuristicDivisorTables; - - 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(); - } - } - - private sealed class AcceleratorReferenceComparer : IEqualityComparer - { - internal static AcceleratorReferenceComparer Instance { get; } = new(); - - public bool Equals(Accelerator? x, Accelerator? y) - { - return ReferenceEquals(x, y); - } - - public int GetHashCode(Accelerator obj) - { - return RuntimeHelpers.GetHashCode(obj); - } - } - - private sealed class SharedHeuristicGpuTables - { - private static readonly ConcurrentDictionary _sharedTables = new(20_480, PerfectNumberConstants.RollingAccelerators); - - private int _referenceCount; - - internal static SharedHeuristicGpuTables EnsureStaticTables(Accelerator accelerator, AcceleratorStream stream) - => _sharedTables.GetOrAdd(accelerator, _ => new SharedHeuristicGpuTables(accelerator, stream)); - - internal SharedHeuristicGpuTables(Accelerator accelerator, AcceleratorStream stream) - { - var primesDefault = DivisorGenerator.SmallPrimes; - var primesLastOne = DivisorGenerator.SmallPrimesLastOne; - var primesLastSeven = DivisorGenerator.SmallPrimesLastSeven; - var primesLastThree = DivisorGenerator.SmallPrimesLastThree; - var primesLastNine = DivisorGenerator.SmallPrimesLastNine; - var primesPow2Default = DivisorGenerator.SmallPrimesPow2; - var primesPow2LastOne = DivisorGenerator.SmallPrimesPow2LastOne; - var primesPow2LastSeven = DivisorGenerator.SmallPrimesPow2LastSeven; - var primesPow2LastThree = DivisorGenerator.SmallPrimesPow2LastThree; - var primesPow2LastNine = DivisorGenerator.SmallPrimesPow2LastNine; - - // lock (accelerator) - { - DevicePrimesDefault = accelerator.Allocate1D(primesDefault.Length); - DevicePrimesLastOne = accelerator.Allocate1D(primesLastOne.Length); - DevicePrimesLastSeven = accelerator.Allocate1D(primesLastSeven.Length); - DevicePrimesLastThree = accelerator.Allocate1D(primesLastThree.Length); - DevicePrimesLastNine = accelerator.Allocate1D(primesLastNine.Length); - DevicePrimesPow2Default = accelerator.Allocate1D(primesPow2Default.Length); - DevicePrimesPow2LastOne = accelerator.Allocate1D(primesPow2LastOne.Length); - DevicePrimesPow2LastSeven = accelerator.Allocate1D(primesPow2LastSeven.Length); - DevicePrimesPow2LastThree = accelerator.Allocate1D(primesPow2LastThree.Length); - DevicePrimesPow2LastNine = accelerator.Allocate1D(primesPow2LastNine.Length); - } - - DevicePrimesDefault.View.CopyFromCPU(stream, primesDefault); - DevicePrimesLastOne.View.CopyFromCPU(stream, primesLastOne); - DevicePrimesLastSeven.View.CopyFromCPU(stream, primesLastSeven); - DevicePrimesLastThree.View.CopyFromCPU(stream, primesLastThree); - DevicePrimesLastNine.View.CopyFromCPU(stream, primesLastNine); - DevicePrimesPow2Default.View.CopyFromCPU(stream, primesPow2Default); - DevicePrimesPow2LastOne.View.CopyFromCPU(stream, primesPow2LastOne); - DevicePrimesPow2LastSeven.View.CopyFromCPU(stream, primesPow2LastSeven); - DevicePrimesPow2LastThree.View.CopyFromCPU(stream, primesPow2LastThree); - DevicePrimesPow2LastNine.View.CopyFromCPU(stream, primesPow2LastNine); - - HeuristicPrimeSieves.EnsureInitialized(); - HeuristicCombinedPrimeTester.EnsureInitialized(); - - var heuristicGroupA = HeuristicPrimeSieves.GroupADivisors; - HeuristicGroupADivisors = CopySpanToDevice(accelerator, stream, heuristicGroupA); - - var heuristicGroupASquares = HeuristicPrimeSieves.GroupADivisorSquares; - HeuristicGroupADivisorSquares = CopySpanToDevice(accelerator, stream, heuristicGroupASquares); - - HeuristicGroupBDivisorsEnding1 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastOneWithoutLastThree); - HeuristicGroupBDivisorSquaresEnding1 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree); - - HeuristicGroupBDivisorsEnding7 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastSevenWithoutLastThree); - HeuristicGroupBDivisorSquaresEnding7 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree); - - HeuristicGroupBDivisorsEnding9 = CopyUintSpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesLastNineWithoutLastThree); - HeuristicGroupBDivisorSquaresEnding9 = CopySpanToDevice(accelerator, stream, DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree); - - var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1Span; - HeuristicCombinedDivisorsEnding1 = CopySpanToDevice(accelerator, stream, combinedEnding1); - var combinedSquaresEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1SquaresSpan; - HeuristicCombinedDivisorSquaresEnding1 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding1); - - var combinedEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3Span; - HeuristicCombinedDivisorsEnding3 = CopySpanToDevice(accelerator, stream, combinedEnding3); - var combinedSquaresEnding3 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding3SquaresSpan; - HeuristicCombinedDivisorSquaresEnding3 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding3); - - var combinedEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7Span; - HeuristicCombinedDivisorsEnding7 = CopySpanToDevice(accelerator, stream, combinedEnding7); - var combinedSquaresEnding7 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding7SquaresSpan; - HeuristicCombinedDivisorSquaresEnding7 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding7); - - var combinedEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9Span; - HeuristicCombinedDivisorsEnding9 = CopySpanToDevice(accelerator, stream, combinedEnding9); - var combinedSquaresEnding9 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding9SquaresSpan; - HeuristicCombinedDivisorSquaresEnding9 = CopySpanToDevice(accelerator, stream, combinedSquaresEnding9); - } - - internal MemoryBuffer1D DevicePrimesDefault { get; } - internal MemoryBuffer1D DevicePrimesLastOne { get; } - internal MemoryBuffer1D DevicePrimesLastSeven { get; } - internal MemoryBuffer1D DevicePrimesLastThree { get; } - internal MemoryBuffer1D DevicePrimesLastNine { get; } - internal MemoryBuffer1D DevicePrimesPow2Default { get; } - internal MemoryBuffer1D DevicePrimesPow2LastOne { get; } - internal MemoryBuffer1D DevicePrimesPow2LastSeven { get; } - internal MemoryBuffer1D DevicePrimesPow2LastThree { get; } - internal MemoryBuffer1D DevicePrimesPow2LastNine { get; } - internal MemoryBuffer1D HeuristicGroupADivisors { get; } - internal MemoryBuffer1D HeuristicGroupADivisorSquares { get; } - internal MemoryBuffer1D HeuristicGroupBDivisorsEnding1 { get; } - internal MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding1 { get; } - internal MemoryBuffer1D HeuristicGroupBDivisorsEnding7 { get; } - internal MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding7 { get; } - internal MemoryBuffer1D HeuristicGroupBDivisorsEnding9 { get; } - internal MemoryBuffer1D HeuristicGroupBDivisorSquaresEnding9 { get; } - internal MemoryBuffer1D HeuristicCombinedDivisorsEnding1 { get; } - internal MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding1 { get; } - internal MemoryBuffer1D HeuristicCombinedDivisorsEnding3 { get; } - internal MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding3 { get; } - internal MemoryBuffer1D HeuristicCombinedDivisorsEnding7 { get; } - internal MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding7 { get; } - internal MemoryBuffer1D HeuristicCombinedDivisorsEnding9 { get; } - internal MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9 { get; } - - internal HeuristicGpuDivisorTables CreateHeuristicDivisorTables() - { - return new HeuristicGpuDivisorTables( - HeuristicCombinedDivisorsEnding1.View, - HeuristicCombinedDivisorsEnding3.View, - HeuristicCombinedDivisorsEnding7.View, - HeuristicCombinedDivisorsEnding9.View, - HeuristicCombinedDivisorSquaresEnding1.View, - HeuristicCombinedDivisorSquaresEnding3.View, - HeuristicCombinedDivisorSquaresEnding7.View, - HeuristicCombinedDivisorSquaresEnding9.View, - HeuristicGroupADivisors.View, - HeuristicGroupADivisorSquares.View, - HeuristicGroupBDivisorsEnding1.View, - HeuristicGroupBDivisorSquaresEnding1.View, - HeuristicGroupBDivisorsEnding7.View, - HeuristicGroupBDivisorSquaresEnding7.View, - HeuristicGroupBDivisorsEnding9.View, - HeuristicGroupBDivisorSquaresEnding9.View); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AddReference() => Atomic.Add(ref _referenceCount, 1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void Release() => Atomic.Add(ref _referenceCount, -1); - - private static MemoryBuffer1D CopySpanToDevice(Accelerator accelerator, AcceleratorStream stream, ReadOnlySpan span) - { - MemoryBuffer1D? buffer; - // lock (accelerator) - { - buffer = accelerator.Allocate1D(span.Length); - } - - if (!span.IsEmpty) - { - ref ulong sourceRef = ref MemoryMarshal.GetReference(span); - buffer.View.CopyFromCPU(stream, ref sourceRef, span.Length); - } - - return buffer; - } - - private static MemoryBuffer1D CopyUintSpanToDevice(Accelerator accelerator, AcceleratorStream stream, ReadOnlySpan span) - { - MemoryBuffer1D? buffer; - // lock (accelerator) - { - buffer = accelerator.Allocate1D(span.Length); - } - - if (!span.IsEmpty) - { - var converted = new ulong[span.Length]; - for (int i = 0; i < span.Length; i++) - { - converted[i] = span[i]; - } - - buffer.View.CopyFromCPU(stream, converted); - } - - return buffer; - } - - internal void Dispose() - { - DevicePrimesDefault.Dispose(); - DevicePrimesLastOne.Dispose(); - DevicePrimesLastSeven.Dispose(); - DevicePrimesLastThree.Dispose(); - DevicePrimesLastNine.Dispose(); - DevicePrimesPow2Default.Dispose(); - DevicePrimesPow2LastOne.Dispose(); - DevicePrimesPow2LastSeven.Dispose(); - DevicePrimesPow2LastThree.Dispose(); - DevicePrimesPow2LastNine.Dispose(); - HeuristicGroupADivisors.Dispose(); - HeuristicGroupADivisorSquares.Dispose(); - HeuristicGroupBDivisorsEnding1.Dispose(); - HeuristicGroupBDivisorSquaresEnding1.Dispose(); - HeuristicGroupBDivisorsEnding7.Dispose(); - HeuristicGroupBDivisorSquaresEnding7.Dispose(); - HeuristicGroupBDivisorsEnding9.Dispose(); - HeuristicGroupBDivisorSquaresEnding9.Dispose(); - HeuristicCombinedDivisorsEnding1.Dispose(); - HeuristicCombinedDivisorSquaresEnding1.Dispose(); - HeuristicCombinedDivisorsEnding3.Dispose(); - HeuristicCombinedDivisorSquaresEnding3.Dispose(); - HeuristicCombinedDivisorsEnding7.Dispose(); - HeuristicCombinedDivisorSquaresEnding7.Dispose(); - HeuristicCombinedDivisorsEnding9.Dispose(); - HeuristicCombinedDivisorSquaresEnding9.Dispose(); - } - } - } - // Expose cache clearing for accelerator disposal coordination public static void ClearGpuCaches() { - PrimeTesterGpuContextPool.Clear(); + PrimeTesterByLastDigitAccelerator.Clear(); } internal static void DisposeGpuContexts() { - PrimeTesterGpuContextPool.DisposeAll(); + PrimeTesterByLastDigitAccelerator.DisposeAll(); lock (GpuWarmUpLock) { WarmedGpuLeaseCount = 0; @@ -715,7 +241,7 @@ 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 = PrimeTesterGpuContextPool.Rent(); + var gpu = PrimeTesterByLastDigitAccelerator.Rent(); var accelerator = gpu.Accelerator; var stream = gpu.Stream;