MontgomeryDivisorData converted to a pooled class to limit GC pressure. MontgomeryDivisorDataGpu added for GPU operations. PrimeOrderCalculator kernels precompiled during warmup.

This commit is contained in:
mlemanczyk committed 2025-11-24 15:14:31 +01:00
1 parent 07efd5d2a1
commit 070e2a14fd
32 files changed
+310 -192

No files matched your search

@@ -21,10 +21,10 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi
// return;
// }
MontgomeryDivisorData cachedData = divisorData;
if (cachedData.Modulus != divisor)
if (divisorData.Modulus != divisor)
{
cachedData = MontgomeryDivisorData.FromModulus(divisor);
throw new InvalidOperationException("Divisor data is for a different modulus");
// cachedData = MontgomeryDivisorData.FromModulus(divisor);
}
if (divisorCycle == 0UL)
@@ -39,7 +39,7 @@ internal sealed class MersenneCpuDivisorScanSession : IMersenneNumberDivisorByDi
// Keep these remainder steppers in place so future updates continue reusing the previously computed residues.
// They are critical for avoiding repeated full Montgomery exponentiation work when scanning divisors.
var exponentStepper = new ExponentRemainderStepperCpu(cachedData);
var exponentStepper = new ExponentRemainderStepperCpu(divisorData);
var cycleStepper = new CycleRemainderStepper(divisorCycle);
@@ -204,43 +204,37 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
remainder11 = (byte)((((divisorHigh % 11UL) * 5UL) + (divisorLow % 11UL)) % 11UL);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
var divisorPool = MontgomeryDivisorDataPool.Shared;
while (divisor.CompareTo(limit) <= 0)
{
bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0;
if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
{
ulong candidate = divisor.Low;
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(candidate);
MontgomeryDivisorData divisorData = divisorPool.FromModulus(candidate);
ulong divisorCycle;
// Divisors generated from 2 * k * p + 1 exceed the small-cycle snapshot when p >= 138,000,000, so the short path below never runs.
// if (candidate <= PerfectNumberConstants.MaxQForDivisorCycles)
// {
// divisorCycle = cycleCache.GetCycleLength(candidate);
// }
// else
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
candidate,
prime,
divisorData,
out ulong computedCycle,
out bool primeOrderFailed) || computedCycle == 0UL)
{
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
candidate,
prime,
divisorData,
out ulong computedCycle,
out bool primeOrderFailed) || computedCycle == 0UL)
{
// Divisors produced by 2 * k * p + 1 always exceed PerfectNumberConstants.MaxQForDivisorCycles
// for the exponents scanned here, so skip the unused cache fallback and compute directly.
divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
candidate,
divisorData,
skipPrimeOrderHeuristic: primeOrderFailed);
}
else
{
divisorCycle = computedCycle;
}
// Divisors produced by 2 * k * p + 1 always exceed PerfectNumberConstants.MaxQForDivisorCycles
// for the exponents scanned here, so skip the unused cache fallback and compute directly.
divisorCycle = MersenneDivisorCycles.CalculateCycleLength(
candidate,
divisorData,
skipPrimeOrderHeuristic: primeOrderFailed);
}
else
{
divisorCycle = computedCycle;
}
divisorPool.Return(divisorData);
if (divisorCycle == prime)
{
// A cycle equal to the tested exponent (which is prime in this path) guarantees that the candidate divides
@@ -290,18 +284,16 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
out bool processedAll,
out ulong foundDivisor)
{
processedAll = false;
foundDivisor = 0UL;
bool canAdvance = step <= limit;
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
if (!canAdvance)
{
if (divisor <= limit)
{
if (remainder3 != 0 && remainder7 != 0 && remainder11 != 0 && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
{
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
ulong divisorCycle;
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
@@ -321,6 +313,8 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
divisorCycle = computedCycle;
}
divisorPool.Return(divisorData);
if (divisorCycle == prime)
{
foundDivisor = divisor;
@@ -351,13 +345,12 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
}
ulong remainingIterations = ((limit - divisor) / step) + 1UL;
while (true)
{
bool passesSmallModuli = remainder3 != 0 && remainder7 != 0 && remainder11 != 0;
if (passesSmallModuli && (remainder8 == 1 || remainder8 == 7) && ((decimalMask >> remainder10) & 1) != 0)
{
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
ulong divisorCycle;
if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(
gpu,
@@ -377,6 +370,7 @@ public sealed class MersenneNumberDivisorByDivisorCpuTester : IMersenneNumberDiv
divisorCycle = computedCycle;
}
divisorPool.Return(divisorData);
if (divisorCycle == prime)
{
foundDivisor = divisor;
+12 -6
View File
@@ -76,19 +76,23 @@ public sealed class DivisorCycleCache
// }
ulong[] snapshot = _snapshot;
ulong cycleLength;
if (divisor < (ulong)snapshot.Length)
{
ulong cached = snapshot[divisor];
if (cached == 0UL)
cycleLength = snapshot[divisor];
if (cycleLength == 0UL)
{
throw new InvalidDataException($"Divisor cycle is missing for {divisor}");
}
return cached;
return cycleLength;
}
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
return MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: true);
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
cycleLength = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: true);
divisorPool.Return(divisorData);
return cycleLength;
}
public void GetCycleLengths(ReadOnlySpan<ulong> divisors, Span<ulong> cycles)
@@ -185,12 +189,14 @@ public sealed class DivisorCycleCache
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ComputeCyclesCpu(ReadOnlySpan<ulong> divisors, Span<ulong> cycles, ReadOnlySpan<int> indices)
{
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
for (int i = 0; i < indices.Length; i++)
{
int targetIndex = indices[i];
ulong divisor = divisors[targetIndex];
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData);
divisorPool.Return(divisorData);
}
}
@@ -69,6 +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);
_kernels[i] = new CalculatorKernels(accelerator);
// HeuristicCombinedGpuTables.WarmUp(i, stream);
// SmallPrimeFactorGpuTables.WarmUp(i, stream);
// SharedHeuristicGpuTables.EnsureStaticTables(accelerator, stream);
@@ -115,18 +116,41 @@ public sealed class PrimeOrderCalculatorAccelerator
public Dictionary<ulong, int> Pow2ModEntriesToTestOnHost = [];
private static readonly CalculatorKernels[] _kernels =new CalculatorKernels[AcceleratorPool.Shared.Accelerators.Length];
private sealed class CalculatorKernels(Accelerator accelerator)
{
public readonly Kernel CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel));
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>>();
public readonly Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>(PrimeTesterKernels.HeuristicTrialCombinedDivisionKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>>();
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>>();
public readonly Kernel PollardRhoKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.TryPollardRhoKernel));
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>> Pow2ModWideKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>(PrimeOrderGpuHeuristics.Pow2ModKernelWide)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>>();
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>> SharesFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>>>();
public readonly Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ulong, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ulong, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ulong, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
}
public readonly Kernel CheckFactorsKernel;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews> HeuristicCombinedTrialDivisionKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> KeepMontgomeryKernelLauncher;
public readonly Kernel PollardRhoKernel;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>> Pow2ModWideKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>> SharesFactorKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SmallPrimeFactorKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> SmallPrimeSieveKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, ulong, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> SpecialMaxKernelLauncher;
public void EnsureCapacity(int factorsCount, int primeTesterCapacity)
{
@@ -260,25 +284,18 @@ public sealed class PrimeOrderCalculatorAccelerator
SmallPrimeFactorSquares = tables.Squares.View;
}
CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.CheckFactorsKernel));
var kernels = _kernels[acceleratorIndex];
ConvertToStandardKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelConvertToStandard)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
HeuristicCombinedTrialDivisionKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>(PrimeTesterKernels.HeuristicTrialCombinedDivisionKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, byte, ArrayView<int>, ulong, ulong, HeuristicCombinedGpuViews>>();
KeepMontgomeryKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.Pow2MontgomeryKernelKeepMontgomery)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
PollardRhoKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<ulong, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<byte, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(Pow2MontgomeryKernels.TryPollardRhoKernel));
Pow2ModWideKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>(PrimeOrderGpuHeuristics.Pow2ModKernelWide)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>>();
SharesFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SharesFactorKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<byte>>>();
SmallPrimeFactorKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(SmallPrimeFactorKernels.SmallPrimeFactorKernelScan)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, uint, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<int, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
SmallPrimeSieveKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterKernels.SmallPrimeSieveKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>>();
SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView1D<ulong, Stride1D.Dense>, int, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>>();
CheckFactorsKernel = kernels.CheckFactorsKernel;
ConvertToStandardKernelLauncher = kernels.ConvertToStandardKernelLauncher;
HeuristicCombinedTrialDivisionKernelLauncher = kernels.HeuristicCombinedTrialDivisionKernelLauncher;
KeepMontgomeryKernelLauncher = kernels.KeepMontgomeryKernelLauncher;
PollardRhoKernel = kernels.PollardRhoKernel;
Pow2ModWideKernelLauncher = kernels.Pow2ModWideKernelLauncher;
SharesFactorKernelLauncher = kernels.SharesFactorKernelLauncher;
SmallPrimeFactorKernelLauncher = kernels.SmallPrimeFactorKernelLauncher;
SmallPrimeSieveKernelLauncher = kernels.SmallPrimeSieveKernelLauncher;
SpecialMaxKernelLauncher = kernels.SpecialMaxKernelLauncher;
}
}
@@ -17,18 +17,28 @@ internal static class Pow2MontgomeryKernels
public static void Pow2MontgomeryKernelKeepMontgomery(
Index1D index,
ArrayView1D<ulong, Stride1D.Dense> exponents,
MontgomeryDivisorData divisor,
ulong divisorModulus,
ulong divisorNPrime,
ulong divisorMontgomeryOne,
ulong divisorMontgomeryTwo,
ulong divisorMontgomeryTwoSquared,
ArrayView1D<ulong, Stride1D.Dense> results)
{
MontgomeryDivisorDataGpu divisor = new(divisorModulus, divisorNPrime, divisorMontgomeryOne, divisorMontgomeryTwo, divisorMontgomeryTwoSquared);
results[0] = ULongExtensions.Pow2MontgomeryModWindowedGpuKeepMontgomery(divisor, exponents[0]);
}
public static void Pow2MontgomeryKernelConvertToStandard(
Index1D index,
ArrayView1D<ulong, Stride1D.Dense> exponents,
MontgomeryDivisorData divisor,
ulong divisorModulus,
ulong divisorNPrime,
ulong divisorMontgomeryOne,
ulong divisorMontgomeryTwo,
ulong divisorMontgomeryTwoSquared,
ArrayView1D<ulong, Stride1D.Dense> results)
{
MontgomeryDivisorDataGpu divisor = new(divisorModulus, divisorNPrime, divisorMontgomeryOne, divisorMontgomeryTwo, divisorMontgomeryTwoSquared);
results[0] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponents[0]);
}
@@ -11,12 +11,17 @@ internal static partial class PrimeOrderGpuHeuristics
int count,
ulong phi,
ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense> toTest,
MontgomeryDivisorData divisorData,
ulong divisorModulus,
ulong divisorNPrime,
ulong divisorMontgomeryOne,
ulong divisorMontgomeryTwo,
ulong divisorMontgomeryTwoSquared,
ArrayView1D<ulong, Stride1D.Dense> result
)
{
ulong order = phi;
int entryCount = count;
MontgomeryDivisorDataGpu divisorData = new(divisorModulus, divisorNPrime, divisorMontgomeryOne, divisorMontgomeryTwo, divisorMontgomeryTwoSquared);
for (int i = 0; i < entryCount; i++)
{
@@ -155,7 +160,11 @@ internal static partial class PrimeOrderGpuHeuristics
Index1D index,
ulong prime,
OrderKernelConfig config,
MontgomeryDivisorData divisor,
ulong divisorModulus,
ulong divisorNPrime,
ulong divisorMontgomeryOne,
ulong divisorMontgomeryTwo,
ulong divisorMontgomeryTwoSquared,
ArrayView1D<uint, Stride1D.Dense> primes,
ArrayView1D<ulong, Stride1D.Dense> squares,
int primeCount,
@@ -227,6 +236,8 @@ internal static partial class PrimeOrderGpuHeuristics
SortFactors(phiFactors, phiExponents, phiFactorCount);
MontgomeryDivisorDataGpu divisor = new(divisorModulus, divisorNPrime, divisorMontgomeryOne, divisorMontgomeryTwo, divisorMontgomeryTwoSquared);
if (TrySpecialMaxKernel(phi, prime, phiFactors, phiFactorCount, divisor))
{
resultOut[0] = phi;
@@ -381,7 +392,7 @@ internal static partial class PrimeOrderGpuHeuristics
ulong prime,
ArrayView1D<ulong, Stride1D.Dense> factors,
int factorCount,
in MontgomeryDivisorData divisor)
in MontgomeryDivisorDataGpu divisor)
{
for (int i = 0; i < factorCount; i++)
{
@@ -401,7 +412,7 @@ internal static partial class PrimeOrderGpuHeuristics
return true;
}
private static ulong InitializeStartingOrderKernel(ulong prime, ulong phi, in MontgomeryDivisorData divisor)
private static ulong InitializeStartingOrderKernel(ulong prime, ulong phi, in MontgomeryDivisorDataGpu divisor)
{
ulong order = phi;
ulong residue = prime & 7UL;
@@ -423,7 +434,7 @@ internal static partial class PrimeOrderGpuHeuristics
ArrayView1D<ulong, Stride1D.Dense> factors,
ArrayView1D<int, Stride1D.Dense> exponents,
int factorCount,
in MontgomeryDivisorData divisor)
in MontgomeryDivisorDataGpu divisor)
{
for (int i = 0; i < factorCount; i++)
{
@@ -458,7 +469,7 @@ internal static partial class PrimeOrderGpuHeuristics
private static bool TryConfirmOrderKernel(
ulong prime,
ulong order,
in MontgomeryDivisorData divisor,
in MontgomeryDivisorDataGpu divisor,
uint limit,
ArrayView1D<uint, Stride1D.Dense> primes,
ArrayView1D<ulong, Stride1D.Dense> squares,
@@ -532,7 +543,7 @@ internal static partial class PrimeOrderGpuHeuristics
ulong order,
ulong previousOrder,
byte hasPreviousOrder,
in MontgomeryDivisorData divisor,
in MontgomeryDivisorDataGpu divisor,
uint limit,
int maxPowChecks,
ArrayView1D<uint, Stride1D.Dense> primes,
@@ -851,7 +862,7 @@ internal static partial class PrimeOrderGpuHeuristics
private static bool TryConfirmCandidateKernel(
ulong prime,
ulong candidate,
in MontgomeryDivisorData divisor,
in MontgomeryDivisorDataGpu divisor,
uint limit,
ArrayView1D<uint, Stride1D.Dense> primes,
ArrayView1D<ulong, Stride1D.Dense> squares,
@@ -1174,7 +1185,7 @@ internal static partial class PrimeOrderGpuHeuristics
}
}
private static bool Pow2EqualsOneKernel(ulong exponent, in MontgomeryDivisorData divisor)
private static bool Pow2EqualsOneKernel(ulong exponent, in MontgomeryDivisorDataGpu divisor)
{
return ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponent) == 1UL;
}
@@ -1200,9 +1211,10 @@ internal static partial class PrimeOrderGpuHeuristics
/// This kernel always sets the result of the corresponding element. Callers don't need to clear the output buffers.
internal static void Pow2ModKernel(Index1D index, ArrayView1D<ulong, Stride1D.Dense> exponents, MontgomeryDivisorData divisor, ArrayView1D<ulong, Stride1D.Dense> remainders)
internal static void Pow2ModKernel(Index1D index, ArrayView1D<ulong, Stride1D.Dense> exponents, ulong divisorModulus, ulong divisorNPrime, ulong divisorMontgomeryOne, ulong divisorMontgomeryTwo, ulong divisorMontgomeryTwoSquared, ArrayView1D<ulong, Stride1D.Dense> remainders)
{
ulong exponent = exponents[index];
MontgomeryDivisorDataGpu divisor = new(divisorModulus, divisorNPrime, divisorMontgomeryOne, divisorMontgomeryTwo, divisorMontgomeryTwoSquared);
remainders[index] = ULongExtensions.Pow2MontgomeryModWindowedGpuConvertToStandard(divisor, exponent);
}
@@ -10,7 +10,7 @@ internal static partial class PrimeOrderGpuHeuristics
ulong phi,
ArrayView1D<ulong, Stride1D.Dense> factors,
int factorCount,
MontgomeryDivisorData divisor,
ulong divisorModulus,
ArrayView1D<ulong, Stride1D.Dense> candidates,
ArrayView1D<ulong, Stride1D.Dense> resultOut)
{
@@ -57,7 +57,7 @@ internal static partial class PrimeOrderGpuHeuristics
candidates[insert + 1] = current;
}
var divisorPartial = new GpuDivisorPartialData(divisor.Modulus);
var divisorPartial = new GpuDivisorPartialData(divisorModulus);
var stepper = new ExponentRemainderStepperGpu(divisorPartial);
if (stepper.InitializeIsUnityGpu(candidates[0]))
{
@@ -260,7 +260,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
ArrayView1D<ulong, Stride1D.Dense> exponentViewDevice = exponentBuffer.View;
ArrayView1D<byte, Stride1D.Dense> hitsView = hitsBuffer.View;
ArrayView1D<int, Stride1D.Dense> hitIndexView = hitIndexBuffer.View;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
while (remainingCount > 0UL && !composite)
{
int chunkCount = chunkCountBaseline;
@@ -284,7 +284,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
if (passesSmallModuli && (localRemainder8 == 1 || localRemainder8 == 7) && ((decimalMask >> localRemainder10) & 1) != 0)
{
ulong divisorValue = (ulong)nextDivisor128;
MontgomeryDivisorData montgomeryData = MontgomeryDivisorData.FromModulus(divisorValue);
MontgomeryDivisorData montgomeryData = divisorPool.FromModulus(divisorValue);
ulong divisorCycle = ResolveDivisorCycle(gpu, divisorValue, prime, in montgomeryData);
if (divisorCycle == prime)
{
@@ -294,6 +294,7 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN
return true;
}
divisorPool.Return(montgomeryData);
divisorSpan[admissibleCount] = divisorValue;
divisorDataSpan[admissibleCount] = new GpuDivisorPartialData(divisorValue);
offsetSpan[admissibleCount] = admissibleCount;
@@ -0,0 +1,10 @@
namespace PerfectNumbers.Core.Gpu;
public readonly struct MontgomeryDivisorDataGpu(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
{
public readonly ulong Modulus = modulus;
public readonly ulong NPrime = nPrime;
public readonly ulong MontgomeryOne = montgomeryOne;
public readonly ulong MontgomeryTwo = montgomeryTwo;
public readonly ulong MontgomeryTwoSquared = montgomeryTwoSquared;
}
@@ -25,7 +25,7 @@ public static class Pow2MontgomeryGpuCalculator
// resultBuffer.MemSetToZero(stream);
var kernelLauncher = gpu.KeepMontgomeryKernelLauncher;
kernelLauncher(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
kernelLauncher(stream, 1, exponentBuffer.View, divisor.Modulus, divisor.NPrime, divisor.MontgomeryOne, divisor.MontgomeryTwo, divisor.MontgomeryTwoSquared, resultBuffer.View);
resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize();
@@ -54,7 +54,7 @@ public static class Pow2MontgomeryGpuCalculator
var kernelLauncher = gpu.ConvertToStandardKernelLauncher!;
kernelLauncher(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
kernelLauncher(stream, 1, exponentBuffer.View, divisor.Modulus, divisor.NPrime, divisor.MontgomeryOne, divisor.MontgomeryTwo, divisor.MontgomeryTwoSquared, resultBuffer.View);
resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize();
@@ -82,7 +82,7 @@ public static class Pow2MontgomeryGpuCalculator
var accelerator = gpu.Accelerator;
var kernelLauncher = gpu.ConvertToStandardKernelLauncher;
kernelLauncher(stream, 1, exponentBuffer.View, divisor, resultBuffer.View);
kernelLauncher(stream, 1, exponentBuffer.View, divisor.Modulus, divisor.NPrime, divisor.MontgomeryOne, divisor.MontgomeryTwo, divisor.MontgomeryTwoSquared, resultBuffer.View);
resultBuffer.View.CopyToCPU(stream, ref result, 1);
stream.Synchronize();
@@ -398,14 +398,18 @@ internal static partial class PrimeOrderGpuHeuristics
statusBuffer.View);
var orderKernel = GetOrderKernel(acceleratorIndex);
var kernelLauncher = orderKernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, OrderKernelConfig, MontgomeryDivisorData, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>>();
var kernelLauncher = orderKernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, OrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>>();
kernelLauncher(
stream,
1,
prime,
kernelConfig,
divisorData,
divisorData.Modulus,
divisorData.NPrime,
divisorData.MontgomeryOne,
divisorData.MontgomeryTwo,
divisorData.MontgomeryTwoSquared,
smallPrimesView,
smallSquaresView,
(int)smallPrimesView.Length,
@@ -417,7 +421,7 @@ internal static partial class PrimeOrderGpuHeuristics
resultBuffer.View.CopyToCPU(stream, ref order, 1);
stream.Synchronize();
stream.Dispose();
AcceleratorStreamPool.Return(acceleratorIndex, stream);
phiFactorBuffer.Dispose();
phiExponentBuffer.Dispose();
workFactorBuffer.Dispose();
@@ -458,7 +462,7 @@ internal static partial class PrimeOrderGpuHeuristics
}
var accelerator = _accelerators[acceleratorIndex];
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, OrderKernelConfig, MontgomeryDivisorData, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>(CalculateOrderKernel);
var loaded = accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, OrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>(CalculateOrderKernel);
var kernel = KernelUtil.GetKernel(loaded);
pool[acceleratorIndex] = kernel;
@@ -530,11 +534,12 @@ internal static partial class PrimeOrderGpuHeuristics
// remainderBuffer.MemSetToZero(stream);
var pow2ModKernel = GetPow2ModKernel(acceleratorIndex);
var kernelLauncher = pow2ModKernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>>();
var kernelLauncher = pow2ModKernel.CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>>();
try
{
kernelLauncher(stream, exponents.Length, exponentBuffer.View, divisorData, remainderBuffer.View);
kernelLauncher(stream, exponents.Length, exponentBuffer.View, divisorData.Modulus, divisorData.NPrime, divisorData.MontgomeryOne, divisorData.MontgomeryTwo, divisorData.MontgomeryTwoSquared, remainderBuffer.View);
remainderBuffer.View.CopyToCPU(stream, ref MemoryMarshal.GetReference(results), exponents.Length);
stream.Synchronize();
@@ -557,7 +562,7 @@ internal static partial class PrimeOrderGpuHeuristics
return true;
}
private static void ComputePow2ModCpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorData divisorData, Span<ulong> results)
private static void ComputePow2ModCpu(ReadOnlySpan<ulong> exponents, ulong prime, in MontgomeryDivisorDataGpu divisorData, Span<ulong> results)
{
int length = exponents.Length;
for (int i = 0; i < length; i++)
@@ -636,7 +641,7 @@ internal static partial class PrimeOrderGpuHeuristics
}
}
private static ulong Pow2ModCpu(ulong exponent, ulong modulus, in MontgomeryDivisorData divisorData)
private static ulong Pow2ModCpu(ulong exponent, ulong modulus, in MontgomeryDivisorDataGpu divisorData)
{
if (modulus <= 1UL)
{
@@ -655,7 +660,7 @@ internal static partial class PrimeOrderGpuHeuristics
}
var accelerator = _accelerators[acceleratorIndex];
var loaded = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, MontgomeryDivisorData, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernel));
var loaded = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(Pow2ModKernel));
pool[acceleratorIndex] = loaded;
return loaded;
@@ -478,7 +478,7 @@ public sealed class HeuristicCombinedPrimeTester
internal static HeuristicDivisorPreparation PrepareHeuristicDivisor(in HeuristicDivisorCandidate candidate)
{
ulong divisor = candidate.Value;
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor);
bool hasCycleHint = TryGetCycleLengthHint(divisor, out ulong cycleLength);
return new(candidate, divisorData, cycleLength, hasCycleHint);
}
+1 -1
View File
@@ -445,7 +445,7 @@ public sealed class HeuristicPrimeTester
internal static HeuristicDivisorPreparation PrepareHeuristicDivisor(in HeuristicDivisorCandidate candidate)
{
ulong divisor = candidate.Value;
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor);
bool hasCycleHint = TryGetCycleLengthHint(divisor, out ulong cycleLength);
return new HeuristicDivisorPreparation(candidate, divisorData, cycleLength, hasCycleHint);
}
+6 -2
View File
@@ -222,8 +222,10 @@ public class MersenneDivisorCycles
if (divisor <= ulong.MaxValue)
{
ulong divisor64 = (ulong)divisor;
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64);
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor64);
ulong cycle = CalculateCycleLength(divisor64, divisorData);
divisorPool.Return(divisorData);
return (UInt128)cycle;
}
@@ -733,6 +735,7 @@ public class MersenneDivisorCycles
ulong localStart = threadStart;
ulong localEnd = threadEnd;
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
tasks[taskIndex] = Task.Run(() =>
{
ulong rangeLength = localEnd - localStart + 1UL;
@@ -758,8 +761,9 @@ public class MersenneDivisorCycles
// TODO: Migrate this generation path to the shared
// unrolled-hex calculator so the CPU generator matches
// the GPU benchmark leader for large divisors.
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
MontgomeryDivisorData divisorData = divisorPool.FromModulus(divisor);
ulong cycle = CalculateCycleLength(divisor, divisorData);
divisorPool.Return(divisorData);
localCycles[localCycleIndex++] = (divisor, cycle);
}
+18 -41
View File
@@ -1,47 +1,24 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core;
public readonly struct MontgomeryDivisorData(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
public sealed class MontgomeryDivisorData
{
public readonly ulong Modulus = modulus;
public static readonly MontgomeryDivisorData Empty = new();
public ulong Modulus;
public ulong NPrime;
public ulong MontgomeryOne;
public ulong MontgomeryTwo;
public ulong MontgomeryTwoSquared;
public readonly ulong NPrime = nPrime;
public readonly ulong MontgomeryOne = montgomeryOne;
public readonly ulong MontgomeryTwo = montgomeryTwo;
public readonly ulong MontgomeryTwoSquared = montgomeryTwoSquared;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong ComputeMontgomeryResidue(UInt128 value, ulong modulus) => (ulong)(value % modulus);
private static ulong ComputeMontgomeryNPrime(ulong modulus)
{
ulong inv = modulus;
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
return unchecked(0UL - inv);
}
public static MontgomeryDivisorData FromModulus(ulong modulus)
{
ulong nPrime = ComputeMontgomeryNPrime(modulus);
ulong montgomeryOne = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64, modulus);
ulong montgomeryTwo = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64x2, modulus);
ulong montgomeryTwoSquared = ULongExtensions.MontgomeryMultiplyCpu(montgomeryTwo, montgomeryTwo, modulus, nPrime);
return new MontgomeryDivisorData(
modulus,
nPrime,
montgomeryOne,
montgomeryTwo,
montgomeryTwoSquared);
}
public MontgomeryDivisorData()
{
}
public MontgomeryDivisorData(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
{
Modulus = modulus;
NPrime = nPrime;
MontgomeryOne = montgomeryOne;
MontgomeryTwo = montgomeryTwo;
MontgomeryTwoSquared = montgomeryTwoSquared;
}
}
@@ -0,0 +1,80 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core;
public static class MontgomeryDivisorDataPool
{
[ThreadStatic]
private static Queue<MontgomeryDivisorData>? _pool;
public static Queue<MontgomeryDivisorData> Shared => _pool ??= new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong ComputeMontgomeryResidue(UInt128 value, ulong modulus) => (ulong)(value % modulus);
private static ulong ComputeMontgomeryNPrime(ulong modulus)
{
ulong inv = modulus;
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
inv *= unchecked(2UL - modulus * inv);
return unchecked(0UL - inv);
}
public static MontgomeryDivisorData Rent(ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
{
var pool = _pool ??= new();
if (pool.TryDequeue(out var pooled))
{
pooled.Modulus = modulus;
pooled.NPrime = nPrime;
pooled.MontgomeryOne = montgomeryOne;
pooled.MontgomeryTwo = montgomeryTwo;
pooled.MontgomeryTwoSquared = montgomeryTwoSquared;
return pooled;
}
return new(modulus, nPrime, montgomeryOne, montgomeryTwo, montgomeryTwoSquared);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Return(MontgomeryDivisorData divisorData) => _pool!.Enqueue(divisorData);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MontgomeryDivisorData FromModulus(this Queue<MontgomeryDivisorData> queue, ulong modulus)
{
ulong nPrime = ComputeMontgomeryNPrime(modulus);
ulong montgomeryOne = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64, modulus);
ulong montgomeryTwo = ComputeMontgomeryResidue(UInt128Numbers.OneShiftedLeft64x2, modulus);
ulong montgomeryTwoSquared = ULongExtensions.MontgomeryMultiplyCpu(montgomeryTwo, montgomeryTwo, modulus, nPrime);
return queue.Rent(
modulus,
nPrime,
montgomeryOne,
montgomeryTwo,
montgomeryTwoSquared);
}
public static MontgomeryDivisorData Rent(this Queue<MontgomeryDivisorData> queue, ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared)
{
if (queue.TryDequeue(out var pooled))
{
pooled.Modulus = modulus;
pooled.NPrime = nPrime;
pooled.MontgomeryOne = montgomeryOne;
pooled.MontgomeryTwo = montgomeryTwo;
pooled.MontgomeryTwoSquared = montgomeryTwoSquared;
return pooled;
}
return new(modulus, nPrime, montgomeryOne, montgomeryTwo, montgomeryTwoSquared);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Return(this Queue<MontgomeryDivisorData> queue, MontgomeryDivisorData divisorData) => queue.Enqueue(divisorData);
}
@@ -8,7 +8,7 @@ public static class PerfectNumberConstants
public const int DefaultSmallPrimeFactorSlotCount = 64;
public const int DefaultSpecialMaxFactorCapacity = 1024;
public const ulong ExtraDivisorCycleSearchLimit = 64UL;
public const int GpuFrequency = 16 - 1;
public const int GpuFrequency = 4 - 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;
@@ -1389,7 +1389,7 @@ internal static partial class PrimeOrderCalculator
private static bool RunOnGpu()
{
// return false;
// return true;
return true;
// int cpuCount;
// lock(_sync)
@@ -44,7 +44,7 @@ internal static partial class PrimeOrderCalculator
// var kernelLauncher = gpu.CheckFactorsKernelLauncher;
var kernel = gpu.CheckFactorsKernel;
kernel.Launch(stream, 1, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData, gpu.OutputUlong.View);
kernel.Launch(stream, 1, entryCount, phi, gpu.Pow2ModEntriesToTestOnDevice.View, divisorData.Modulus, divisorData.NPrime, divisorData.MontgomeryOne, divisorData.MontgomeryTwo, divisorData.MontgomeryTwoSquared, gpu.OutputUlong.View);
gpu.OutputUlong.View.CopyToCPU(stream, ref order, 1);
stream.Synchronize();
@@ -146,7 +146,7 @@ internal static partial class PrimeOrderCalculator
phi,
specialMaxFactorsView,
factorCount,
divisorData,
divisorData.Modulus,
gpu.OutputUlong.View,
specialMaxResultView);
@@ -82,7 +82,7 @@ internal static partial class PrimeOrderCalculator
Console.WriteLine(messageFactory());
}
}
private static UInt128 CalculateWideInternal(in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderSearchConfig config)
private static UInt128 CalculateWideInternal(in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData? divisorData, in PrimeOrderSearchConfig config)
{
if (prime <= UInt128.One)
{
+4 -2
View File
@@ -86,13 +86,15 @@ internal static partial class PrimeOrderCalculator
}
ulong prime64 = (ulong)prime;
divisorData = MontgomeryDivisorData.FromModulus(prime64);
Queue<MontgomeryDivisorData> divisorPool = MontgomeryDivisorDataPool.Shared;
divisorData = divisorPool.FromModulus(prime64);
ulong order64 = Calculate(prime64, previous, divisorData, config, device);
divisorPool.Return(divisorData);
result = order64 == 0UL ? UInt128.Zero : (UInt128)order64;
}
else
{
divisorData = default;
divisorData = MontgomeryDivisorData.Empty;
result = CalculateWideInternal(prime, previousOrder, divisorData, config);
}
+4 -4
View File
@@ -229,13 +229,13 @@ public static partial class ULongExtensions
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong Pow2MontgomeryModWindowedGpuKeepMontgomery(in MontgomeryDivisorData divisor, ulong exponent)
internal static ulong Pow2MontgomeryModWindowedGpuKeepMontgomery(in MontgomeryDivisorDataGpu divisor, ulong exponent)
{
return Pow2MontgomeryModWindowedGpuMontgomeryResult(divisor, exponent);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong Pow2MontgomeryModWindowedGpuConvertToStandard(in MontgomeryDivisorData divisor, ulong exponent)
internal static ulong Pow2MontgomeryModWindowedGpuConvertToStandard(in MontgomeryDivisorDataGpu divisor, ulong exponent)
{
ulong modulus = divisor.Modulus;
ulong nPrime = divisor.NPrime;
@@ -244,7 +244,7 @@ public static partial class ULongExtensions
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong Pow2MontgomeryModWindowedGpuMontgomeryResult(in MontgomeryDivisorData divisor, ulong exponent)
private static ulong Pow2MontgomeryModWindowedGpuMontgomeryResult(in MontgomeryDivisorDataGpu divisor, ulong exponent)
{
ulong modulus = divisor.Modulus;
// Reuse the GPU-compatible MontgomeryMultiply helper highlighted in MontgomeryMultiplyBenchmarks so the accelerator path
@@ -349,7 +349,7 @@ public static partial class ULongExtensions
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong ComputeMontgomeryOddPowerGpu(ulong exponent, in MontgomeryDivisorData divisor, ulong modulus, ulong nPrime)
private static ulong ComputeMontgomeryOddPowerGpu(ulong exponent, in MontgomeryDivisorDataGpu divisor, ulong modulus, ulong nPrime)
{
ulong baseValue = divisor.MontgomeryTwo;
ulong power = divisor.MontgomeryOne;