Cache GPU divisor partial data on GPU path

This commit is contained in:
Marek Lemańczyk committed 2025-10-24 18:56:21 +02:00
1 parent cc281ab423
commit 69acd3dad9
5 files changed
+71 -17

No files matched your search

@@ -15,17 +15,17 @@ internal struct ExponentRemainderStepperGpu
private GpuUInt128 _currentResidue; private GpuUInt128 _currentResidue;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ExponentRemainderStepperGpu(in MontgomeryDivisorData divisor) public ExponentRemainderStepperGpu(in GpuDivisorPartialData divisor)
{ {
ulong modulus = divisor.Modulus; ulong modulus = divisor.Modulus;
_modulus = modulus; _modulus = modulus;
_modulusWide = new ReadOnlyGpuUInt128(modulus); _modulusWide = divisor.ModulusWide;
PreviousExponent = 0UL; PreviousExponent = 0UL;
_currentResidue = new GpuUInt128(1UL); _currentResidue = new GpuUInt128(1UL);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MatchesDivisor(in MontgomeryDivisorData divisor) public bool MatchesDivisor(in GpuDivisorPartialData divisor)
{ {
return _modulus == divisor.Modulus; return _modulus == divisor.Modulus;
} }
@@ -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;
}
}
@@ -30,7 +30,7 @@ internal static class DivisorByDivisorKernels
public static void EvaluateDivisorWithStepperKernel( public static void EvaluateDivisorWithStepperKernel(
Index1D _, Index1D _,
MontgomeryDivisorData divisor, GpuDivisorPartialData divisor,
ulong divisorCycle, ulong divisorCycle,
ulong firstCycleRemainder, ulong firstCycleRemainder,
ArrayView<ulong> exponents, ArrayView<ulong> exponents,
@@ -101,12 +101,12 @@ internal static class DivisorByDivisorKernels
hits[globalIndex] = montgomeryResult == 1UL ? (byte)1 : (byte)0; hits[globalIndex] = montgomeryResult == 1UL ? (byte)1 : (byte)0;
} }
public static void ComputeMontgomeryExponentKernel(Index1D index, MontgomeryDivisorData divisor, ArrayView<ulong> exponents, ArrayView<ulong> results) public static void ComputeMontgomeryExponentKernel(Index1D index, GpuDivisorPartialData divisor, ArrayView<ulong> exponents, ArrayView<ulong> results)
{ {
ulong modulus = divisor.Modulus; 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. // 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 exponent = exponents[index];
ulong montgomeryResult = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor, exponent); ulong montgomeryResult = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor.MontgomeryData, exponent);
results[index] = montgomeryResult; results[index] = montgomeryResult;
} }
} }
@@ -11,7 +11,7 @@ using ILGPU.Runtime;
namespace PerfectNumbers.Core.Gpu; namespace PerfectNumbers.Core.Gpu;
/// <summary> /// <summary>
/// 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 <see cref="ConfigureFromMaxPrime"/> before invoking other members so the divisor limits are populated. /// Consumers must call <see cref="ConfigureFromMaxPrime"/> before invoking other members so the divisor limits are populated.
/// </summary> /// </summary>
public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDivisorByDivisorTester public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDivisorByDivisorTester
@@ -24,7 +24,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
// private bool _isConfigured; // private bool _isConfigured;
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>> _kernelCache = new(); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>> _kernelCache = new();
private readonly ConcurrentDictionary<Accelerator, Action<Index1D, MontgomeryDivisorData, ulong, ulong, ArrayView<ulong>, ArrayView<byte>>> _stepperKernelCache = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary<Accelerator, Action<Index1D, GpuDivisorPartialData, ulong, ulong, ArrayView<ulong>, ArrayView<byte>>> _stepperKernelCache = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentDictionary<Accelerator, ConcurrentBag<BatchResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance); private readonly ConcurrentDictionary<Accelerator, ConcurrentBag<BatchResources>> _resourcePools = new(AcceleratorReferenceComparer.Instance);
private readonly ConcurrentBag<GpuContextPool.GpuContextLease> _acceleratorPool = new(); private readonly ConcurrentBag<GpuContextPool.GpuContextLease> _acceleratorPool = new();
private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new(); private readonly ConcurrentBag<DivisorScanSession> _sessionPool = new();
@@ -32,8 +32,8 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private Action<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> GetKernel(Accelerator accelerator) => private Action<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>> GetKernel(Accelerator accelerator) =>
_kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel)); _kernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>, ArrayView<int>>(DivisorByDivisorKernels.CheckKernel));
private Action<Index1D, MontgomeryDivisorData, ulong, ulong, ArrayView<ulong>, ArrayView<byte>> GetStepperKernel(Accelerator accelerator) => private Action<Index1D, GpuDivisorPartialData, ulong, ulong, ArrayView<ulong>, ArrayView<byte>> GetStepperKernel(Accelerator accelerator) =>
_stepperKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, MontgomeryDivisorData, ulong, ulong, ArrayView<ulong>, ArrayView<byte>>(DivisorByDivisorKernels.EvaluateDivisorWithStepperKernel)); _stepperKernelCache.GetOrAdd(accelerator, acc => acc.LoadAutoGroupedStreamKernel<Index1D, GpuDivisorPartialData, ulong, ulong, ArrayView<ulong>, ArrayView<byte>>(DivisorByDivisorKernels.EvaluateDivisorWithStepperKernel));
public int GpuBatchSize public int GpuBatchSize
@@ -258,7 +258,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
for (int i = 0; i < filteredCount; i++) for (int i = 0; i < filteredCount; i++)
{ {
ulong divisorValue = filteredDivisorsSpan[i]; ulong divisorValue = filteredDivisorsSpan[i];
MontgomeryDivisorData divisorDataValue = MontgomeryDivisorData.FromModulus(divisorValue); GpuDivisorPartialData divisorDataValue = GpuDivisorPartialData.Create(divisorValue);
ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, divisorDataValue); ulong divisorCycle = ResolveDivisorCycle(divisorValue, prime, divisorDataValue);
if (divisorCycle != prime) if (divisorCycle != prime)
{ {
@@ -305,11 +305,11 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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) 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; return computedCycle;
@@ -384,7 +384,7 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
private readonly MersenneNumberDivisorByDivisorGpuTester _owner; private readonly MersenneNumberDivisorByDivisorGpuTester _owner;
private readonly GpuContextPool.GpuContextLease _lease; private readonly GpuContextPool.GpuContextLease _lease;
private readonly Accelerator _accelerator; private readonly Accelerator _accelerator;
private Action<Index1D, MontgomeryDivisorData, ulong, ulong, ArrayView<ulong>, ArrayView<byte>> _stepperKernel = null!; private Action<Index1D, GpuDivisorPartialData, ulong, ulong, ArrayView<ulong>, ArrayView<byte>> _stepperKernel = null!;
private MemoryBuffer1D<ulong, Stride1D.Dense> _exponentsBuffer = null!; private MemoryBuffer1D<ulong, Stride1D.Dense> _exponentsBuffer = null!;
private MemoryBuffer1D<byte, Stride1D.Dense> _hitBuffer = null!; private MemoryBuffer1D<byte, Stride1D.Dense> _hitBuffer = null!;
private ulong[] _hostBuffer = null!; private ulong[] _hostBuffer = null!;
@@ -459,10 +459,10 @@ public sealed class MersenneNumberDivisorByDivisorGpuTester : IMersenneNumberDiv
// return; // return;
// } // }
MontgomeryDivisorData cachedData = divisorData; GpuDivisorPartialData cachedData = GpuDivisorPartialData.FromMontgomery(divisorData);
if (cachedData.Modulus != divisor) if (cachedData.Modulus != divisor)
{ {
cachedData = MontgomeryDivisorData.FromModulus(divisor); cachedData = GpuDivisorPartialData.Create(divisor);
} }
ulong cycle = divisorCycle; 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) 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 else
{ {
@@ -408,6 +408,22 @@ public class MersenneDivisorCycles
return success; 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( private static bool AccumulateFactors(
ulong value, ulong value,
Dictionary<ulong, int> counts) Dictionary<ulong, int> counts)