From 69acd3dad970877449a853ef8e7f9467db5dc276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Fri, 24 Oct 2025 18:56:21 +0200 Subject: [PATCH] Cache GPU divisor partial data on GPU path --- .../Gpu/ExponentRemainderStepperGpu.cs | 6 +-- .../Gpu/GpuDivisorPartialData.cs | 38 +++++++++++++++++++ .../Gpu/Kernels/DivisorByDivisorKernels.cs | 6 +-- ...MersenneNumberDivisorByDivisorGpuTester.cs | 22 +++++------ PerfectNumbers.Core/MersenneDivisorCycles.cs | 16 ++++++++ 5 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 PerfectNumbers.Core/Gpu/GpuDivisorPartialData.cs diff --git a/PerfectNumbers.Core/Gpu/ExponentRemainderStepperGpu.cs b/PerfectNumbers.Core/Gpu/ExponentRemainderStepperGpu.cs index dbfa2c1..0d1dc90 100644 --- a/PerfectNumbers.Core/Gpu/ExponentRemainderStepperGpu.cs +++ b/PerfectNumbers.Core/Gpu/ExponentRemainderStepperGpu.cs @@ -15,17 +15,17 @@ internal struct ExponentRemainderStepperGpu private GpuUInt128 _currentResidue; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ExponentRemainderStepperGpu(in MontgomeryDivisorData divisor) + public ExponentRemainderStepperGpu(in GpuDivisorPartialData divisor) { ulong modulus = divisor.Modulus; _modulus = modulus; - _modulusWide = new ReadOnlyGpuUInt128(modulus); + _modulusWide = divisor.ModulusWide; PreviousExponent = 0UL; _currentResidue = new GpuUInt128(1UL); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MatchesDivisor(in MontgomeryDivisorData divisor) + public bool MatchesDivisor(in GpuDivisorPartialData divisor) { return _modulus == divisor.Modulus; } diff --git a/PerfectNumbers.Core/Gpu/GpuDivisorPartialData.cs b/PerfectNumbers.Core/Gpu/GpuDivisorPartialData.cs new file mode 100644 index 0000000..6087ad1 --- /dev/null +++ b/PerfectNumbers.Core/Gpu/GpuDivisorPartialData.cs @@ -0,0 +1,38 @@ +using System.Runtime.CompilerServices; +using PerfectNumbers.Core; + +namespace PerfectNumbers.Core.Gpu; + +internal readonly struct GpuDivisorPartialData +{ + public readonly ulong Modulus; + public readonly ReadOnlyGpuUInt128 ModulusWide; + private readonly MontgomeryDivisorData _montgomeryData; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private GpuDivisorPartialData(ulong modulus, ReadOnlyGpuUInt128 modulusWide, in MontgomeryDivisorData montgomeryData) + { + Modulus = modulus; + ModulusWide = modulusWide; + _montgomeryData = montgomeryData; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static GpuDivisorPartialData Create(ulong modulus) + { + MontgomeryDivisorData montgomery = MontgomeryDivisorData.FromModulus(modulus); + return new GpuDivisorPartialData(modulus, new ReadOnlyGpuUInt128(modulus), in montgomery); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static GpuDivisorPartialData FromMontgomery(in MontgomeryDivisorData divisor) + { + return new GpuDivisorPartialData(divisor.Modulus, new ReadOnlyGpuUInt128(divisor.Modulus), in divisor); + } + + public MontgomeryDivisorData MontgomeryData + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _montgomeryData; + } +} diff --git a/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs index 4fd04c9..0bd7573 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/DivisorByDivisorKernels.cs @@ -30,7 +30,7 @@ internal static class DivisorByDivisorKernels public static void EvaluateDivisorWithStepperKernel( Index1D _, - MontgomeryDivisorData divisor, + GpuDivisorPartialData divisor, ulong divisorCycle, ulong firstCycleRemainder, ArrayView exponents, @@ -101,12 +101,12 @@ internal static class DivisorByDivisorKernels hits[globalIndex] = montgomeryResult == 1UL ? (byte)1 : (byte)0; } - public static void ComputeMontgomeryExponentKernel(Index1D index, MontgomeryDivisorData divisor, ArrayView exponents, ArrayView results) + public static void ComputeMontgomeryExponentKernel(Index1D index, GpuDivisorPartialData divisor, ArrayView exponents, ArrayView results) { ulong modulus = divisor.Modulus; // Each divisor flowing through this kernel follows q = 2kp + 1 with k >= 1, so modulus is always odd and exceeds one. ulong exponent = exponents[index]; - ulong montgomeryResult = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor, exponent); + ulong montgomeryResult = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor.MontgomeryData, exponent); results[index] = montgomeryResult; } } diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index a0490be..83d270b 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -11,7 +11,7 @@ using ILGPU.Runtime; namespace PerfectNumbers.Core.Gpu; /// -/// Scans Mersenne divisors on the GPU for prime exponents p >= 31 using per-divisor Montgomery data. +/// 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 @@ -24,7 +24,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv // private bool _isConfigured; private readonly ConcurrentDictionary, ArrayView, ArrayView, ArrayView>> _kernelCache = new(); - private readonly ConcurrentDictionary, ArrayView>> _stepperKernelCache = new(AcceleratorReferenceComparer.Instance); + private readonly ConcurrentDictionary, ArrayView>> _stepperKernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary> _resourcePools = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentBag _acceleratorPool = new(); private readonly ConcurrentBag _sessionPool = new(); @@ -32,8 +32,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private Action, ArrayView, ArrayView, ArrayView> GetKernel(Accelerator accelerator) => _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView, ArrayView, ArrayView>(DivisorByDivisorKernels.CheckKernel)); - private Action, ArrayView> GetStepperKernel(Accelerator accelerator) => - _stepperKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView>(DivisorByDivisorKernels.EvaluateDivisorWithStepperKernel)); + private Action, ArrayView> GetStepperKernel(Accelerator accelerator) => + _stepperKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel, ArrayView>(DivisorByDivisorKernels.EvaluateDivisorWithStepperKernel)); public int GpuBatchSize @@ -258,7 +258,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv for (int i = 0; i < filteredCount; i++) { ulong divisorValue = filteredDivisorsSpan[i]; - MontgomeryDivisorData divisorDataValue = MontgomeryDivisorData.FromModulus(divisorValue); + GpuDivisorPartialData divisorDataValue = GpuDivisorPartialData.Create(divisorValue); ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, divisorDataValue); if (divisorCycle != prime) { @@ -305,11 +305,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong ResolveDivisorCycle(ulong divisor, ulong prime, in MontgomeryDivisorData divisorData) + private static ulong ResolveDivisorCycle(ulong divisor, ulong prime, in GpuDivisorPartialData divisorData) { if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentGpu(divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) { - return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); + return MersenneDivisorCycles.CalculateCycleLengthGpu(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); } return computedCycle; @@ -384,7 +384,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv private readonly MersenneNumberDivisorByDivisorGpuTester _owner; private readonly GpuContextPool.GpuContextLease _lease; private readonly Accelerator _accelerator; - private Action, ArrayView> _stepperKernel = null!; + private Action, ArrayView> _stepperKernel = null!; private MemoryBuffer1D _exponentsBuffer = null!; private MemoryBuffer1D _hitBuffer = null!; private ulong[] _hostBuffer = null!; @@ -459,10 +459,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv // return; // } - MontgomeryDivisorData cachedData = divisorData; + GpuDivisorPartialData cachedData = GpuDivisorPartialData.FromMontgomery(divisorData); if (cachedData.Modulus != divisor) { - cachedData = MontgomeryDivisorData.FromModulus(divisor); + cachedData = GpuDivisorPartialData.Create(divisor); } ulong cycle = divisorCycle; @@ -472,7 +472,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv { if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentGpu(divisor, firstPrime, cachedData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) { - cycle = MersenneDivisorCycles.CalculateCycleLength(divisor, cachedData, skipPrimeOrderHeuristic: primeOrderFailed); + cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(divisor, cachedData, skipPrimeOrderHeuristic: primeOrderFailed); } else { diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCycles.cs index 7395e85..ad8abaf 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCycles.cs @@ -408,6 +408,22 @@ public class MersenneDivisorCycles return success; } + internal static bool TryCalculateCycleLengthForExponentGpu( + ulong divisor, + ulong exponent, + in GpuDivisorPartialData divisorData, + out ulong cycleLength, + out bool primeOrderFailed) => + TryCalculateCycleLengthForExponentGpu(divisor, exponent, divisorData.MontgomeryData, out cycleLength, out primeOrderFailed); + + internal static ulong CalculateCycleLengthGpu( + ulong divisor, + in GpuDivisorPartialData divisorData, + bool skipPrimeOrderHeuristic = false) + { + return CalculateCycleLength(divisor, divisorData.MontgomeryData, skipPrimeOrderHeuristic); + } + private static bool AccumulateFactors( ulong value, Dictionary counts)