From 6d1ff1372e016c46a5ba0666093f2d12d4556ae1 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sun, 23 Nov 2025 00:07:03 +0100 Subject: [PATCH] Factoring switched to PrimeTester.IsPrime for the best performance. PrimeOrderCalculator.PartialFactor switched to GPU factorization. Every so often it will send work to GPU instead of CPU. BatchedResources renamed to MersenneNumberDivisorByDivisorAccelerator for consistency and moved to its own file. It now uses array instead of dictionary for lookup free performance. --- ...rsenneNumberDivisorByDivisorAccelerator.cs | 119 +++++++++ .../PrimeOrderCalculatorAccelerator.cs | 16 +- ...MersenneNumberDivisorByDivisorGpuTester.cs | 229 ++---------------- PerfectNumbers.Core/MersenneDivisorCycles.cs | 8 +- PerfectNumbers.Core/PerfectNumberConstants.cs | 4 +- .../PrimeOrderCalculator.Cpu.cs | 57 ++++- .../PrimeOrderCalculator.Gpu.cs | 9 +- 7 files changed, 202 insertions(+), 240 deletions(-) create mode 100644 PerfectNumbers.Core/Gpu/Accelerators/MersenneNumberDivisorByDivisorAccelerator.cs diff --git a/PerfectNumbers.Core/Gpu/Accelerators/MersenneNumberDivisorByDivisorAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/MersenneNumberDivisorByDivisorAccelerator.cs new file mode 100644 index 0000000..b76772a --- /dev/null +++ b/PerfectNumbers.Core/Gpu/Accelerators/MersenneNumberDivisorByDivisorAccelerator.cs @@ -0,0 +1,119 @@ +using System.Buffers; +using ILGPU; +using ILGPU.Runtime; + +namespace PerfectNumbers.Core.Gpu; + +public sealed class MersenneNumberDivisorByDivisorAccelerator +{ + internal MersenneNumberDivisorByDivisorAccelerator(Accelerator accelerator, int capacity) + { + ArrayPool ulongPool = ThreadStaticPools.UlongPool; + ArrayPool intPool = ThreadStaticPools.IntPool; + ArrayPool partialPool = ThreadStaticPools.GpuDivisorPartialDataPool; + + Divisors = ulongPool.Rent(capacity); + Exponents = ulongPool.Rent(capacity); + DivisorData = partialPool.Rent(capacity); + Offsets = intPool.Rent(capacity); + Counts = intPool.Rent(capacity); + Cycles = ulongPool.Rent(capacity); + + lock (accelerator) + { + DivisorDataBuffer = accelerator.Allocate1D(capacity); + OffsetBuffer = accelerator.Allocate1D(capacity); + CountBuffer = accelerator.Allocate1D(capacity); + CycleBuffer = accelerator.Allocate1D(capacity); + ExponentBuffer = accelerator.Allocate1D(capacity); + HitsBuffer = accelerator.Allocate1D(capacity); + HitIndexBuffer = accelerator.Allocate1D(1); + + CheckDivisorKernel = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(DivisorByDivisorKernels.CheckKernel)); + } + } + + internal MemoryBuffer1D DivisorDataBuffer; + internal MemoryBuffer1D OffsetBuffer; + internal MemoryBuffer1D CountBuffer; + internal MemoryBuffer1D CycleBuffer; + internal MemoryBuffer1D ExponentBuffer; + internal MemoryBuffer1D HitsBuffer; + internal MemoryBuffer1D HitIndexBuffer; + + internal ulong[] Divisors; + internal ulong[] Exponents; + internal GpuDivisorPartialData[] DivisorData; + internal int[] Offsets; + internal int[] Counts; + internal ulong[] Cycles; + internal readonly Kernel CheckDivisorKernel; + + public void EnsureCapacity(Accelerator accelerator, int requiredCapacity) + { + if (DivisorDataBuffer.Length < requiredCapacity) + { + ReallocateBuffers(accelerator, requiredCapacity); + } + } + + private void ReallocateBuffers(Accelerator accelerator, int capacity) + { + DivisorDataBuffer.Dispose(); + OffsetBuffer.Dispose(); + CountBuffer.Dispose(); + CycleBuffer.Dispose(); + ExponentBuffer.Dispose(); + HitsBuffer.Dispose(); + HitIndexBuffer.Dispose(); + + lock (accelerator) + { + DivisorDataBuffer = accelerator.Allocate1D(capacity); + OffsetBuffer = accelerator.Allocate1D(capacity); + CountBuffer = accelerator.Allocate1D(capacity); + CycleBuffer = accelerator.Allocate1D(capacity); + ExponentBuffer = accelerator.Allocate1D(capacity); + HitsBuffer = accelerator.Allocate1D(capacity); + HitIndexBuffer = accelerator.Allocate1D(1); + } + + ArrayPool ulongPool = ThreadStaticPools.UlongPool; + ArrayPool intPool = ThreadStaticPools.IntPool; + ArrayPool partialPool = ThreadStaticPools.GpuDivisorPartialDataPool; + ulongPool.Return(Divisors, clearArray: false); + ulongPool.Return(Exponents, clearArray: false); + partialPool.Return(DivisorData, clearArray: false); + intPool.Return(Offsets, clearArray: false); + intPool.Return(Counts, clearArray: false); + ulongPool.Return(Cycles, clearArray: false); + + Divisors = ulongPool.Rent(capacity); + Exponents = ulongPool.Rent(capacity); + DivisorData = partialPool.Rent(capacity); + Offsets = intPool.Rent(capacity); + Counts = intPool.Rent(capacity); + Cycles = ulongPool.Rent(capacity); + } + + public void Dispose() + { + DivisorDataBuffer.Dispose(); + OffsetBuffer.Dispose(); + CountBuffer.Dispose(); + CycleBuffer.Dispose(); + ExponentBuffer.Dispose(); + HitsBuffer.Dispose(); + HitIndexBuffer.Dispose(); + + ArrayPool ulongPool = ThreadStaticPools.UlongPool; + ArrayPool intPool = ThreadStaticPools.IntPool; + ArrayPool partialPool = ThreadStaticPools.GpuDivisorPartialDataPool; + ulongPool.Return(Divisors, clearArray: false); + ulongPool.Return(Exponents, clearArray: false); + partialPool.Return(DivisorData, clearArray: false); + intPool.Return(Offsets, clearArray: false); + intPool.Return(Counts, clearArray: false); + ulongPool.Return(Cycles, clearArray: false); + } +} diff --git a/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs index dcea11a..bdd6369 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs @@ -69,7 +69,7 @@ public sealed class PrimeOrderCalculatorAccelerator AcceleratorStream stream = accelerator.CreateStream(); // TODO: Review which tables are needed when final execution path is defined. LastDigitGpuTables.WarmUp(i, stream); - HeuristicCombinedGpuTables.WarmUp(i, stream); + // HeuristicCombinedGpuTables.WarmUp(i, stream); // SmallPrimeFactorGpuTables.WarmUp(i, stream); // SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream); // _ = GpuKernelPool.GetOrAddKernels(accelerator, stream, KernelType.None); @@ -115,13 +115,13 @@ public sealed class PrimeOrderCalculatorAccelerator public Dictionary Pow2ModEntriesToTestOnHost = []; - public readonly Action, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D> CheckFactorsKernelLauncher; + public readonly Kernel CheckFactorsKernel; public readonly Action, MontgomeryDivisorData, ArrayView1D> ConvertToStandardKernelLauncher; public readonly Action, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher; public readonly Action, MontgomeryDivisorData, ArrayView1D> KeepMontgomeryKernelLauncher; - public readonly Action, ArrayView1D, ArrayView1D> PollardRhoKernelLauncher; + public readonly Kernel PollardRhoKernel; public readonly Action, GpuUInt128, ArrayView1D> Pow2ModWideKernelLauncher; public readonly Action, ArrayView> SharesFactorKernelLauncher; public readonly Action, ArrayView1D, int, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D> SmallPrimeFactorKernelLauncher; @@ -247,9 +247,9 @@ public sealed class PrimeOrderCalculatorAccelerator DevicePrimesPow2LastThree = lastDigitSharedTables.DevicePrimesPow2LastThree.View; DevicePrimesPow2LastNine = lastDigitSharedTables.DevicePrimesPow2LastNine.View; - DivisorTables = HeuristicCombinedGpuTables - .GetStaticTables(acceleratorIndex) - .CreateViews(); + // DivisorTables = HeuristicCombinedGpuTables + // .GetStaticTables(acceleratorIndex) + // .CreateViews(); var smallPrimeFactorTables = SmallPrimeFactorGpuTables.GetStaticTables(acceleratorIndex); @@ -260,7 +260,7 @@ public sealed class PrimeOrderCalculatorAccelerator SmallPrimeFactorSquares = tables.Squares.View; } - CheckFactorsKernelLauncher = KernelUtil.GetKernel(accelerator.LoadStreamKernel, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D>(PrimeOrderGpuHeuristics.CheckFactorsKernel)).CreateLauncherDelegate, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D>>(); + CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D>(PrimeOrderGpuHeuristics.CheckFactorsKernel)); ConvertToStandardKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, MontgomeryDivisorData, ArrayView1D>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard)).CreateLauncherDelegate, MontgomeryDivisorData, ArrayView1D>>(); @@ -268,7 +268,7 @@ public sealed class PrimeOrderCalculatorAccelerator KeepMontgomeryKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, MontgomeryDivisorData, ArrayView1D>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery)).CreateLauncherDelegate, MontgomeryDivisorData, ArrayView1D>>(); - PollardRhoKernelLauncher = KernelUtil.GetKernel(accelerator.LoadStreamKernel, ArrayView1D, ArrayView1D>(Pow2MontgomeryKernels.TryPollardRhoKernel)).CreateLauncherDelegate, ArrayView1D, ArrayView1D>>(); + PollardRhoKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel, ArrayView1D, ArrayView1D>(Pow2MontgomeryKernels.TryPollardRhoKernel)); Pow2ModWideKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel, GpuUInt128, ArrayView1D>(PrimeOrderGpuHeuristics.Pow2ModKernelWide)).CreateLauncherDelegate, GpuUInt128, ArrayView1D>>(); diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index 4e49e3c..a939a8c 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -1,4 +1,3 @@ -using System.Buffers; using System.Collections.Concurrent; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -12,7 +11,7 @@ namespace PerfectNumbers.Core.Gpu; /// Scans Mersenne divisors on the GPU for prime exponents p >= 31 using cached GPU divisor partial data. /// Consumers must call before invoking other members so the divisor limits are populated. /// -public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDivisorByDivisorTester +public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDivisorByDivisorTester { private int _gpuBatchSize = GpuConstants.ScanBatchSize; // EvenPerfectBitScanner configures the GPU tester once before scanning and never mutates the configuration afterwards, @@ -21,27 +20,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private ulong _divisorLimit; // private bool _isConfigured; - [ThreadStatic] - private static Dictionary, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>? _kernel; + private readonly ConcurrentBag[] _resourcePool = [..AcceleratorPool.Shared.Accelerators.Select(x => new ConcurrentBag())]; - private readonly ConcurrentBag _resourcePool = []; private readonly ConcurrentBag _sessionPool = []; - private static Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> GetKernel(Accelerator accelerator) - { - var pool = _kernel ??= []; - if (pool.TryGetValue(accelerator, out var cached)) - { - return cached; - } - - var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(DivisorByDivisorKernels.CheckKernel); - var kernel = KernelUtil.GetKernel(loaded); - cached = kernel.CreateLauncherDelegate, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>(); - pool[accelerator] = cached; - return cached; - } - public int GpuBatchSize { get => _gpuBatchSize; @@ -107,20 +89,16 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv // var accelerator = _accelerators[acceleratorIndex]; var gpu = PrimeOrderCalculatorAccelerator.Rent(batchCapacity); var acceleratorIndex = gpu.AcceleratorIndex; - var accelerator = gpu.Accelerator; - var stream = AcceleratorStreamPool.Rent(acceleratorIndex); // Monitor.Enter(gpuLease.ExecutionLock); - var kernel = GetKernel(accelerator); - var resources = RentBatchResources(accelerator, batchCapacity); + var resources = RentBatchResources(acceleratorIndex, batchCapacity); composite = CheckDivisors( gpu, prime, allowedMax, - kernel, - stream, + resources.CheckDivisorKernel, resources.DivisorDataBuffer, resources.OffsetBuffer, resources.CountBuffer, @@ -138,8 +116,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv out coveredRange, out processedCount); - AcceleratorStreamPool.Return(acceleratorIndex, stream); - ReturnBatchResources(resources); + ReturnBatchResources(acceleratorIndex, resources); PrimeOrderCalculatorAccelerator.Return(gpu); // Monitor.Exit(gpuLease.ExecutionLock); // GpuPrimeWorkLimiter.Release(); @@ -189,8 +166,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong allowedMax, - Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> kernel, - AcceleratorStream stream, + Kernel kernel, MemoryBuffer1D divisorDataBuffer, MemoryBuffer1D offsetBuffer, MemoryBuffer1D countBuffer, @@ -213,6 +189,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv bool processedAll; processedCount = 0UL; lastProcessed = 0UL; + var acceleratorIndex = gpu.AcceleratorIndex; // EvenPerfectBitScanner always configures non-empty GPU divisor buffers, so the defensive guard stays // commented out. Tests and benchmarks that rely on zero-capacity buffers must be updated instead of re-enabling @@ -352,6 +329,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv continue; } + var stream = AcceleratorStreamPool.Rent(acceleratorIndex); divisorDataView.CopyFromCPU(stream, ref divisorDataRef, admissibleCount); offsetView.CopyFromCPU(stream, ref offsetRef, admissibleCount); countView.CopyFromCPU(stream, ref countRef, admissibleCount); @@ -361,11 +339,12 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv int sentinel = int.MaxValue; hitIndexView.CopyFromCPU(stream, ref sentinel, 1); - kernel(stream, new Index1D(admissibleCount), divisorDataView, offsetView, countView, exponentViewDevice, cycleView, hitsView, hitIndexView); + kernel.Launch(stream, 1, new Index1D(admissibleCount), divisorDataView, offsetView, countView, exponentViewDevice, cycleView, hitsView, hitIndexView); int firstHit = sentinel; hitIndexView.CopyToCPU(stream, ref firstHit, 1); stream.Synchronize(); + AcceleratorStreamPool.Return(acceleratorIndex, stream); int hitIndex = firstHit >= admissibleCount ? -1 : firstHit; bool hitFound = hitIndex >= 0; composite = hitFound; @@ -537,7 +516,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv if (_kernel == null) { - _kernel = GetKernel(_accelerator); + _kernel = KernelUtil.GetKernel(_accelerator.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>(DivisorByDivisorKernels.CheckKernel)).CreateLauncherDelegate, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView>>(); } if (_divisorBuffer == null) @@ -694,192 +673,20 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv - private BatchResources RentBatchResources(Accelerator accelerator, int capacity) + private MersenneNumberDivisorByDivisorAccelerator RentBatchResources(int acceleratorIndex, int capacity) { - if (!_resourcePool.TryTake(out BatchResources? resources)) + var queue = _resourcePool[acceleratorIndex]; + var accelerator = AcceleratorPool.Shared.Accelerators[acceleratorIndex]; + if (!queue.TryTake(out MersenneNumberDivisorByDivisorAccelerator? resources)) { - resources = new BatchResources(Math.Max(1, capacity)); + return new MersenneNumberDivisorByDivisorAccelerator(accelerator, capacity); } - resources.Bind(accelerator, capacity); + resources.EnsureCapacity(accelerator, capacity); return resources; } - private void ReturnBatchResources(BatchResources resources) => _resourcePool.Add(resources); - - private sealed class BatchResources - { - private Accelerator? _accelerator; - private MemoryBuffer1D? _divisorDataBuffer; - private MemoryBuffer1D? _offsetBuffer; - private MemoryBuffer1D? _countBuffer; - private MemoryBuffer1D? _cycleBuffer; - private MemoryBuffer1D? _exponentBuffer; - private MemoryBuffer1D? _hitsBuffer; - private MemoryBuffer1D? _hitIndexBuffer; - private int _deviceCapacity; - private int _hostCapacity; - - internal BatchResources(int capacity) - { - int actualCapacity = Math.Max(1, capacity); - RentHostArrays(actualCapacity); - } - - internal MemoryBuffer1D DivisorDataBuffer => _divisorDataBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator."); - - internal MemoryBuffer1D OffsetBuffer => _offsetBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator."); - - internal MemoryBuffer1D CountBuffer => _countBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator."); - - internal MemoryBuffer1D CycleBuffer => _cycleBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator."); - - internal MemoryBuffer1D ExponentBuffer => _exponentBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator."); - - internal MemoryBuffer1D HitsBuffer => _hitsBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator."); - - internal MemoryBuffer1D HitIndexBuffer => _hitIndexBuffer ?? throw new InvalidOperationException("Batch resources not bound to an accelerator."); - - internal ulong[] Divisors { get; private set; } = null!; - - internal ulong[] Exponents { get; private set; } = null!; - - internal GpuDivisorPartialData[] DivisorData { get; private set; } = null!; - - internal int[] Offsets { get; private set; } = null!; - - internal int[] Counts { get; private set; } = null!; - - internal ulong[] Cycles { get; private set; } = null!; - - - internal int Capacity => _hostCapacity; - - internal void Bind(Accelerator accelerator, int requiredCapacity) - { - EnsureHostCapacity(requiredCapacity); - - if (!ReferenceEquals(_accelerator, accelerator)) - { - ReleaseDeviceBuffers(); - _accelerator = accelerator; - } - - EnsureDeviceCapacity(accelerator, requiredCapacity); - } - - private void EnsureHostCapacity(int requiredCapacity) - { - int desiredCapacity = Math.Max(1, requiredCapacity); - if (_hostCapacity >= desiredCapacity) - { - return; - } - - ReturnHostArrays(); - RentHostArrays(desiredCapacity); - } - - private void EnsureDeviceCapacity(Accelerator accelerator, int requiredCapacity) - { - int desiredCapacity = Math.Max(1, requiredCapacity); - if (_divisorDataBuffer is null) - { - AllocateDeviceBuffers(accelerator, desiredCapacity); - return; - } - - if (_deviceCapacity >= desiredCapacity) - { - return; - } - - ReleaseDeviceBuffers(); - AllocateDeviceBuffers(accelerator, desiredCapacity); - } - - private void AllocateDeviceBuffers(Accelerator accelerator, int capacity) - { - lock (accelerator) - { - _divisorDataBuffer = accelerator.Allocate1D(capacity); - _offsetBuffer = accelerator.Allocate1D(capacity); - _countBuffer = accelerator.Allocate1D(capacity); - _cycleBuffer = accelerator.Allocate1D(capacity); - _exponentBuffer = accelerator.Allocate1D(capacity); - _hitsBuffer = accelerator.Allocate1D(capacity); - _hitIndexBuffer = accelerator.Allocate1D(1); - } - - _deviceCapacity = capacity; - } - - private void RentHostArrays(int capacity) - { - ArrayPool ulongPool = ThreadStaticPools.UlongPool; - ArrayPool intPool = ThreadStaticPools.IntPool; - ArrayPool partialPool = ThreadStaticPools.GpuDivisorPartialDataPool; - - Divisors = ulongPool.Rent(capacity); - Exponents = ulongPool.Rent(capacity); - DivisorData = partialPool.Rent(capacity); - Offsets = intPool.Rent(capacity); - Counts = intPool.Rent(capacity); - Cycles = ulongPool.Rent(capacity); - _hostCapacity = capacity; - } - - private void ReleaseDeviceBuffers() - { - _divisorDataBuffer?.Dispose(); - _offsetBuffer?.Dispose(); - _countBuffer?.Dispose(); - _cycleBuffer?.Dispose(); - _exponentBuffer?.Dispose(); - _hitsBuffer?.Dispose(); - _hitIndexBuffer?.Dispose(); - _divisorDataBuffer = null; - _offsetBuffer = null; - _countBuffer = null; - _cycleBuffer = null; - _exponentBuffer = null; - _hitsBuffer = null; - _hitIndexBuffer = null; - _deviceCapacity = 0; - } - - private void ReturnHostArrays() - { - if (_hostCapacity == 0) - { - return; - } - - ArrayPool ulongPool = ThreadStaticPools.UlongPool; - ArrayPool intPool = ThreadStaticPools.IntPool; - ArrayPool partialPool = ThreadStaticPools.GpuDivisorPartialDataPool; - ulongPool.Return(Divisors, clearArray: false); - ulongPool.Return(Exponents, clearArray: false); - partialPool.Return(DivisorData, clearArray: false); - intPool.Return(Offsets, clearArray: false); - intPool.Return(Counts, clearArray: false); - ulongPool.Return(Cycles, clearArray: false); - Divisors = Array.Empty(); - Exponents = Array.Empty(); - DivisorData = Array.Empty(); - Offsets = Array.Empty(); - Counts = Array.Empty(); - Cycles = Array.Empty(); - _hostCapacity = 0; - } - - public void Dispose() - { - ReleaseDeviceBuffers(); - _accelerator = null; - ReturnHostArrays(); - } - } + private void ReturnBatchResources(int acceleratorIndex, MersenneNumberDivisorByDivisorAccelerator resources) => _resourcePool[acceleratorIndex].Add(resources); private sealed class AcceleratorReferenceComparer : IEqualityComparer { diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index d27a0f5..27fc63d 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -412,8 +412,8 @@ public class MersenneDivisorCycles // Console.WriteLine($"MersenneDivisorCycles.TryFactorIntoCountsInternal hits {Volatile.Read(ref _tryFactorIntoCountsInternalHits)}"); // HeuristicPrimeTester primeTester = _primeTester ??= new(); - if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining)) - // if (PrimeTester.IsPrimeCpu(remaining, CancellationToken.None)) + // if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, remaining)) + if (PrimeTester.IsPrime(remaining)) { AddFactor(counts, remaining); return true; @@ -958,8 +958,8 @@ public class MersenneDivisorCycles // HeuristicPrimeTester primeTester = _primeTester ??= new(); // if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.Exclusive.IsPrimeCpu(divisor, CancellationToken.None)) - // if (!skipPrimeOrderHeuristic && primeTester.IsPrimeGpu(divisor)) - if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor)) + if (!skipPrimeOrderHeuristic && PrimeTester.IsPrime(divisor)) + // if (!skipPrimeOrderHeuristic && HeuristicCombinedPrimeTester.IsPrimeGpu(divisor)) { ulong computedOrder = PrimeOrderCalculator.Calculate( divisor, diff --git a/PerfectNumbers.Core/PerfectNumberConstants.cs b/PerfectNumbers.Core/PerfectNumberConstants.cs index 945e7c5..dceda8a 100644 --- a/PerfectNumbers.Core/PerfectNumberConstants.cs +++ b/PerfectNumbers.Core/PerfectNumberConstants.cs @@ -8,12 +8,12 @@ public static class PerfectNumberConstants public const int DefaultSmallPrimeFactorSlotCount = 64; public const int DefaultSpecialMaxFactorCapacity = 1024; public const ulong ExtraDivisorCycleSearchLimit = 64UL; - public const int GpuFrequency = 768 - 1; + public const int GpuFrequency = 16 - 1; public const int MaxQForDivisorCycles = 4_000_000; public static readonly uint PrimesLimit = 1_000_000; //(ulong)Array.MaxLength;// 1_000_000; public const int MaxOddPowersCount = 128; public const int PooledArrayThreshold = 64; - public static readonly int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup; + public static readonly int RollingAccelerators = 298; //SharedGpuContext.Device.MaxNumThreadsPerGroup; public const int ThreadsByAccelerator = 1; // TODO: Load these limits from the benchmark-driven configuration so CPU and GPU scans stay aligned with the optimal // divisor-cycle datasets we generate offline. diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 777238a..a7cc4d4 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -66,9 +66,11 @@ internal static partial class PrimeOrderCalculator ulong result; if (phiFactors.Factors is null) { - result = /*prime <= PerfectNumberConstants.MaxQForDivisorCycles - ? CalculateByFactorizationGpu(gpu, prime, divisorData) - : */CalculateByFactorizationCpu(gpu, prime, divisorData); + // result = prime <= PerfectNumberConstants.MaxQForDivisorCycles + // ? CalculateByFactorizationGpu(gpu, prime, divisorData) + // : CalculateByFactorizationCpu(gpu, prime, divisorData); + result = CalculateByFactorizationGpu(gpu, prime, divisorData); + // result = CalculateByFactorizationCpu(gpu, prime, divisorData); phiFactors.Dispose(); PrimeOrderCalculatorAccelerator.Return(gpu); @@ -109,8 +111,8 @@ internal static partial class PrimeOrderCalculator { orderFactors?.Dispose(); // GpuPrimeWorkLimiter.Release(); - return CalculateByFactorizationCpu(gpu, prime, divisorData); - // return CalculateByFactorizationGpu(gpu, prime, divisorData); + // return CalculateByFactorizationCpu(gpu, prime, divisorData); + return CalculateByFactorizationGpu(gpu, prime, divisorData); } if (TryHeuristicFinishCpu(gpu, prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order)) @@ -138,7 +140,7 @@ internal static partial class PrimeOrderCalculator Span stackBuffer = stackalloc ulong[length]; - if (length <= 9) + if (length <= 8) { return EvaluateSpecialMaxCandidates(stackBuffer, factorSpan, phi, prime, divisorData); } @@ -1174,9 +1176,10 @@ internal static partial class PrimeOrderCalculator // bool isPrime = composite > PerfectNumberConstants.MaxQForDivisorCycles // ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite) // : PrimeTester.IsPrime(composite); + // bool isPrime = PrimeTester.IsPrime(composite); + // bool isPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(composite); // bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite); bool runOnGpu = RunOnGpu(); - bool isPrime = !runOnGpu ? PrimeTester.IsPrime(composite) : HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite); // bool isPrime = PrimeTester.IsPrime(composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); @@ -1243,13 +1246,18 @@ internal static partial class PrimeOrderCalculator if (!entry.HasKnownPrimality) { - // bool isPrime = primeTester.HeuristicIsPrimeGpu(composite); + // bool isPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(composite); + // bool isPrime = PrimeTester.IsPrime(composite); + // bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(composite); // bool isPrime = PrimeTester.IsPrimeInternal(composite, CancellationToken.None); // Atomic.Add(ref _partialFactorPendingHits, 1UL); // Console.WriteLine($"Partial factor pending hits {Volatile.Read(ref _partialFactorPendingHits)}"); - bool isPrime = PrimeTester.IsPrime(composite); + bool isPrime = RunOnGpu() + ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite) + : PrimeTester.IsPrime(composite); + // bool isPrime = PrimeTester.IsPrime(composite); // bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, composite); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(composite); @@ -1281,7 +1289,9 @@ internal static partial class PrimeOrderCalculator } else { - // cofactorIsPrime = primeTester.HeuristicIsPrimeGpu(cofactor); + // cofactorIsPrime = PrimeTester.IsPrime(cofactor); + // cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeCpu(cofactor); + // cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(cofactor); // cofactorIsPrime = PrimeTester.IsPrimeInternal(cofactor, CancellationToken.None); // Atomic.Add(ref _partialFactorCofactorHits, 1UL); @@ -1289,7 +1299,10 @@ internal static partial class PrimeOrderCalculator // cofactorIsPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor); // cofactorIsPrime = PrimeTester.IsPrimeGpu(gpu, cofactor); - cofactorIsPrime = PrimeTester.IsPrime(cofactor); + cofactorIsPrime = RunOnGpu() + ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, cofactor) + : PrimeTester.IsPrime(cofactor); + // cofactorIsPrime = PrimeTester.IsPrime(cofactor); // cofactorIsPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(cofactor); } @@ -1375,7 +1388,23 @@ internal static partial class PrimeOrderCalculator private static bool RunOnGpu() { + // return false; // return true; + // int cpuCount; + + // lock(_sync) + // { + // cpuCount = _cpuCount; + // cpuCount++; + // if (cpuCount == PerfectNumberConstants.GpuFrequency) + // { + // cpuCount = 0; + // } + + // _cpuCount = cpuCount; + // } + + // return cpuCount == 0; int cpuCount = Atomic.Add(ref _cpuCount, 1); // return (cpuCount % GpuFrequency) == 0; if (cpuCount == PerfectNumberConstants.GpuFrequency) @@ -1800,6 +1829,9 @@ internal static partial class PrimeOrderCalculator // bool isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value); // bool isPrime = PrimeTester.IsPrimeGpu(value); bool isPrime = PrimeTester.IsPrime(value); + // bool isPrime = RunOnGpu() + // ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, value) + // : PrimeTester.IsPrime(value); // bool isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(value); if (!knownComposite && isPrime) { @@ -1816,6 +1848,9 @@ internal static partial class PrimeOrderCalculator // isPrime = primeTester.HeuristicIsPrimeGpu(factor); // isPrime = HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, factor); // isPrime = PrimeTester.IsPrimeGpu(factor); + // isPrime = RunOnGpu() + // ? HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, factor) + // : PrimeTester.IsPrime(factor); isPrime = PrimeTester.IsPrime(factor); // isPrime = Open.Numeric.Primes.Prime.Numbers.IsPrime(factor); if (isPrime) diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index aae5691..3796425 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -42,8 +42,9 @@ internal static partial class PrimeOrderCalculator AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); gpu.Pow2ModEntriesToTestOnDevice.View.CopyFromCPU(stream, entries); - var kernelLauncher = gpu.CheckFactorsKernelLauncher; - kernelLauncher(stream, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.OutputUlong.View); + // var kernelLauncher = gpu.CheckFactorsKernelLauncher; + var kernel = gpu.CheckFactorsKernel; + kernel.Launch(stream, 1, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.OutputUlong.View); gpu.OutputUlong.View.CopyToCPU(stream, ref order, 1); stream.Synchronize(); @@ -170,9 +171,9 @@ internal static partial class PrimeOrderCalculator AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); gpu.Input.View.CopyFromCPU(stream, randomStateSpan); - var kernelLauncher = gpu.PollardRhoKernelLauncher; + var kernel = gpu.PollardRhoKernel; - kernelLauncher(stream, n, 1, gpu.Input.View, gpu.OutputByte.View, gpu.OutputUlong.View); + kernel.Launch(stream, 1, n, 1, gpu.Input.View, gpu.OutputByte.View, gpu.OutputUlong.View); gpu.OutputByte.View.CopyToCPU(stream, factoredSpan); gpu.OutputUlong.View.CopyToCPU(stream, factorSpan);