diff --git a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs index 0a9fcbf..ea94b27 100644 --- a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs +++ b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs @@ -4,8 +4,6 @@ using ILGPU; using ILGPU.Runtime; using PerfectNumbers.Core; using System.Runtime.InteropServices; -using System.Numerics; - namespace PerfectNumbers.Core.Gpu; internal enum GpuPow2ModStatus @@ -18,7 +16,7 @@ internal enum GpuPow2ModStatus internal static class PrimeOrderGpuHeuristics { private static readonly ConcurrentDictionary OverflowedPrimes = new(); - private static readonly ConcurrentDictionary, GpuUInt128, ArrayView1D>> Pow2ModKernelCache = new(); + private static readonly ConcurrentDictionary, MontgomeryDivisorData, ArrayView1D>> Pow2ModKernelCache = new(); private static PrimeOrderGpuCapability s_capability = PrimeOrderGpuCapability.Default; internal static ConcurrentDictionary OverflowRegistry => OverflowedPrimes; @@ -108,7 +106,8 @@ internal static class PrimeOrderGpuHeuristics exponentBuffer.View.CopyFromCPU(ref MemoryMarshal.GetReference(exponents), exponents.Length); remainderBuffer.MemSetToZero(); - kernel(stream, exponents.Length, exponentBuffer.View, new GpuUInt128(prime), remainderBuffer.View); + MontgomeryDivisorData divisor = MontgomeryDivisorDataCache.Get(prime); + kernel(stream, exponents.Length, exponentBuffer.View, divisor, remainderBuffer.View); stream.Synchronize(); @@ -142,44 +141,24 @@ internal static class PrimeOrderGpuHeuristics return 0UL; } - ulong result = 1UL % modulus; - ulong baseValue = 2UL % modulus; - ulong remaining = exponent; - - while (remaining > 0UL) - { - if ((remaining & 1UL) != 0UL) - { - result = (ulong)(((UInt128)result * baseValue) % modulus); - } - - remaining >>= 1; - if (remaining == 0UL) - { - break; - } - - baseValue = (ulong)(((UInt128)baseValue * baseValue) % modulus); - } - - return result; + MontgomeryDivisorData divisor = MontgomeryDivisorDataCache.Get(modulus); + return exponent.Pow2MontgomeryModWindowed(divisor, keepMontgomery: false); } - private static Action, GpuUInt128, ArrayView1D> GetPow2ModKernel(Accelerator accelerator) + private static Action, MontgomeryDivisorData, ArrayView1D> GetPow2ModKernel(Accelerator accelerator) { return Pow2ModKernelCache.GetOrAdd(accelerator, static accel => { - var loaded = accel.LoadAutoGroupedStreamKernel, GpuUInt128, ArrayView1D>(Pow2ModKernel); + var loaded = accel.LoadAutoGroupedStreamKernel, MontgomeryDivisorData, ArrayView1D>(Pow2ModKernel); var kernel = KernelUtil.GetKernel(loaded); - return kernel.CreateLauncherDelegate, GpuUInt128, ArrayView1D>>(); + return kernel.CreateLauncherDelegate, MontgomeryDivisorData, ArrayView1D>>(); }); } - private static void Pow2ModKernel(Index1D index, ArrayView1D exponents, GpuUInt128 modulus, ArrayView1D remainders) + private static void Pow2ModKernel(Index1D index, ArrayView1D exponents, MontgomeryDivisorData divisor, ArrayView1D remainders) { ulong exponent = exponents[index]; - GpuUInt128 result = GpuUInt128.Pow2Mod(exponent, modulus); - remainders[index] = result.Low; + remainders[index] = exponent.Pow2MontgomeryModWindowed(divisor, keepMontgomery: false); } internal readonly record struct PrimeOrderGpuCapability(int ModulusBits, int ExponentBits) diff --git a/todo.txt b/todo.txt index 0f130aa..19278dc 100644 --- a/todo.txt +++ b/todo.txt @@ -1,2 +1 @@ -- Swap PrimeOrderGpuHeuristics.Pow2ModCpu for the existing Pow2MontgomeryModWindowed helper and plan the same windowed ladder for the GPU kernel implementation. - Extend the GPU numeric type probe to cover proposed custom types such as GpuUInt256 and GpuUInt512 once their definitions are available so we can plan the wide-order heuristics on the device.