Pow2MontgomeryModWindowedConvertToStandardCpu usages replaced with a comparison to MontgomeryOne avoiding unnecessary conversions. Results updated.

This commit is contained in:
mlemanczyk committed 2026-01-03 01:37:40 +01:00
1 parent 3f59cdc14b
commit 6b80e31e6c
7 files changed
+57 -34

No files matched your search

@@ -141,6 +141,12 @@ public static class Pow2MontgomeryKernels
return Pow2MontgomeryModWindowedGpuMontgomeryResult(divisor, exponent);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong Pow2MontgomeryModWindowedGpuKeepMontgomery(this MontgomeryDivisorDataGpu divisor, ulong exponent)
{
return Pow2MontgomeryModWindowedGpuMontgomeryResult(divisor, exponent);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong Pow2MontgomeryModWindowedGpuConvertToStandard(this MontgomeryDivisorDataGpu divisor, ulong exponent)
{
@@ -39,7 +39,7 @@ internal static partial class PrimeOrderKernels
}
// ulong candidate = order / primeFactor;
if (divisorData.Pow2MontgomeryModWindowedGpuConvertToStandard(candidate) == 1UL)
if (divisorData.Pow2MontgomeryModWindowedGpuKeepMontgomery(candidate) == divisorData.MontgomeryOne)
{
order = candidate;
continue;
@@ -395,7 +395,7 @@ internal static partial class PrimeOrderKernels
}
ulong reduced = phi / factor;
if (divisor.Pow2MontgomeryModWindowedGpuConvertToStandard(reduced) == 1UL)
if (divisor.Pow2MontgomeryModWindowedGpuKeepMontgomery(reduced) == divisor.MontgomeryOne)
{
return false;
}
@@ -1179,7 +1179,7 @@ internal static partial class PrimeOrderKernels
private static bool Pow2EqualsOneKernel(ulong exponent, in MontgomeryDivisorDataGpu divisor)
{
return divisor.Pow2MontgomeryModWindowedGpuConvertToStandard(exponent) == 1UL;
return divisor.Pow2MontgomeryModWindowedGpuKeepMontgomery(exponent) == divisor.MontgomeryOne;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
@@ -99,7 +99,7 @@ public struct MersenneDivisorCyclesTemplate
return cycleLength == exponent;
}
return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL;
return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne;
}
public static bool CycleEqualsExponentForMersenneCandidate(ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent)
@@ -125,7 +125,7 @@ public struct MersenneDivisorCyclesTemplate
}
}
return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL;
return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne;
}
private static bool IsValidMersenneDivisorCandidate(ulong divisor, ulong exponent)
@@ -938,7 +938,7 @@ public static partial class PrimeOrderCalculatorTemplate
ulong candidate = order - primeFactor * remainder;
// ulong candidate = order / primeFactor;
if (candidate.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL)
if (candidate.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne)
{
order = candidate;
continue;
@@ -1674,7 +1674,7 @@ public static partial class PrimeOrderCalculatorTemplate
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static bool Pow2EqualsOne(ulong exponent, in MontgomeryDivisorData divisorData)
{
return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL;
return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne;
}
#if DEVICE_HYBRID || DEVICE_GPU
@@ -1685,7 +1685,7 @@ public static partial class PrimeOrderCalculatorTemplate
in MontgomeryDivisorData divisorData)
{
// TODO: swap to GPU pow2 when available; fallback to CPU for now.
return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL;
return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -1695,7 +1695,7 @@ public static partial class PrimeOrderCalculatorTemplate
ulong prime,
in MontgomeryDivisorData divisorData)
{
return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL;
return exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(divisorData) == divisorData.MontgomeryOne;
}
#endif
+3 -3
View File
@@ -18,7 +18,7 @@ public static partial class ULongExtensions
// Small divisors come from the precomputed <= 4,000,000 snapshot and tiny rotation counts
// appear when cycle reductions trigger. Route both cases through the single-bit ladder so the
// CPU path avoids renting the window tables when a cheaper method is available.
if (exponent <= Pow2WindowFallbackThreshold || modulus <= (ulong)PerfectNumberConstants.MaxQForDivisorCycles)
if (exponent <= Pow2WindowFallbackThreshold || modulus <= PerfectNumberConstants.MaxQForDivisorCycles)
{
return Pow2MontgomeryModSingleBitKeepMontgomery(exponent, divisor);
}
@@ -88,7 +88,7 @@ public static partial class ULongExtensions
// Small divisors come from the precomputed <= 4,000,000 snapshot and tiny rotation counts
// appear when cycle reductions trigger. Route both cases through the single-bit ladder so the
// CPU path avoids renting the window tables when a cheaper method is available.
if (exponent <= Pow2WindowFallbackThreshold || modulus <= (ulong)PerfectNumberConstants.MaxQForDivisorCycles)
if (exponent <= Pow2WindowFallbackThreshold || modulus <= PerfectNumberConstants.MaxQForDivisorCycles)
{
return Pow2MontgomeryModSingleBitConvertToStandard(exponent, divisor);
}
@@ -274,7 +274,7 @@ public static partial class ULongExtensions
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static bool AdvanceMersenneDivisorCycleStepCpu(ref ulong pow, ulong divisor, ref ulong order)
{
pow += pow;
+19 -22
View File
@@ -390,7 +390,7 @@ public static partial class ULongExtensions
return value.BinaryGcd(exponentLog) > 1UL;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong BinaryGcd(this ulong a, ulong b)
{
if (a == 0UL)
@@ -423,7 +423,7 @@ public static partial class ULongExtensions
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static ulong Select(ulong left, ulong right, bool useRight)
{
ulong mask = useRight ? ulong.MaxValue : 0UL;
@@ -432,7 +432,7 @@ public static partial class ULongExtensions
// Benchmarks (Mod5ULongBenchmarks) show the direct `% 5` is still cheaper (~0.26 ns vs 0.43 ns), so keep the modulo until a faster lookup is proven.
// (Mod8/Mod10 stay masked because they win; Mod5 currently does not.)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static byte Mod10(this ulong value) => (byte)(value % 5UL) switch
{
0 => 0,
@@ -446,7 +446,7 @@ public static partial class ULongExtensions
public static ulong Mod128(this ulong value) => value & 127UL;
// Benchmarks confirm `%` beats our current Mod5/Mod3 helpers for 64-bit inputs, so leave these modulo operations in place until a superior lookup is available.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Mod10_8_5_3(this ulong value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3)
{
mod8 = value & 7UL;
@@ -474,7 +474,7 @@ public static partial class ULongExtensions
}
// Mod5/Mod3 lookup tables are currently slower on 64-bit operands; keep the direct modulo until benchmarks flip.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Mod10_8_5_3Steps(this ulong value, out ulong step10, out ulong step8, out ulong step5, out ulong step3)
{
ulong mod8 = value & 7UL;
@@ -520,16 +520,16 @@ public static partial class ULongExtensions
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static UInt128 Mul64(this ulong a, ulong b) => ((UInt128)a.MulHighCpu(b) << 64) | (UInt128)(a * b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong MulHighCpu(this ulong x, ulong y)
{
return (ulong)(((UInt128)x * y) >> 64);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong MulHighGpu(this ulong x, ulong y)
{
// Retain this manual decomposition for GPU-style arithmetic so callers staging accelerator work
@@ -553,7 +553,7 @@ public static partial class ULongExtensions
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong AddMod64(this ulong value, ulong addend, ulong modulus)
{
// Every CPU caller (Pollard Rho and the ModPow64 ladder) supplies moduli of at least three, so leave this guard
@@ -580,7 +580,7 @@ public static partial class ULongExtensions
return (ulong)sum;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static ulong FoldMontgomery(ulong value, ulong modulus)
{
// Pollard Rho and ModPow64 both feed moduli of at least three, so retain the documentation and skip the runtime guard.
@@ -609,7 +609,7 @@ public static partial class ULongExtensions
return folded;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong ModPow64(this ulong value, ulong exponent, ulong modulus)
{
// Pollard Rho and the Mersenne testers never pass moduli below two, so keep the guard commented for clarity.
@@ -635,13 +635,10 @@ public static partial class ULongExtensions
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
// Reuse the direct UInt128 reduction path that dominated MulMod64Benchmarks to avoid
// the redundant operand modulo operations in the older fallback.
public static ulong MulMod64(this ulong a, ulong b, ulong modulus)
{
return (ulong)(((UInt128)a * b) % modulus);
}
public static ulong MulMod64(this ulong a, ulong b, ulong modulus) => (ulong)(((UInt128)a * b) % modulus);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong MontgomeryMultiplyCpu(this ulong a, ulong b, ulong modulus, ulong nPrime)
@@ -658,7 +655,7 @@ public static partial class ULongExtensions
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static ulong Pow2MontgomeryModSingleBitKeepMontgomery(ulong exponent, in MontgomeryDivisorData divisor)
{
ulong modulus = divisor.Modulus;
@@ -686,7 +683,7 @@ public static partial class ULongExtensions
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static ulong Pow2MontgomeryModSingleBitConvertToStandard(ulong exponent, in MontgomeryDivisorData divisor)
{
ulong modulus = divisor.Modulus;
@@ -714,7 +711,7 @@ public static partial class ULongExtensions
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static UInt128 PowMod(this ulong exponent, UInt128 modulus)
{
// EvenPerfectBitScanner starts CPU scans at p >= 138,000,000 (Program.cs),
@@ -1198,7 +1195,7 @@ public static partial class ULongExtensions
return value % modulus;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool SharesFactorWithExponentMinusOneCpu(this ulong exponent)
{
ulong prime, value = exponent - 1UL;
@@ -1235,7 +1232,7 @@ public static partial class ULongExtensions
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool SharesFactorWithExponentMinusOneGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu)
{
ulong prime, value = exponent - 1UL;
@@ -1272,7 +1269,7 @@ public static partial class ULongExtensions
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool SharesFactorWithExponentMinusOneHybrid(this ulong exponent, PrimeOrderCalculatorAccelerator gpu)
{
ulong prime, value = exponent - 1UL;
+20
View File
@@ -34510,3 +34510,23 @@ p,divisor,searchedMersenne,detailedCheck,passedAllTests
10639,427251792503,True,True,False
20023,1235434157297,True,True,False
12497,347432096281,True,True,False
17539,1434748148857,True,True,False
5669,122398868791,True,True,False
12721,338895581441,True,True,False
15773,344578472209,True,True,False
16573,588828580471,True,True,False
5581,129359320121,True,True,False
17477,606383809609,True,True,False
19571,2844272257303,True,True,False
9433,444094320401,True,True,False
9769,296887940119,True,True,False
15629,284749721023,True,True,False
16519,2418552373423,True,True,False
7603,918910927273,True,True,False
20399,167108609,True,True,False
10729,290280712591,True,True,False
20411,40823,True,True,False
20441,406981291169,True,True,False
9511,1366035502967,True,True,False
2789,128664552119,True,True,False
20479,2948977,True,True,False
Can't render this file because it is too large.