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);