Address windowed pow2 review feedback
This commit is contained in:
1 parent
481d1010e8
commit
da6ea25729
9 files changed
+17
-20
No files matched your search
@@ -21,7 +21,7 @@ internal static class DivisorKernels
|
|||||||
ReadOnlyGpuUInt128 readOnlyMod = divisor;
|
ReadOnlyGpuUInt128 readOnlyMod = divisor;
|
||||||
if (x == 0)
|
if (x == 0)
|
||||||
{
|
{
|
||||||
baseVal = GpuUInt128.Pow2Mod(exponent, in readOnlyMod); // Uses the eight-bit windowed ladder measured fastest in GPU benchmarks.
|
baseVal = GpuUInt128.Pow2Mod(exponent, in readOnlyMod);
|
||||||
result[0] = baseVal.High == 0UL && baseVal.Low == 1UL ? (byte)1 : (byte)0;
|
result[0] = baseVal.High == 0UL && baseVal.Low == 1UL ? (byte)1 : (byte)0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ internal static class DivisorKernels
|
|||||||
|
|
||||||
GpuUInt128 pow = baseVal;
|
GpuUInt128 pow = baseVal;
|
||||||
pow.ModPow(exponent / ux, in readOnlyMod);
|
pow.ModPow(exponent / ux, in readOnlyMod);
|
||||||
var part2 = GpuUInt128.Pow2Mod(exponent % ux, in readOnlyMod); // Windowed pow2 keeps the mixed-radix tail fast.
|
var part2 = GpuUInt128.Pow2Mod(exponent % ux, in readOnlyMod);
|
||||||
GpuUInt128 product = pow;
|
GpuUInt128 product = pow;
|
||||||
ReadOnlyGpuUInt128 part2ReadOnly = part2.AsReadOnly();
|
ReadOnlyGpuUInt128 part2ReadOnly = part2.AsReadOnly();
|
||||||
product.MulMod(in part2ReadOnly, in readOnlyMod);
|
product.MulMod(in part2ReadOnly, in readOnlyMod);
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ internal static class IncrementalKernels
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong phi64 = phi.Low;
|
ulong phi64 = phi.Low;
|
||||||
if (GpuUInt128.Pow2Mod(phi64, in readOnlyQ) != GpuUInt128.One) // Windowed pow2 keeps the totient check fast.
|
if (GpuUInt128.Pow2Mod(phi64, in readOnlyQ) != GpuUInt128.One)
|
||||||
{
|
{
|
||||||
orders[index] = 0UL;
|
orders[index] = 0UL;
|
||||||
return;
|
return;
|
||||||
@@ -192,7 +192,7 @@ internal static class IncrementalKernels
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong phi64 = phi.Low;
|
ulong phi64 = phi.Low;
|
||||||
if (GpuUInt128.Pow2Mod(phi64, in readOnlyQ) != GpuUInt128.One) // Windowed pow2 keeps the order probe aligned with benchmarks.
|
if (GpuUInt128.Pow2Mod(phi64, in readOnlyQ) != GpuUInt128.One)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ internal static class OrderKernels
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong phi64 = phi.Low;
|
ulong phi64 = phi.Low;
|
||||||
GpuUInt128 pow = GpuUInt128.Pow2Mod(phi64, in readOnlyQ); // Windowed pow2 keeps the cycle check aligned with benchmark wins.
|
GpuUInt128 pow = GpuUInt128.Pow2Mod(phi64, in readOnlyQ);
|
||||||
if (pow != GpuUInt128.One)
|
if (pow != GpuUInt128.One)
|
||||||
{
|
{
|
||||||
orders[index] = 0UL;
|
orders[index] = 0UL;
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ internal static class Pow2ModKernels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pow2Minus1Mod already routes through the windowed pow2 helper benchmarked as fastest for large divisors.
|
|
||||||
if (GpuUInt128.Pow2Minus1Mod(exponent, in readOnlyQ) != GpuUInt128.Zero)
|
if (GpuUInt128.Pow2Minus1Mod(exponent, in readOnlyQ) != GpuUInt128.Zero)
|
||||||
{
|
{
|
||||||
orders[index] = 0UL;
|
orders[index] = 0UL;
|
||||||
@@ -169,7 +168,7 @@ internal static class Pow2ModKernels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (GpuUInt128.Pow2Mod(exponent, in readOnlyQ) != GpuUInt128.One) // Windowed pow2 keeps the order guard aligned with GPU benchmarks.
|
if (GpuUInt128.Pow2Mod(exponent, in readOnlyQ) != GpuUInt128.One)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
|
|
||||||
var gpu = GpuContextPool.RentPreferred(preferCpu: false);
|
var gpu = GpuContextPool.RentPreferred(preferCpu: false);
|
||||||
var accelerator = gpu.Accelerator;
|
var accelerator = gpu.Accelerator;
|
||||||
var kernel = GetBatchKernel(accelerator); // Batch kernel already relies on the Mersenne squaring reducer; windowed pow2 helpers do not apply here.
|
var kernel = GetBatchKernel(accelerator);
|
||||||
|
|
||||||
var expBuffer = accelerator.Allocate1D<ulong>(count);
|
var expBuffer = accelerator.Allocate1D<ulong>(count);
|
||||||
ulong[] expArray = ArrayPool<ulong>.Shared.Rent(count);
|
ulong[] expArray = ArrayPool<ulong>.Shared.Rent(count);
|
||||||
@@ -479,7 +479,6 @@ public class MersenneNumberLucasLehmerGpuTester
|
|||||||
{
|
{
|
||||||
if ((exponent & 1UL) != 0UL)
|
if ((exponent & 1UL) != 0UL)
|
||||||
{
|
{
|
||||||
// The base varies per primitive-root candidate, so pow2-specific ladders do not apply.
|
|
||||||
result = MulMod(result, value, modulus);
|
result = MulMod(result, value, modulus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
|
|||||||
{
|
{
|
||||||
GpuKernelType.Pow2Mod => gpuLease.Pow2ModOrderKernel,
|
GpuKernelType.Pow2Mod => gpuLease.Pow2ModOrderKernel,
|
||||||
_ => gpuLease.IncrementalOrderKernel,
|
_ => gpuLease.IncrementalOrderKernel,
|
||||||
}; // Pow2ModOrderKernel already routes through the windowed pow2 helper measured fastest for large divisors.
|
};
|
||||||
// Both GPU order kernels already rely on the windowed pow2 helper, so this wrapper only selects the delegate
|
|
||||||
// and manages the scoped GPU resources.
|
|
||||||
|
|
||||||
var foundBuffer = accelerator.Allocate1D<int>(1); // TODO: Replace this allocation with the pooled device buffer from GpuOrderKernelBenchmarks so repeated scans reuse the pinned staging memory instead of allocating per run.
|
var foundBuffer = accelerator.Allocate1D<int>(1); // TODO: Replace this allocation with the pooled device buffer from GpuOrderKernelBenchmarks so repeated scans reuse the pinned staging memory instead of allocating per run.
|
||||||
exponent.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
exponent.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
||||||
|
|||||||
@@ -985,8 +985,6 @@ public class MersenneDivisorCycles
|
|||||||
private static ulong CalculateCycleLengthFallback(ulong divisor)
|
private static ulong CalculateCycleLengthFallback(ulong divisor)
|
||||||
{
|
{
|
||||||
// Otherwise, find order of 2 mod divisor using the shared unrolled-hex stepping sequence.
|
// Otherwise, find order of 2 mod divisor using the shared unrolled-hex stepping sequence.
|
||||||
// Divisors <= 3 hit this fallback, so the lightweight CPU path already matches the windowed pow2 helper
|
|
||||||
// without paying for a GPU round-trip.
|
|
||||||
return divisor.CalculateMersenneDivisorCycleLengthUnrolledHexCpu();
|
return divisor.CalculateMersenneDivisorCycleLengthUnrolledHexCpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -319,8 +319,6 @@ public sealed class MersenneNumberTester(
|
|||||||
private static void LegacyOrderKernel(Index1D index, ulong exponent, ArrayView<UInt128> qs, ArrayView<ulong> orders)
|
private static void LegacyOrderKernel(Index1D index, ulong exponent, ArrayView<UInt128> qs, ArrayView<ulong> orders)
|
||||||
{
|
{
|
||||||
UInt128 q = qs[index];
|
UInt128 q = qs[index];
|
||||||
// The legacy kernel already relies on the adaptive PowMod helper, which switches to the windowed pow2
|
|
||||||
// ladder for large divisors.
|
|
||||||
UInt128 pow = exponent.PowMod(q);
|
UInt128 pow = exponent.PowMod(q);
|
||||||
orders[index] = pow == 1UL ? exponent : 0UL;
|
orders[index] = pow == 1UL ? exponent : 0UL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,15 @@ public class MersenneResidueStepper
|
|||||||
m = modulus;
|
m = modulus;
|
||||||
p = p0;
|
p = p0;
|
||||||
|
|
||||||
// Callers hand this stepper pre-reduced residues from the windowed pow2 pipeline; keep the modulo guard for tests
|
// Inputs are already reduced in production; keep the old modulo guard commented for debugging reference.
|
||||||
// while production inputs already arrive inside [0, m).
|
// M_mod = M_mod_m_at_p0 % m;
|
||||||
M_mod = M_mod_m_at_p0 % m;
|
// pow2_mod = (M_mod + 1) % m;
|
||||||
pow2_mod = (M_mod + 1) % m;
|
M_mod = M_mod_m_at_p0;
|
||||||
|
pow2_mod = M_mod + 1;
|
||||||
|
if (pow2_mod >= m)
|
||||||
|
{
|
||||||
|
pow2_mod -= m;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Inline this accessor into callers so the hot residue path reads the backing fields directly
|
// TODO: Inline this accessor into callers so the hot residue path reads the backing fields directly
|
||||||
|
|||||||
Reference in new issue
Block a user