From 05d3061c47b1a04fdfdadf2ca6abbc10c026a436 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Wed, 10 Dec 2025 19:59:51 +0100 Subject: [PATCH] MethodImplOptions.AggressiveOptimization & MethodImplOptions.AggressiveInlining optimized. Small optimizations. --- .../HeuristicCombinedPrimeTesterTests.cs | 5 +- .../AscendingDivisorRemainderStepper.cs | 6 +- .../BidirectionalCycleRemainderStepper.cs | 6 +- .../Cpu/DeterministicRandomCpu.cs | 5 + .../ExponentRemainderStepperCpu.cs | 24 +- .../HeuristicCombinedGpuTables.cs | 6 + .../PrimeOrderCalculatorAccelerator.cs | 3 + PerfectNumbers.Core/Gpu/GpuKernelPool.cs | 2 + .../Gpu/Kernels/Pow2ModKernels.cs | 3 + .../Gpu/Kernels/Pow2MontgomeryKernels.cs | 8 +- .../Gpu/Kernels/PrimeOrderKernels.cs | 5 + .../Gpu/MersenneNumberResidueGpuTester.cs | 9 +- .../Gpu/Pow2MontgomeryGpuCalculator.cs | 4 +- .../Gpu/PrimeOrderGpuHeuristics.cs | 12 + .../HeuristicCombinedPrimeTester.cs | 86 +- .../MontgomeryDivisorDataPool.cs | 1 + .../PrimeOrderCalculator.Cpu.cs | 958 +++++++++--------- .../PrimeOrderCalculator.Gpu.cs | 8 +- PerfectNumbers.Core/PrimeOrderCalculator.cs | 1 + PerfectNumbers.Core/PrimeTesterByLastDigit.cs | 3 +- PerfectNumbers.Core/ULongExtensions.Cpu.cs | 12 +- PerfectNumbers.Core/ULongExtensions.Gpu.cs | 16 +- PerfectNumbers.Core/ULongExtensions.cs | 4 +- 23 files changed, 628 insertions(+), 559 deletions(-) diff --git a/PerfectNumbers.Core.Tests/HeuristicCombinedPrimeTesterTests.cs b/PerfectNumbers.Core.Tests/HeuristicCombinedPrimeTesterTests.cs index 1447ad6..0c424fb 100644 --- a/PerfectNumbers.Core.Tests/HeuristicCombinedPrimeTesterTests.cs +++ b/PerfectNumbers.Core.Tests/HeuristicCombinedPrimeTesterTests.cs @@ -12,10 +12,7 @@ public class HeuristicCombinedPrimeTesterTests [InlineData((byte)9)] public void Enumerator_uses_one_a_one_b_schedule_by_default(byte lastDigit) { - Span groupBState = - stackalloc HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState[4]; - - var enumerator = HeuristicCombinedPrimeTester.CreateHeuristicDivisorEnumerator(500UL * 500UL, lastDigit, groupBState); + var enumerator = HeuristicCombinedPrimeTester.CreateHeuristicDivisorEnumerator(500UL * 500UL, lastDigit); var results = new List(32); while (results.Count < 20 && enumerator.TryGetNext(out var candidate)) diff --git a/PerfectNumbers.Core/AscendingDivisorRemainderStepper.cs b/PerfectNumbers.Core/AscendingDivisorRemainderStepper.cs index 093b815..61f2b08 100644 --- a/PerfectNumbers.Core/AscendingDivisorRemainderStepper.cs +++ b/PerfectNumbers.Core/AscendingDivisorRemainderStepper.cs @@ -7,7 +7,7 @@ public struct AscendingDivisorRemainderStepper private readonly ulong _dividend; private ulong _maxDivisor; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] public AscendingDivisorRemainderStepper(ulong value) { _dividend = value; @@ -20,10 +20,10 @@ public struct AscendingDivisorRemainderStepper _maxDivisor = root; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public readonly bool ShouldContinue(ulong divisor) => divisor <= _maxDivisor; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public bool Divides(ulong divisor) { ulong remainder = _dividend; diff --git a/PerfectNumbers.Core/BidirectionalCycleRemainderStepper.cs b/PerfectNumbers.Core/BidirectionalCycleRemainderStepper.cs index 1320e2f..77ea982 100644 --- a/PerfectNumbers.Core/BidirectionalCycleRemainderStepper.cs +++ b/PerfectNumbers.Core/BidirectionalCycleRemainderStepper.cs @@ -10,21 +10,21 @@ public struct BidirectionalCycleRemainderStepper(ulong modulus) public readonly ulong Modulus { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] get { return _modulus; } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public void Initialize(ulong value) { _remainder = value.ReduceCycleRemainder(_modulus); _previous = value; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public ulong Step(ulong value) { long delta = unchecked((long)value - (long)_previous); diff --git a/PerfectNumbers.Core/Cpu/DeterministicRandomCpu.cs b/PerfectNumbers.Core/Cpu/DeterministicRandomCpu.cs index 3d04d1b..084d199 100644 --- a/PerfectNumbers.Core/Cpu/DeterministicRandomCpu.cs +++ b/PerfectNumbers.Core/Cpu/DeterministicRandomCpu.cs @@ -1,3 +1,5 @@ +using System.Runtime.CompilerServices; + namespace PerfectNumbers.Core.Cpu; internal static class DeterministicRandomCpu @@ -7,6 +9,7 @@ internal static class DeterministicRandomCpu private static long s_seedCounter; + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong NextUInt64() { ulong state = s_threadState; @@ -22,6 +25,7 @@ internal static class DeterministicRandomCpu return state * 2685821657736338717UL; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static UInt128 NextUInt128() { UInt128 high = (UInt128)NextUInt64(); @@ -29,6 +33,7 @@ internal static class DeterministicRandomCpu return (high << 64) | low; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong InitializeThreadState() { ulong seed = (ulong)Interlocked.Increment(ref s_seedCounter); diff --git a/PerfectNumbers.Core/ExponentRemainderStepperCpu.cs b/PerfectNumbers.Core/ExponentRemainderStepperCpu.cs index 7e03a3b..43b7b15 100644 --- a/PerfectNumbers.Core/ExponentRemainderStepperCpu.cs +++ b/PerfectNumbers.Core/ExponentRemainderStepperCpu.cs @@ -18,7 +18,7 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul private ulong _currentMontgomery = divisor.MontgomeryOne; private static readonly ConcurrentDictionary<(ulong Cycle, ulong Delta), ulong> _cycleDeltaCache = new(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public readonly bool MatchesDivisor(in MontgomeryDivisorData divisor, ulong cycleLength) => _modulus == divisor.Modulus && _nPrime == divisor.NPrime && _montgomeryOne == divisor.MontgomeryOne && _cycleLength == cycleLength; @@ -26,7 +26,7 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul /// Clears the cached Montgomery residue so the stepper can be reused for another divisor. /// Callers must reinitialize the stepper before consuming any residues after a reset. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public void Reset() { PreviousExponent = 0UL; @@ -39,7 +39,7 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul /// /// First exponent evaluated for the current divisor. The caller must reuse the same divisor metadata. /// The canonical residue produced by 2^exponent mod divisor. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public ulong InitializeCpu(ulong exponent) { InitializeCpuState(exponent); @@ -51,7 +51,7 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul /// /// First exponent evaluated for the current divisor. Must match the divisor passed to the constructor. /// when the seed exponent produces a Montgomery residue equal to one. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public bool InitializeCpuIsUnity(ulong exponent) { InitializeCpuState(exponent); @@ -64,7 +64,7 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul /// /// Next exponent in the ascending sequence. Callers must never move backwards or reuse an older exponent. /// The canonical residue produced by 2^exponent mod divisor. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public ulong ComputeNextCpu(ulong exponent) { ulong delta = exponent - PreviousExponent; @@ -84,7 +84,7 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul /// /// Next exponent in the ascending sequence. /// when the canonical residue equals one. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public bool ComputeNextIsUnity(ulong exponent) { ulong delta = exponent - PreviousExponent; @@ -105,8 +105,8 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul /// Residue in Montgomery form produced externally for the configured divisor. /// Outputs whether the supplied residue equals Montgomery one. /// when the residue is accepted and cached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryInitializeFromMontgomeryResult(ulong exponent, ulong montgomeryResult, out bool isUnity) + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public bool TryInitializeFromMontgomeryResult(ulong exponent, ulong montgomeryResult, out bool isUnity) { _currentMontgomery = montgomeryResult; PreviousExponent = exponent; @@ -122,7 +122,7 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul /// Montgomery residue delta corresponding to the exponent gap. /// Outputs whether the resulting residue equals Montgomery one. /// when the advance succeeds. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public bool TryAdvanceWithMontgomeryDelta(ulong exponent, ulong montgomeryDelta, out bool isUnity) { ulong delta = exponent - PreviousExponent; @@ -149,7 +149,7 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul return true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private void InitializeCpuState(ulong exponent) { ulong rotation = ReduceDelta(exponent); @@ -159,10 +159,10 @@ internal struct ExponentRemainderStepperCpu(in MontgomeryDivisorData divisor, ul PreviousExponent = exponent; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private readonly ulong ReduceCurrent() => _currentMontgomery.MontgomeryMultiplyCpu(1UL, _modulus, _nPrime); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private readonly ulong ReduceDelta(ulong delta) { ulong cycleLength = _cycleLength; diff --git a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicCombinedGpuTables.cs b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicCombinedGpuTables.cs index 349c8cd..d56f1e7 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/HeuristicCombinedGpuTables.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/HeuristicCombinedGpuTables.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Runtime; @@ -9,10 +10,13 @@ internal readonly struct HeuristicCombinedGpuTables private static readonly HeuristicCombinedGpuTables[] _sharedTables = new HeuristicCombinedGpuTables[PerfectNumberConstants.RollingAccelerators]; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal static void WarmUp(int acceleratorIndex, AcceleratorStream stream) => _sharedTables[acceleratorIndex] = new(_accelerators[acceleratorIndex], stream); + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static HeuristicCombinedGpuTables GetStaticTables(int acceleratorIndex) => _sharedTables[acceleratorIndex]; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal HeuristicCombinedGpuTables(Accelerator accelerator, AcceleratorStream stream) { var combinedEnding1 = HeuristicCombinedPrimeTester.CombinedDivisorsEnding1; @@ -45,6 +49,7 @@ internal readonly struct HeuristicCombinedGpuTables internal readonly MemoryBuffer1D HeuristicCombinedDivisorsEnding9; internal readonly MemoryBuffer1D HeuristicCombinedDivisorSquaresEnding9; + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal HeuristicCombinedGpuViews CreateViews() => new( HeuristicCombinedDivisorsEnding1.View, HeuristicCombinedDivisorsEnding3.View, @@ -55,6 +60,7 @@ internal readonly struct HeuristicCombinedGpuTables HeuristicCombinedDivisorSquaresEnding7.View, HeuristicCombinedDivisorSquaresEnding9.View); + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal void Dispose() { HeuristicCombinedDivisorsEnding1.Dispose(); diff --git a/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs index 1ba6153..ba5db3d 100644 --- a/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs +++ b/PerfectNumbers.Core/Gpu/Accelerators/PrimeOrderCalculatorAccelerator.cs @@ -195,6 +195,7 @@ public sealed class PrimeOrderCalculatorAccelerator public readonly Action, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView, ArrayView> SmallPrimeSieveKernelLauncher; public readonly Action, int, ulong, ArrayView> SpecialMaxKernelLauncher; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public void EnsureCalculateOrderWideCapacity(int newSize) { if (newSize > Input.Length) @@ -213,6 +214,7 @@ public sealed class PrimeOrderCalculatorAccelerator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public void EnsureCapacity(int factorsCount, int primeTesterCapacity) { Accelerator accelerator = Accelerator; @@ -325,6 +327,7 @@ public sealed class PrimeOrderCalculatorAccelerator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public void EnsureUlongInputOutputCapacity(int newSize) { var accelerator = Accelerator; diff --git a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs index ae856b6..bb6839a 100644 --- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu.Accelerators; @@ -74,6 +75,7 @@ public class GpuKernelPool }); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] static void InitPow2ModKernelScan(Accelerator accelerator, KernelContainer kernels) { InitOnce(ref kernels.Pow2Mod, () => diff --git a/PerfectNumbers.Core/Gpu/Kernels/Pow2ModKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/Pow2ModKernels.cs index 5dcd252..99f2536 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/Pow2ModKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/Pow2ModKernels.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Runtime; @@ -6,6 +7,7 @@ namespace PerfectNumbers.Core.Gpu; internal static class Pow2ModKernels { /// This kernel always sets the result of the corresponding element. Callers don't need to clear the output buffers. + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static void Pow2ModKernelScan( Index1D index, ulong exponent, @@ -107,6 +109,7 @@ internal static class Pow2ModKernels /// This kernel doesn't always set the result of the found array. It sets the value only when it needs to. The callers /// must clean the output buffer before the call to get a deterministic result. + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static void Pow2ModOrderKernelScan( Index1D index, ulong exponent, diff --git a/PerfectNumbers.Core/Gpu/Kernels/Pow2MontgomeryKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/Pow2MontgomeryKernels.cs index 3b09b99..fd441fd 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/Pow2MontgomeryKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/Pow2MontgomeryKernels.cs @@ -117,7 +117,7 @@ public static class Pow2MontgomeryKernels return sumHigh; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] private static void MultiplyPartsGpu(ulong left, ulong right, out ulong high, out ulong low) { ulong leftLow = (uint)left; @@ -135,13 +135,13 @@ public static class Pow2MontgomeryKernels high = unchecked(highProduct + (cross1 >> 32) + (cross2 >> 32) + (carry >> 32)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static ulong Pow2MontgomeryModWindowedGpuKeepMontgomery(in MontgomeryDivisorDataGpu divisor, ulong exponent) { return Pow2MontgomeryModWindowedGpuMontgomeryResult(divisor, exponent); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModWindowedGpuConvertToStandard(this MontgomeryDivisorDataGpu divisor, ulong exponent) { ulong modulus = divisor.Modulus; @@ -150,7 +150,7 @@ public static class Pow2MontgomeryKernels return MontgomeryMultiplyGpu(montgomeryResult, 1UL, modulus, nPrime); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong Pow2MontgomeryModWindowedGpuMontgomeryResult(in MontgomeryDivisorDataGpu divisor, ulong exponent) { ulong modulus = divisor.Modulus; diff --git a/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs b/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs index 951153d..79ca9a0 100644 --- a/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs +++ b/PerfectNumbers.Core/Gpu/Kernels/PrimeOrderKernels.cs @@ -1182,6 +1182,7 @@ internal static partial class PrimeOrderKernels return divisor.Pow2MontgomeryModWindowedGpuConvertToStandard(exponent) == 1UL; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong CalculateByDoublingKernel(ulong prime) { ulong order = 1UL; @@ -1203,6 +1204,7 @@ internal static partial class PrimeOrderKernels /// This kernel always sets the result of the corresponding element. Callers don't need to clear the output buffers. + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal static void Pow2ModKernel(Index1D index, ArrayView1D exponents, ulong divisorModulus, ulong divisorNPrime, ulong divisorMontgomeryOne, ulong divisorMontgomeryTwo, ulong divisorMontgomeryTwoSquared, ArrayView1D remainders) { ulong exponent = exponents[index]; @@ -1211,12 +1213,14 @@ internal static partial class PrimeOrderKernels } /// This kernel always sets the result of the corresponding element. Callers don't need to clear the output buffers. + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal static void Pow2ModKernelWide(Index1D index, ArrayView1D exponents, GpuUInt128 modulus, ArrayView1D remainders) { GpuUInt128 exponent = exponents[index]; remainders[index] = Pow2ModKernelCore(exponent, modulus); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static GpuUInt128[] InitializeOddPowersTable(GpuUInt128 baseValue, GpuUInt128 modulus, int oddPowerCount) { GpuUInt128[] result = new GpuUInt128[PerfectNumberConstants.MaxOddPowersCount]; @@ -1242,6 +1246,7 @@ internal static partial class PrimeOrderKernels return result; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal static GpuUInt128 Pow2ModKernelCore(GpuUInt128 exponent, GpuUInt128 modulus) { // This should never happen in production code. diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs index 01ad758..997abf3 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberResidueGpuTester.cs @@ -23,6 +23,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) // requested by the caller, skip queuing additional block generation, and keep the snapshot cache untouched. // GPU residue variant: check 2^p % q == 1 for q = 2*p*k + 1. + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public void Scan(PrimeOrderCalculatorAccelerator gpu, ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime) { // GpuPrimeWorkLimiter.Acquire(); @@ -114,6 +115,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) // GpuPrimeWorkLimiter.Release(); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static Action, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D> GetPow2ModKernel(Accelerator accelerator) { @@ -128,6 +130,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) return cached; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static Action, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D> CreatePow2ModKernel(Accelerator accelerator) { var loaded = accelerator.LoadAutoGroupedStreamKernel, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D, ArrayView1D>(Pow2ModKernels.Pow2ModKernelScan); @@ -140,6 +143,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ResidueResources RentResources(Accelerator accelerator, int capacity) { var resourcePools = _resourcePools ??= new(); @@ -152,6 +156,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) return new ResidueResources(accelerator, capacity); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void ReturnResources(ResidueResources resources) { var resourcePools = _resourcePools ??= new(); @@ -160,6 +165,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) private sealed class ResidueResources { + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal ResidueResources(Accelerator accelerator, int capacity) { Capacity = Math.Max(1, capacity); @@ -171,6 +177,7 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) OrderArray = ThreadStaticPools.UlongPool.Rent(Capacity); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal void EnsureCapacity(Accelerator accelerator, int capacity) { if (OrderBuffer.Length >= capacity) @@ -213,4 +220,4 @@ public class MersenneNumberResidueGpuTester(bool useGpuOrder) } -} \ No newline at end of file +} diff --git a/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs b/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs index da85329..4cae83f 100644 --- a/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs +++ b/PerfectNumbers.Core/Gpu/Pow2MontgomeryGpuCalculator.cs @@ -7,7 +7,7 @@ namespace PerfectNumbers.Core.Gpu; public static class Pow2MontgomeryGpuCalculator { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong CalculateKeep(PrimeOrderCalculatorAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor) { // GpuPrimeWorkLimiter.Acquire(); @@ -34,7 +34,7 @@ public static class Pow2MontgomeryGpuCalculator return result; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong CalculateConvert(PrimeOrderCalculatorAccelerator gpu, ulong exponent, in MontgomeryDivisorData divisor) { ulong result = 0UL; diff --git a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs index 4528823..d64e9db 100644 --- a/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs +++ b/PerfectNumbers.Core/Gpu/PrimeOrderGpuHeuristics.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Collections.Concurrent; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu.Accelerators; @@ -16,6 +17,7 @@ internal enum GpuPow2ModStatus internal static partial class PrimeOrderGpuHeuristics { + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static void PreloadStaticTables(AcceleratorStream stream) { // _ = GetSmallPrimeDeviceCache(stream); @@ -31,16 +33,19 @@ internal static partial class PrimeOrderGpuHeuristics internal static ConcurrentDictionary OverflowRegistry => OverflowedPrimes; internal static ConcurrentDictionary OverflowRegistryWide => OverflowedPrimesWide; + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static void OverrideCapabilitiesForTesting(PrimeOrderGpuCapability capability) { s_capability = capability; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static void ResetCapabilitiesForTesting() { s_capability = PrimeOrderGpuCapability.Default; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static bool TryPartialFactor( PrimeOrderCalculatorAccelerator gpu, ulong value, @@ -88,6 +93,7 @@ internal static partial class PrimeOrderGpuHeuristics private static readonly Accelerator[] _accelerators = AcceleratorPool.Shared.Accelerators; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryLaunchPartialFactorKernel( PrimeOrderCalculatorAccelerator gpu, ulong value, @@ -156,6 +162,7 @@ internal static partial class PrimeOrderGpuHeuristics return true; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static GpuPow2ModStatus TryPow2Mod(PrimeOrderCalculatorAccelerator gpu, ulong exponent, ulong prime, out ulong remainder, in MontgomeryDivisorData? divisorData) { Span exponents = stackalloc ulong[1]; @@ -167,6 +174,7 @@ internal static partial class PrimeOrderGpuHeuristics return status; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static GpuPow2ModStatus TryPow2ModBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, ulong prime, Span remainders, in MontgomeryDivisorData? divisorData) { // This will never occur in production code @@ -222,6 +230,7 @@ internal static partial class PrimeOrderGpuHeuristics return computed ? GpuPow2ModStatus.Success : GpuPow2ModStatus.Unavailable; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static GpuPow2ModStatus TryPow2Mod(PrimeOrderCalculatorAccelerator gpu, in UInt128 exponent, in UInt128 prime, out UInt128 remainder) { Span exponents = stackalloc UInt128[1]; @@ -233,11 +242,13 @@ internal static partial class PrimeOrderGpuHeuristics return status; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static GpuPow2ModStatus TryPow2ModBatch(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, UInt128 prime, Span remainders) { return TryPow2ModBatchInternal(gpu, exponents, prime, remainders); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] internal static bool TryCalculateOrder( PrimeOrderCalculatorAccelerator gpu, ulong prime, @@ -310,6 +321,7 @@ internal static partial class PrimeOrderGpuHeuristics return order != 0UL; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static GpuPow2ModStatus TryPow2ModBatchInternal(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan exponents, UInt128 prime, Span remainders) { if (exponents.Length == 0) diff --git a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs index 51ee784..acf95c1 100644 --- a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs @@ -41,6 +41,7 @@ public sealed class HeuristicCombinedPrimeTester CombinedDivisorsEnding9Squares = CombinedDivisorsEnding9OneAOneBSquares; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void EnsureInitialized() { // Intentionally left blank. Accessing this method forces the static constructor to run. @@ -155,9 +156,10 @@ public sealed class HeuristicCombinedPrimeTester public static HeuristicCombinedPrimeTester Exclusive => _tester ??= new(); + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static bool IsPrimeCpu(ulong n) { - byte nMod10 = (byte)n.Mod10(); + byte nMod10 = n.Mod10(); ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n); if (maxDivisorSquare < 9UL) @@ -168,13 +170,15 @@ public sealed class HeuristicCombinedPrimeTester return HeuristicTrialDivisionCpu(n, maxDivisorSquare, nMod10); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n) { - byte nMod10 = (byte)n.Mod10(); + byte nMod10 = n.Mod10(); ulong maxDivisorSquare = ComputeHeuristicDivisorSquareLimit(n); return IsPrimeGpu(gpu, n, maxDivisorSquare, nMod10); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static bool IsPrimeGpu(PrimeOrderCalculatorAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10) { // TODO: Is this condition ever met on the execution path in EvenPerfectBitScanner? @@ -187,6 +191,7 @@ public sealed class HeuristicCombinedPrimeTester return !compositeDetected; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool HeuristicTrialDivisionCpu(ulong n, ulong maxDivisorSquare, byte nMod10) { ReadOnlySpan combinedDivisors = GetCombinedDivisors(nMod10); @@ -211,7 +216,7 @@ public sealed class HeuristicCombinedPrimeTester return EvaluateWithOpenNumericFallback(n); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static int GetBatchSize(ulong n) => n switch { < 1_000 => 8, @@ -222,6 +227,7 @@ public sealed class HeuristicCombinedPrimeTester }; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool HeuristicTrialDivisionGpuDetectsDivisor(PrimeOrderCalculatorAccelerator gpu, ulong n, ulong maxDivisorSquare, byte nMod10) { var acceleratorIndex = gpu.AcceleratorIndex; @@ -254,13 +260,13 @@ public sealed class HeuristicCombinedPrimeTester } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static bool EvaluateWithOpenNumericFallback(ulong n) => Prime.Numbers.IsPrime(n); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static ulong ComputeHeuristicDivisorSquareLimit(ulong n) => n; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ReadOnlySpan GetCombinedDivisors(byte nMod10) => nMod10 switch { 1 => CombinedDivisorsEnding1, @@ -270,7 +276,7 @@ public sealed class HeuristicCombinedPrimeTester _ => throw new InvalidOperationException($"Unsupported combined divisor selector for digit {nMod10}."), }; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ReadOnlySpan GetCombinedDivisorSquares(byte nMod10) => nMod10 switch { 1 => CombinedDivisorsEnding1Squares, @@ -280,7 +286,7 @@ public sealed class HeuristicCombinedPrimeTester _ => throw new InvalidOperationException($"Unsupported combined divisor square selector for digit {nMod10}."), }; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static ReadOnlySpan GetCombinedDivisors(byte nMod10, CombinedDivisorPattern pattern) => pattern switch { CombinedDivisorPattern.TwoAOneB => nMod10 switch @@ -318,7 +324,7 @@ public sealed class HeuristicCombinedPrimeTester _ => throw new InvalidOperationException($"Unsupported combined divisor pattern: {pattern}."), }; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static ReadOnlySpan GetCombinedDivisorSquares(byte nMod10, CombinedDivisorPattern pattern) => pattern switch { CombinedDivisorPattern.TwoAOneB => nMod10 switch @@ -428,6 +434,7 @@ public sealed class HeuristicCombinedPrimeTester return result; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static int GetGroupBStartIndex(ReadOnlySpan divisors) { int index = 0; @@ -439,8 +446,10 @@ public sealed class HeuristicCombinedPrimeTester return index; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static bool IsGroupAPrefixValue(ulong value) => value is 3UL or 7UL or 11UL or 13UL; + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static HeuristicDivisorGroup ResolveGroup(ulong value) => value switch { 3UL or 7UL or 11UL or 13UL => HeuristicDivisorGroup.GroupAConstant, @@ -448,6 +457,7 @@ public sealed class HeuristicCombinedPrimeTester _ => HeuristicDivisorGroup.GroupB, }; + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ReadOnlySpan GetGroupBDivisors(byte ending) => ending switch { 1 => DivisorGenerator.SmallPrimesLastOneWithoutLastThree, @@ -457,19 +467,19 @@ public sealed class HeuristicCombinedPrimeTester _ => [], }; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span groupBBuffer) - => new(maxDivisorSquare, nMod10, groupBBuffer); + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + internal static HeuristicDivisorEnumerator CreateHeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10) + => new(maxDivisorSquare, nMod10); internal struct HeuristicGroupBSequenceState { } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static MersenneHeuristicDivisorEnumerator CreateMersenneDivisorEnumerator(ulong exponent, ulong maxDivisor) => new(exponent, maxDivisor); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static HeuristicDivisorPreparation PrepareHeuristicDivisor(in HeuristicDivisorCandidate candidate) { ulong divisor = candidate.Value; @@ -478,7 +488,7 @@ public sealed class HeuristicCombinedPrimeTester return new(candidate, divisorData, cycleLength, hasCycleHint); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static ulong ResolveHeuristicCycleLengthCpu( ulong exponent, in HeuristicDivisorPreparation preparation, @@ -519,7 +529,7 @@ public sealed class HeuristicCombinedPrimeTester return resolvedCycle; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static ulong ResolveHeuristicCycleLengthGpu( PrimeOrderCalculatorAccelerator gpu, ulong exponent, @@ -561,7 +571,7 @@ public sealed class HeuristicCombinedPrimeTester return resolvedCycle; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static bool TryGetCycleLengthHint(ulong divisor, out ulong cycleLength) { ulong[] snapshot = HeuristicSmallCycleSnapshot; @@ -578,35 +588,26 @@ public sealed class HeuristicCombinedPrimeTester return false; } - internal ref struct HeuristicDivisorEnumerator + internal ref struct HeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10) { - private readonly ulong maxDivisorSquare; - private readonly ReadOnlySpan combinedDivisors; - private readonly ReadOnlySpan combinedDivisorSquares; - private int index; - - public HeuristicDivisorEnumerator(ulong maxDivisorSquare, byte nMod10, Span groupBBuffer) - { - this.maxDivisorSquare = maxDivisorSquare; - _ = groupBBuffer; - combinedDivisors = GetCombinedDivisors(nMod10); - combinedDivisorSquares = GetCombinedDivisorSquares(nMod10); - index = 0; - } + private readonly ulong _maxDivisorSquare = maxDivisorSquare; + private readonly ReadOnlySpan _combinedDivisors = GetCombinedDivisors(nMod10); + private readonly ReadOnlySpan _combinedDivisorSquares = GetCombinedDivisorSquares(nMod10); + private int index = 0; public bool TryGetNext(out HeuristicDivisorCandidate candidate) { - while (index < combinedDivisors.Length) + while (index < _combinedDivisors.Length) { int currentIndex = index; - ulong divisorSquare = combinedDivisorSquares[currentIndex]; - if (divisorSquare > maxDivisorSquare) + ulong divisorSquare = _combinedDivisorSquares[currentIndex]; + if (divisorSquare > _maxDivisorSquare) { - index = combinedDivisors.Length; + index = _combinedDivisors.Length; break; } - ulong entry = combinedDivisors[currentIndex]; + ulong entry = _combinedDivisors[currentIndex]; index = currentIndex + 1; HeuristicDivisorGroup group = ResolveGroup(entry); @@ -629,8 +630,6 @@ public sealed class HeuristicCombinedPrimeTester private GpuUInt128 current; private MersenneDivisorResidueStepper residueStepper; private bool active; - private ulong processedCount; - private ulong lastDivisor; public MersenneHeuristicDivisorEnumerator(ulong exponent, ulong maxDivisor) { @@ -649,8 +648,6 @@ public sealed class HeuristicCombinedPrimeTester current = hasCandidates ? firstDivisor : GpuUInt128.Zero; residueStepper = hasCandidates ? new MersenneDivisorResidueStepper(exponent, stepLocal, firstDivisor) : default; active = hasCandidates; - processedCount = 0UL; - lastDivisor = 0UL; } public bool TryGetNext(out HeuristicDivisorCandidate candidate) @@ -658,9 +655,6 @@ public sealed class HeuristicCombinedPrimeTester while (active) { ulong value = current.Low; - processedCount++; - lastDivisor = value; - bool admissible = residueStepper.IsAdmissible(); Advance(); @@ -691,12 +685,6 @@ public sealed class HeuristicCombinedPrimeTester residueStepper.Advance(); } - public readonly ulong ProcessedCount => processedCount; - - public readonly ulong LastDivisor => lastDivisor; - - public readonly bool Exhausted => !active; - private static HeuristicDivisorCandidate CreateCandidate(ulong value) { byte ending = (byte)(value % 10UL); diff --git a/PerfectNumbers.Core/MontgomeryDivisorDataPool.cs b/PerfectNumbers.Core/MontgomeryDivisorDataPool.cs index 26bf81a..6e6b80a 100644 --- a/PerfectNumbers.Core/MontgomeryDivisorDataPool.cs +++ b/PerfectNumbers.Core/MontgomeryDivisorDataPool.cs @@ -60,6 +60,7 @@ public static class MontgomeryDivisorDataPool montgomeryTwoSquared); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MontgomeryDivisorData Rent(this Queue queue, ulong modulus, ulong nPrime, ulong montgomeryOne, ulong montgomeryTwo, ulong montgomeryTwoSquared) { if (queue.TryDequeue(out var pooled)) diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs index 00a9644..c0cdd1e 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Cpu.cs @@ -18,7 +18,7 @@ internal static partial class PrimeOrderCalculator private static PendingEntry? _poolHead; private PendingEntry? _next; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static PendingEntry Rent(ulong value, bool knownComposite) { var poolHead = _poolHead; @@ -35,7 +35,7 @@ internal static partial class PrimeOrderCalculator return poolHead; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static void Return(PendingEntry entry) { entry._next = _poolHead; @@ -46,8 +46,8 @@ internal static partial class PrimeOrderCalculator public bool KnownComposite = knownComposite; public bool HasKnownPrimality = knownComposite; public bool IsPrime = false; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public void WithPrimality(bool isPrime) { KnownComposite = KnownComposite || !isPrime; @@ -125,33 +125,35 @@ internal static partial class PrimeOrderCalculator return result; } - private static UInt128 CalculateWideInternalCpu(in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData? divisorData, in PrimeOrderCalculatorConfig config) - { - if (prime <= UInt128.One) - { - return UInt128.One; - } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static UInt128 CalculateWideInternalCpu(in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData? divisorData, in PrimeOrderCalculatorConfig config) + { + if (prime <= UInt128.One) + { + return UInt128.One; + } - if (prime == (UInt128)3UL) - { - return (UInt128)2UL; - } + if (prime == (UInt128)3UL) + { + return (UInt128)2UL; + } - UInt128 phi = prime - UInt128.One; - PartialFactorResult128 phiFactors = PartialFactorWide(phi, config); - UInt128 result; - if (phiFactors.Factors is null) - { - result = FinishStrictlyWideCpu(prime, divisorData); - } - else - { - result = RunHeuristicPipelineWideCpu(prime, previousOrder, divisorData, config, phi, phiFactors); - } + UInt128 phi = prime - UInt128.One; + PartialFactorResult128 phiFactors = PartialFactorWide(phi, config); + UInt128 result; + if (phiFactors.Factors is null) + { + result = FinishStrictlyWideCpu(prime, divisorData); + } + else + { + result = RunHeuristicPipelineWideCpu(prime, previousOrder, divisorData, config, phi, phiFactors); + } - return result; - } + return result; + } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong RunHeuristicPipelineCpu( ulong prime, ulong? previousOrder, @@ -179,7 +181,7 @@ internal static partial class PrimeOrderCalculator return CalculateByFactorizationCpu(prime, divisorData); } - if (TryHeuristicFinishCpu(prime, candidateOrder, previousOrder, divisorData, config, phiFactors, orderFactors, out ulong order)) + if (TryHeuristicFinishCpu(prime, candidateOrder, previousOrder, divisorData, config, orderFactors, out ulong order)) { return order; } @@ -187,6 +189,7 @@ internal static partial class PrimeOrderCalculator return candidateOrder; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong RunHeuristicPipelineHybrid( PrimeOrderCalculatorAccelerator gpu, ulong prime, @@ -223,306 +226,314 @@ internal static partial class PrimeOrderCalculator return candidateOrder; } - private static UInt128 RunHeuristicPipelineWideCpu( - in UInt128 prime, - in UInt128? previousOrder, - in MontgomeryDivisorData? divisorData, - in PrimeOrderCalculatorConfig config, - in UInt128 phi, - in PartialFactorResult128 phiFactors) - { - if (phiFactors.FullyFactored && TrySpecialMaxWideCpu(phi, prime, divisorData, phiFactors)) - { - return phi; - } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static UInt128 RunHeuristicPipelineWideCpu( + in UInt128 prime, + in UInt128? previousOrder, + in MontgomeryDivisorData? divisorData, + in PrimeOrderCalculatorConfig config, + in UInt128 phi, + in PartialFactorResult128 phiFactors) + { + if (phiFactors.FullyFactored && TrySpecialMaxWideCpu(phi, prime, divisorData, phiFactors)) + { + return phi; + } - UInt128 candidateOrder = InitializeStartingOrderWideCpu(prime, phi, divisorData); - candidateOrder = ExponentLoweringWideCpu(candidateOrder, prime, divisorData, phiFactors); + UInt128 candidateOrder = InitializeStartingOrderWideCpu(prime, phi, divisorData); + candidateOrder = ExponentLoweringWideCpu(candidateOrder, prime, divisorData, phiFactors); - if (TryConfirmOrderWideCpu(prime, candidateOrder, divisorData, config)) - { - return candidateOrder; - } + if (TryConfirmOrderWideCpu(prime, candidateOrder, divisorData, config)) + { + return candidateOrder; + } - if (config.StrictMode) - { - return FinishStrictlyWideCpu(prime, divisorData); - } + if (config.StrictMode) + { + return FinishStrictlyWideCpu(prime, divisorData); + } - if (TryHeuristicFinishWideCpu(prime, candidateOrder, previousOrder, divisorData, config, out UInt128 order)) - { - return order; - } + if (TryHeuristicFinishWideCpu(prime, candidateOrder, previousOrder, divisorData, config, out UInt128 order)) + { + return order; + } - return candidateOrder; - } + return candidateOrder; + } - private static UInt128 FinishStrictlyWideCpu(in UInt128 prime, in MontgomeryDivisorData? divisorData) - { - UInt128 phi = prime - UInt128.One; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static UInt128 FinishStrictlyWideCpu(in UInt128 prime, in MontgomeryDivisorData? divisorData) + { + UInt128 phi = prime - UInt128.One; Queue> uInt128IntDictionaryPool = ThreadStaticPools.UInt128IntDictionaryPool; Dictionary counts = uInt128IntDictionaryPool.Rent(capacity: 8); - FactorCompletelyWide(phi, counts); - int entryCount = counts.Count; - if (entryCount == 0) - { + FactorCompletelyWide(phi, counts); + int entryCount = counts.Count; + if (entryCount == 0) + { uInt128IntDictionaryPool.Return(counts); - return phi; - } + return phi; + } Queue>> keyValuePairUInt128IntegerPool = ThreadStaticPools.KeyValuePairUInt128IntegerPool; var entries = keyValuePairUInt128IntegerPool.Rent(entryCount); entries.AddRange(counts); uInt128IntDictionaryPool.Return(counts); - + entries.Sort(static (a, b) => a.Key.CompareTo(b.Key)); - UInt128 order = phi; - for (int i = 0; i < entryCount; i++) - { - UInt128 primeFactor = entries[i].Key; - int exponent = entries[i].Value; - for (int iteration = 0; iteration < exponent; iteration++) - { - if (order.ReduceCycleRemainder(primeFactor) != UInt128.Zero) - { - UInt128 candidate = order / primeFactor; - if (Pow2ModWideCpu(candidate, prime, divisorData) == UInt128.One) - { - order = candidate; - } - else - { - break; - } - } - } - } + UInt128 order = phi; + for (int i = 0; i < entryCount; i++) + { + UInt128 primeFactor = entries[i].Key; + int exponent = entries[i].Value; + for (int iteration = 0; iteration < exponent; iteration++) + { + if (order.ReduceCycleRemainder(primeFactor) != UInt128.Zero) + { + UInt128 candidate = order / primeFactor; + if (Pow2ModWideCpu(candidate, prime, divisorData) == UInt128.One) + { + order = candidate; + } + else + { + break; + } + } + } + } keyValuePairUInt128IntegerPool.Return(entries); - return order; - } + return order; + } - private static bool TrySpecialMaxWideCpu(in UInt128 phi, in UInt128 prime, in MontgomeryDivisorData? divisorData, in PartialFactorResult128 factors) - { - ReadOnlySpan factorSpan = factors.Factors; - int length = factors.Count; - for (int i = 0; i < length; i++) - { - UInt128 factor = factorSpan[i].Value; - UInt128 reduced = phi / factor; - if (Pow2ModWideCpu(reduced, prime, divisorData) == UInt128.One) - { - return false; - } - } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static bool TrySpecialMaxWideCpu(in UInt128 phi, in UInt128 prime, in MontgomeryDivisorData? divisorData, in PartialFactorResult128 factors) + { + ReadOnlySpan factorSpan = factors.Factors; + int length = factors.Count; + for (int i = 0; i < length; i++) + { + UInt128 factor = factorSpan[i].Value; + UInt128 reduced = phi / factor; + if (Pow2ModWideCpu(reduced, prime, divisorData) == UInt128.One) + { + return false; + } + } - return true; - } + return true; + } - private static UInt128 InitializeStartingOrderWideCpu(in UInt128 prime, in UInt128 phi, in MontgomeryDivisorData? divisorData) - { - UInt128 order = phi; - UInt128 mod8 = prime & (UInt128)7UL; - if (mod8 == UInt128.One || mod8 == (UInt128)7UL) - { - UInt128 half = phi >> 1; - if (Pow2ModWideCpu(half, prime, divisorData) == UInt128.One) - { - order = half; - } - } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static UInt128 InitializeStartingOrderWideCpu(in UInt128 prime, in UInt128 phi, in MontgomeryDivisorData? divisorData) + { + UInt128 order = phi; + UInt128 mod8 = prime & (UInt128)7UL; + if (mod8 == UInt128.One || mod8 == (UInt128)7UL) + { + UInt128 half = phi >> 1; + if (Pow2ModWideCpu(half, prime, divisorData) == UInt128.One) + { + order = half; + } + } - return order; - } + return order; + } - private static UInt128 ExponentLoweringWideCpu(UInt128 order, in UInt128 prime, in MontgomeryDivisorData? divisorData, in PartialFactorResult128 factors) - { - ArrayPool pool = ThreadStaticPools.FactorEntry128Pool; - ReadOnlySpan factorSpan = factors.Factors; - int length = factors.Count; - // TODO: This should never trigger from production code - check - // if (length == 0) - // { - // return order; - // } - FactorEntry128[] tempArray = pool.Rent(length + 1); - Span buffer = tempArray.AsSpan(0, length); + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static UInt128 ExponentLoweringWideCpu(UInt128 order, in UInt128 prime, in MontgomeryDivisorData? divisorData, in PartialFactorResult128 factors) + { + ArrayPool pool = ThreadStaticPools.FactorEntry128Pool; + ReadOnlySpan factorSpan = factors.Factors; + int length = factors.Count; + // TODO: This should never trigger from production code - check + // if (length == 0) + // { + // return order; + // } + FactorEntry128[] tempArray = pool.Rent(length + 1); + Span buffer = tempArray.AsSpan(0, length); - factorSpan.CopyTo(buffer); - if (!factors.FullyFactored && factors.Cofactor > UInt128.One && IsPrimeWide(factors.Cofactor)) - { - buffer[length] = new FactorEntry128(factors.Cofactor, 1); - length++; - } + factorSpan.CopyTo(buffer); + if (!factors.FullyFactored && factors.Cofactor > UInt128.One && IsPrimeWide(factors.Cofactor)) + { + buffer[length] = new FactorEntry128(factors.Cofactor, 1); + length++; + } - buffer[..length].Sort(static (a, b) => a.Value.CompareTo(b.Value)); + buffer[..length].Sort(static (a, b) => a.Value.CompareTo(b.Value)); - for (int i = 0; i < length; i++) - { - UInt128 primeFactor = buffer[i].Value; - int exponent = buffer[i].Exponent; - for (int iteration = 0; iteration < exponent; iteration++) - { - if (order.ReduceCycleRemainder(primeFactor) == UInt128.Zero) - { - UInt128 reduced = order / primeFactor; - if (Pow2ModWideCpu(reduced, prime, divisorData) == UInt128.One) - { - order = reduced; - continue; - } - } + for (int i = 0; i < length; i++) + { + UInt128 primeFactor = buffer[i].Value; + int exponent = buffer[i].Exponent; + for (int iteration = 0; iteration < exponent; iteration++) + { + if (order.ReduceCycleRemainder(primeFactor) == UInt128.Zero) + { + UInt128 reduced = order / primeFactor; + if (Pow2ModWideCpu(reduced, prime, divisorData) == UInt128.One) + { + order = reduced; + continue; + } + } - break; - } - } + break; + } + } - pool.Return(tempArray, clearArray: false); - return order; - } + pool.Return(tempArray, clearArray: false); + return order; + } - private static bool TryConfirmOrderWideCpu(in UInt128 prime, in UInt128 order, in MontgomeryDivisorData? divisorData, in PrimeOrderCalculatorConfig config) - { - if (order == UInt128.Zero) - { - return false; - } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static bool TryConfirmOrderWideCpu(in UInt128 prime, in UInt128 order, in MontgomeryDivisorData? divisorData, in PrimeOrderCalculatorConfig config) + { + if (order == UInt128.Zero) + { + return false; + } - if (Pow2ModWideCpu(order, prime, divisorData) != UInt128.One) - { - return false; - } + if (Pow2ModWideCpu(order, prime, divisorData) != UInt128.One) + { + return false; + } - PartialFactorResult128 factorization = PartialFactorWide(order, config); - if (factorization.Factors is null) - { - return false; - } + PartialFactorResult128 factorization = PartialFactorWide(order, config); + if (factorization.Factors is null) + { + return false; + } - if (!factorization.FullyFactored) - { - if (factorization.Cofactor <= UInt128.One) - { - return false; - } + if (!factorization.FullyFactored) + { + if (factorization.Cofactor <= UInt128.One) + { + return false; + } - if (!IsPrimeWide(factorization.Cofactor)) - { - return false; - } + if (!IsPrimeWide(factorization.Cofactor)) + { + return false; + } - factorization = factorization.WithAdditionalPrime(factorization.Cofactor); - } + factorization = factorization.WithAdditionalPrime(factorization.Cofactor); + } - ReadOnlySpan span = factorization.Factors; - int length = factorization.Count; - for (int i = 0; i < length; i++) - { - UInt128 primeFactor = span[i].Value; - UInt128 reduced = order; - for (int iteration = 0; iteration < span[i].Exponent; iteration++) - { - if (reduced.ReduceCycleRemainder(primeFactor) != UInt128.Zero) - { - break; - } + ReadOnlySpan span = factorization.Factors; + int length = factorization.Count; + for (int i = 0; i < length; i++) + { + UInt128 primeFactor = span[i].Value; + UInt128 reduced = order; + for (int iteration = 0; iteration < span[i].Exponent; iteration++) + { + if (reduced.ReduceCycleRemainder(primeFactor) != UInt128.Zero) + { + break; + } - reduced /= primeFactor; - if (Pow2ModWideCpu(reduced, prime, divisorData) == UInt128.One) - { - return false; - } - } - } + reduced /= primeFactor; + if (Pow2ModWideCpu(reduced, prime, divisorData) == UInt128.One) + { + return false; + } + } + } - return true; - } + return true; + } - private static bool TryHeuristicFinishWideCpu( - in UInt128 prime, - in UInt128 order, - in UInt128? previousOrder, - in MontgomeryDivisorData? divisorData, - in PrimeOrderCalculatorConfig config, - out UInt128 result) - { - result = UInt128.Zero; - if (order <= UInt128.One) - { - return false; - } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static bool TryHeuristicFinishWideCpu( + in UInt128 prime, + in UInt128 order, + in UInt128? previousOrder, + in MontgomeryDivisorData? divisorData, + in PrimeOrderCalculatorConfig config, + out UInt128 result) + { + result = UInt128.Zero; + if (order <= UInt128.One) + { + return false; + } - PartialFactorResult128 orderFactors = PartialFactorWide(order, config); - if (orderFactors.Factors is null) - { - return false; - } + PartialFactorResult128 orderFactors = PartialFactorWide(order, config); + if (orderFactors.Factors is null) + { + return false; + } - if (!orderFactors.FullyFactored) - { - if (orderFactors.Cofactor <= UInt128.One) - { - return false; - } + if (!orderFactors.FullyFactored) + { + if (orderFactors.Cofactor <= UInt128.One) + { + return false; + } - if (!IsPrimeWide(orderFactors.Cofactor)) - { - return false; - } + if (!IsPrimeWide(orderFactors.Cofactor)) + { + return false; + } - orderFactors = orderFactors.WithAdditionalPrime(orderFactors.Cofactor); - } + orderFactors = orderFactors.WithAdditionalPrime(orderFactors.Cofactor); + } - int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecks * 4; + int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecksCapacity; Queue> uInt128ListPool = ThreadStaticPools.UInt128ListPool; List candidates = uInt128ListPool.Rent(capacity); - FactorEntry128[] factorArray = orderFactors.Factors!; + FactorEntry128[] factorArray = orderFactors.Factors!; - BuildCandidatesWide(order, factorArray, orderFactors.Count, candidates, capacity); + BuildCandidatesWide(order, factorArray, orderFactors.Count, candidates, capacity); - int candidateCount = candidates.Count; - if (candidateCount == 0) - { + int candidateCount = candidates.Count; + if (candidateCount == 0) + { uInt128ListPool.Return(candidates); - return false; - } + return false; + } - SortCandidatesWide(prime, previousOrder, candidates); + SortCandidatesWide(prime, previousOrder, candidates); - int powBudget = config.MaxPowChecks <= 0 ? candidateCount : config.MaxPowChecks; - int powUsed = 0; + int powBudget = config.MaxPowChecks <= 0 ? candidateCount : config.MaxPowChecks; + int powUsed = 0; - for (int i = 0; i < candidateCount; i++) - { - if (powUsed >= powBudget && powBudget > 0) - { - break; - } + for (int i = 0; i < candidateCount; i++) + { + if (powUsed >= powBudget && powBudget > 0) + { + break; + } - UInt128 candidate = candidates[i]; - powUsed++; + UInt128 candidate = candidates[i]; + powUsed++; - if (Pow2ModWideCpu(candidate, prime, divisorData) != UInt128.One) - { - continue; - } + if (Pow2ModWideCpu(candidate, prime, divisorData) != UInt128.One) + { + continue; + } - if (!TryConfirmCandidateWideCpu(prime, candidate, config, ref powUsed, powBudget, divisorData)) - { - continue; - } + if (!TryConfirmCandidateWideCpu(prime, candidate, config, ref powUsed, powBudget, divisorData)) + { + continue; + } - result = candidate; + result = candidate; uInt128ListPool.Return(candidates); - return true; - } + return true; + } uInt128ListPool.Return(candidates); - return false; - } + return false; + } // private static ulong specialMaxHits; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TrySpecialMaxCpu(ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) { int length = factors.Count; @@ -539,6 +550,7 @@ internal static partial class PrimeOrderCalculator return EvaluateSpecialMaxCandidatesCpu(stackBuffer, factorSpan, phi, prime, divisorData); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TrySpecialMaxHybrid(PrimeOrderCalculatorAccelerator gpu, ulong phi, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) { int length = factors.Count; @@ -560,6 +572,7 @@ internal static partial class PrimeOrderCalculator return EvaluateSpecialMaxCandidatesGpu(gpu, factorSpan, phi, prime, divisorData); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool EvaluateSpecialMaxCandidatesCpu(Span buffer, ReadOnlySpan factors, ulong phi, ulong prime, in MontgomeryDivisorData divisorData) { int factorCount = factors.Length; @@ -604,13 +617,14 @@ internal static partial class PrimeOrderCalculator return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong InitializeStartingOrderCpu(ulong prime, ulong phi, in MontgomeryDivisorData divisorData) { ulong order = phi; if ((prime & 7UL) == 1UL || (prime & 7UL) == 7UL) { ulong half = phi >> 1; - if (Pow2EqualsOneCpu(half, prime, divisorData)) + if (Pow2EqualsOneCpu(half, divisorData)) { order = half; } @@ -619,6 +633,7 @@ internal static partial class PrimeOrderCalculator return order; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong InitializeStartingOrderHybrid(PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong phi, in MontgomeryDivisorData divisorData) { ulong order = phi; @@ -634,14 +649,16 @@ internal static partial class PrimeOrderCalculator return order; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong ExponentLoweringCpu(ulong order, ulong prime, PartialFactorResult factors, in MontgomeryDivisorData divisorData) { ReadOnlySpan factorSpan = factors.Factors; ReadOnlySpan exponentsSpan = factors.Exponents; int length = factors.Count; - ulong[] tempFactorsArray = Pools.ExclusiveUlongArray.Rent(length + 1); - int[] tempExponentsArray = Pools.ExclusiveIntArray.Rent(length + 1); + int newLength = length + 1; + ulong[] tempFactorsArray = Pools.ExclusiveUlongArray.Rent(newLength); + int[] tempExponentsArray = Pools.ExclusiveIntArray.Rent(newLength); Span ulongBuffer = tempFactorsArray; factorSpan.CopyTo(ulongBuffer); @@ -652,7 +669,7 @@ internal static partial class PrimeOrderCalculator { ulongBuffer[length] = factors.Cofactor; intBuffer[length] = 1; - length++; + length = newLength; } Array.Sort(tempFactorsArray, tempExponentsArray, 0, length); @@ -706,6 +723,7 @@ internal static partial class PrimeOrderCalculator return order; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void ProcessExponentLoweringPrime(Span candidateBuffer, Span evaluationBuffer, ref ulong order, ulong primeFactor, int exponent, ref ExponentRemainderStepperCpu stepper) { ulong working = order; @@ -756,6 +774,7 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static bool TryConfirmOrderCpu( ulong prime, ulong order, @@ -765,12 +784,8 @@ internal static partial class PrimeOrderCalculator { reusableFactorization = null; - if (order == 0UL) - { - return false; - } - - if (!Pow2EqualsOneCpu(order, prime, divisorData)) + ArgumentOutOfRangeException.ThrowIfEqual(order, 0UL); + if (!Pow2EqualsOneCpu(order, divisorData)) { return false; } @@ -803,6 +818,7 @@ internal static partial class PrimeOrderCalculator return true; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryConfirmOrderHybrid( PrimeOrderCalculatorAccelerator gpu, ulong prime, @@ -851,6 +867,7 @@ internal static partial class PrimeOrderCalculator return true; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool ValidateOrderAgainstFactors( ulong prime, ulong order, @@ -916,6 +933,7 @@ internal static partial class PrimeOrderCalculator return !violates; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool ValidateOrderForFactor(Span buffer, ulong primeFactor, int exponent, ulong order, ref ExponentRemainderStepperCpu stepper) { ulong working = order; @@ -964,13 +982,13 @@ internal static partial class PrimeOrderCalculator return false; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryHeuristicFinishCpu( ulong prime, ulong order, ulong? previousOrder, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config, - PartialFactorResult phiFactors, PartialFactorResult? cachedOrderFactors, out ulong result) { @@ -1005,7 +1023,7 @@ internal static partial class PrimeOrderCalculator orderFactors.WithAdditionalPrime(orderFactors.Cofactor); } - int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecks << 2; + int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecksCapacity; List candidates = ThreadStaticPools.RentUlongList(capacity); candidates.Clear(); ulong[] factorArray = orderFactors.Factors!; @@ -1025,65 +1043,49 @@ internal static partial class PrimeOrderCalculator int powUsed = 0; Span candidateSpan = CollectionsMarshal.AsSpan(candidates); ExponentRemainderStepperCpu powStepper = ThreadStaticPools.RentExponentStepperCpu(divisorData); - bool powStepperInitialized = false; - int index = 0; + ulong candidate = candidateSpan[0]; + powUsed++; + + bool equalsOne = powStepper.InitializeCpuIsUnity(candidate); + if (equalsOne && TryConfirmCandidateCpu(prime, candidate, divisorData, config, ref powUsed, powBudget)) + { + candidates.Clear(); + ThreadStaticPools.ReturnUlongList(candidates); + ThreadStaticPools.ReturnExponentStepperCpu(powStepper); + result = candidate; + return true; + } + + int index = 1; while (index < candidateCount && powUsed < powBudget) { - int remaining = candidateCount - index; int budgetRemaining = powBudget - powUsed; - int batchSize = Math.Min(remaining, Math.Min(budgetRemaining, PrimeOrderConstants.MaxGpuBatchSize)); - if (batchSize <= 0) + if (budgetRemaining <= 0) { break; } - ReadOnlySpan batch = candidateSpan.Slice(index, batchSize); + candidate = candidateSpan[index++]; + powUsed++; - int startIndex = 0; - if (!powStepperInitialized) + equalsOne = powStepper.ComputeNextIsUnity(candidate); + if (!equalsOne) { - ulong candidate = batch[0]; - powUsed++; - - bool equalsOne = powStepper.InitializeCpuIsUnity(candidate); - powStepperInitialized = true; - if (equalsOne && TryConfirmCandidateCpu(prime, candidate, divisorData, config, ref powUsed, powBudget)) - { - candidates.Clear(); - ThreadStaticPools.ReturnUlongList(candidates); - ThreadStaticPools.ReturnExponentStepperCpu(powStepper); - result = candidate; - return true; - } - - startIndex = 1; + continue; } - for (int i = startIndex; i < batchSize && powUsed < powBudget; i++) + if (!TryConfirmCandidateCpu(prime, candidate, divisorData, config, ref powUsed, powBudget)) { - ulong candidate = batch[i]; - powUsed++; - - bool equalsOne = powStepper.ComputeNextIsUnity(candidate); - if (!equalsOne) - { - continue; - } - - if (!TryConfirmCandidateCpu(prime, candidate, divisorData, config, ref powUsed, powBudget)) - { - continue; - } - - candidates.Clear(); - ThreadStaticPools.ReturnUlongList(candidates); - ThreadStaticPools.ReturnExponentStepperCpu(powStepper); - result = candidate; - return true; + continue; } - index += batchSize; + candidates.Clear(); + ThreadStaticPools.ReturnUlongList(candidates); + ThreadStaticPools.ReturnExponentStepperCpu(powStepper); + result = candidate; + return true; + } candidates.Clear(); @@ -1098,6 +1100,7 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryHeuristicFinishHybrid( PrimeOrderCalculatorAccelerator gpu, ulong prime, @@ -1135,7 +1138,7 @@ internal static partial class PrimeOrderCalculator orderFactors.WithAdditionalPrime(orderFactors.Cofactor); } - int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecks << 2; + int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecksCapacity; List candidates = ThreadStaticPools.RentUlongList(capacity); candidates.Clear(); ulong[] factorArray = orderFactors.Factors!; @@ -1230,6 +1233,7 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryHeuristicFinishGpu( PrimeOrderCalculatorAccelerator gpu, ulong prime, @@ -1267,7 +1271,7 @@ internal static partial class PrimeOrderCalculator orderFactors.WithAdditionalPrime(orderFactors.Cofactor); } - int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecks << 2; + int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecksCapacity; List candidates = ThreadStaticPools.RentUlongList(capacity); candidates.Clear(); ulong[] factorArray = orderFactors.Factors!; @@ -1428,6 +1432,7 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void SortCandidates(ulong prime, ulong? previousOrder, List candidates) { ulong previous = previousOrder ?? 0UL; @@ -1437,56 +1442,73 @@ internal static partial class PrimeOrderCalculator ulong threshold3 = (prime * 3UL) >> 3; int previousGroup = hasPrevious ? GetGroup(previous, threshold1, threshold2, threshold3) : 1; - candidates.Sort((x, y) => - { - CandidateKey keyX = BuildKey(x, previous, previousGroup, hasPrevious, threshold1, threshold2, threshold3); - CandidateKey keyY = BuildKey(y, previous, previousGroup, hasPrevious, threshold1, threshold2, threshold3); - int compare = keyX.Primary.CompareTo(keyY.Primary); - if (compare != 0) - { - return compare; - } - - compare = keyX.Secondary.CompareTo(keyY.Secondary); - if (compare != 0) - { - return compare; - } - - return keyX.Tertiary.CompareTo(keyY.Tertiary); - }); + candidates.Sort((x, y) => CompareCandidates(x, y, previous, previousGroup, hasPrevious, threshold1, threshold2, threshold3)); } - private static CandidateKey BuildKey(ulong value, ulong previous, int previousGroup, bool hasPrevious, ulong threshold1, ulong threshold2, ulong threshold3) + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static int CompareCandidates(ulong left, ulong right, ulong previous, int previousGroup, bool hasPrevious, ulong threshold1, ulong threshold2, ulong threshold3) { - int group = GetGroup(value, threshold1, threshold2, threshold3); - if (group == 0) + int leftGroup = GetGroup(left, threshold1, threshold2, threshold3); + int rightGroup = GetGroup(right, threshold1, threshold2, threshold3); + if (leftGroup == 0) { - return CandidateKey.Max; + return rightGroup == 0 ? 0 : 1; } - bool isGe = !hasPrevious || value >= previous; - int primary = ComputePrimary(group, isGe, previousGroup); - long secondary; - long tertiary; - - if (group == 3) + if (rightGroup == 0) { - secondary = -(long)value; - tertiary = -(long)value; + return -1; + } + + bool leftIsGe = !hasPrevious || left >= previous; + bool rightIsGe = !hasPrevious || right >= previous; + int leftPrimary = ComputePrimary(leftGroup, leftIsGe, previousGroup); + int rightPrimary = ComputePrimary(rightGroup, rightIsGe, previousGroup); + + int compare = leftPrimary.CompareTo(rightPrimary); + if (compare != 0) + { + return compare; + } + + long leftSecondary; + long leftTertiary; + if (leftGroup == 3) + { + leftSecondary = -(long)left; + leftTertiary = -(long)left; } else { - ulong reference = hasPrevious ? previous : 0UL; - reference = hasPrevious ? (value > reference ? value - reference : reference - value) : value; - secondary = (long)reference; - tertiary = (long)value; + ulong leftReference = hasPrevious ? (left > previous ? left - previous : previous - left) : left; + leftSecondary = (long)leftReference; + leftTertiary = (long)left; } - return new CandidateKey(primary, secondary, tertiary); + long rightSecondary; + long rightTertiary; + if (rightGroup == 3) + { + rightSecondary = -(long)right; + rightTertiary = -(long)right; + } + else + { + ulong rightReference = hasPrevious ? (right > previous ? right - previous : previous - right) : right; + rightSecondary = (long)rightReference; + rightTertiary = (long)right; + } + + compare = leftSecondary.CompareTo(rightSecondary); + if (compare != 0) + { + return compare; + } + + return leftTertiary.CompareTo(rightTertiary); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static int ComputePrimary(int group, bool isGe, int previousGroup) { int groupOffset = group switch @@ -1510,7 +1532,7 @@ internal static partial class PrimeOrderCalculator return groupOffset + (isGe ? 0 : 1); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static int GetGroup(ulong value, ulong threshold1, ulong threshold2, ulong threshold3) { if (value <= threshold1) @@ -1531,6 +1553,7 @@ internal static partial class PrimeOrderCalculator return 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static void BuildCandidates(ulong order, ulong[] factors, int[] exponents, int count, List candidates, int limit) { if (count == 0) @@ -1542,7 +1565,7 @@ internal static partial class PrimeOrderCalculator BuildCandidatesRecursive(order, factors, exponents, 0, 1UL, candidates, limit); } - // private static void BuildCandidatesRecursive(ulong order, in ReadOnlySpan factors, int index, ulong divisorProduct, List candidates, int limit) + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void BuildCandidatesRecursive(ulong order, in ulong[] factors, in int[] exponents, int index, ulong divisorProduct, List candidates, int limit) { if (candidates.Count >= limit) @@ -1603,6 +1626,7 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryConfirmCandidateCpu(ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config, ref int powUsed, int powBudget) { PartialFactorResult factorization = PartialFactorCpu(candidate, config); @@ -1682,6 +1706,7 @@ internal static partial class PrimeOrderCalculator return !violates; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryConfirmCandidateHybrid(PrimeOrderCalculatorAccelerator gpu, ulong prime, ulong candidate, in MontgomeryDivisorData divisorData, in PrimeOrderCalculatorConfig config, ref int powUsed, int powBudget) { PartialFactorResult factorization = PartialFactorHybrid(gpu, candidate, config); @@ -1760,6 +1785,7 @@ internal static partial class PrimeOrderCalculator return !violates; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool CheckCandidateViolation(Span buffer, ulong primeFactor, int exponent, ulong candidate, ulong prime, ref int powUsed, int powBudget, ref ExponentRemainderStepperCpu stepper) { ulong working = candidate; @@ -1841,10 +1867,11 @@ internal static partial class PrimeOrderCalculator return false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool Pow2EqualsOneCpu(ulong exponent, ulong prime, in MontgomeryDivisorData divisorData) + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static bool Pow2EqualsOneCpu(ulong exponent, in MontgomeryDivisorData divisorData) => exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static bool Pow2EqualsOneHybrid(PrimeOrderCalculatorAccelerator gpu, ulong exponent, ulong prime, in MontgomeryDivisorData divisorData) { // if (exponent >= PerfectNumberConstants.MaxQForDivisorCycles || prime >= PerfectNumberConstants.MaxQForDivisorCycles) @@ -1869,6 +1896,7 @@ internal static partial class PrimeOrderCalculator private static readonly ulong[] _emptyUlongArray = []; private static readonly int[] _emptyIntArray = []; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static PartialFactorResult PartialFactorCpu(ulong value, in PrimeOrderCalculatorConfig config) { if (value <= 1UL) @@ -1884,8 +1912,8 @@ internal static partial class PrimeOrderCalculator // primeSlots.Clear(); // exponentSlots.Clear(); + ArgumentOutOfRangeException.ThrowIfEqual(config.SmallFactorLimit, 0U); uint limit = config.SmallFactorLimit == 0 ? uint.MaxValue : config.SmallFactorLimit; - ulong remaining = value; List pending = ThreadStaticPools.RentPrimeOrderPendingEntryList(PrimeOrderConstants.GpuSmallPrimeFactorSlots); @@ -1895,7 +1923,7 @@ internal static partial class PrimeOrderCalculator primeSlots, exponentSlots, out int factorCount, - out remaining); + out ulong remaining); bool limitReached; if (remaining > 1UL) @@ -2033,6 +2061,7 @@ internal static partial class PrimeOrderCalculator return result; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static PartialFactorResult PartialFactorHybrid(PrimeOrderCalculatorAccelerator gpu, ulong value, in PrimeOrderCalculatorConfig config) { if (value <= 1UL) @@ -2205,6 +2234,7 @@ internal static partial class PrimeOrderCalculator return result; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static PartialFactorResult PartialFactorGpu(PrimeOrderCalculatorAccelerator gpu, ulong value, in PrimeOrderCalculatorConfig config) { if (value <= 1UL) @@ -2382,7 +2412,7 @@ internal static partial class PrimeOrderCalculator return result; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void CollectFactorsHybrid(PrimeOrderCalculatorAccelerator gpu, Span primeSlots, Span exponentSlots, ref ulong[] primeSlotArray, ref int[] exponentSlotArray, ref int factorCount, List pending, Stack compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached) { while (compositeStack.Count > 0) @@ -2417,7 +2447,7 @@ internal static partial class PrimeOrderCalculator pollardRhoDeadlineReached = false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void CollectFactorsCpu(Span primeSlots, Span exponentSlots, ref int factorCount, List pending, Stack compositeStack, long deadlineTimestamp, out bool pollardRhoDeadlineReached) { while (compositeStack.Count > 0) @@ -2452,7 +2482,7 @@ internal static partial class PrimeOrderCalculator pollardRhoDeadlineReached = false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void CollectFactorsGpu(PrimeOrderCalculatorAccelerator gpu, ref Span primeSlots, ref Span exponentSlots, ref ulong[] primeSlotArray, ref int[] exponentSlotArray, ref int factorCount, List pending, Stack compositeStack, long deadlineTimestamp, ref bool pollardRhoDeadlineReached) { while (compositeStack.Count > 0) @@ -2487,13 +2517,14 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool RunOnCpu() { if (PerfectNumberConstants.GpuRatio == 0) { return false; } - + if (PerfectNumberConstants.GpuRatio == 1) { while (true) @@ -2525,6 +2556,7 @@ internal static partial class PrimeOrderCalculator return cpuCount != PerfectNumberConstants.GpuRatio; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void PopulateSmallPrimeFactorsCpu( ulong value, uint limit, @@ -2534,48 +2566,45 @@ internal static partial class PrimeOrderCalculator out ulong remaining) { factorCount = 0; - ulong remainingLocal = value; - if (value <= 1UL) { - remaining = remainingLocal; + remaining = value; return; } uint[] primes = PrimesGenerator.SmallPrimes; ulong[] squares = PrimesGenerator.SmallPrimesPow2; int primeCount = primes.Length; - uint effectiveLimit = limit == 0 ? uint.MaxValue : limit; - for (int i = 0; i < primeCount && remainingLocal > 1UL; i++) + for (int i = 0; i < primeCount && value > 1UL; i++) { uint primeCandidate = primes[i]; - if (primeCandidate > effectiveLimit) + if (primeCandidate > limit) { break; } - ulong primeSquare = squares[i]; + ulong primeValue = squares[i]; // primeSquare will never == 0 in production code // if (primeSquare != 0 && primeSquare > remainingLocal) - if (primeSquare > remainingLocal) + if (primeValue > value) { break; } - ulong primeValue = primeCandidate; + primeValue = primeCandidate; // primeCandidate will never equal 0 in production code // if (primeCandidate == 0UL) // { // continue; // } - if (remainingLocal.ReduceCycleRemainder(primeValue) != 0UL) + if (value.ReduceCycleRemainder(primeValue) != 0UL) { continue; } - int exponent = ExtractSmallPrimeExponent(ref remainingLocal, primeValue); + int exponent = ExtractSmallPrimeExponent(ref value, primeValue); if (exponent == 0) { continue; @@ -2590,10 +2619,10 @@ internal static partial class PrimeOrderCalculator factorCount++; } - remaining = remainingLocal; + remaining = value; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] // Unroll the initial divisions because small exponents dominate in practice. private static int ExtractSmallPrimeExponent(ref ulong value, ulong primeValue) { @@ -2658,6 +2687,7 @@ internal static partial class PrimeOrderCalculator return exponent; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static long CreateDeadlineTimestamp(int milliseconds) { if (milliseconds <= 0) @@ -2665,9 +2695,9 @@ internal static partial class PrimeOrderCalculator return long.MaxValue; } - long stopwatchTicks = ConvertMillisecondsToStopwatchTicks(milliseconds); long startTimestamp = Stopwatch.GetTimestamp(); - long deadline = startTimestamp + stopwatchTicks; + long deadline = ConvertMillisecondsToStopwatchTicks(milliseconds); + deadline += startTimestamp; if (deadline < startTimestamp) { return long.MaxValue; @@ -2676,7 +2706,7 @@ internal static partial class PrimeOrderCalculator return deadline; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static long ConvertMillisecondsToStopwatchTicks(int milliseconds) { long frequency = Stopwatch.Frequency; @@ -2685,6 +2715,7 @@ internal static partial class PrimeOrderCalculator return baseTicks + (remainder / 1000L); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryPollardRhoCpu(ulong n, long deadlineTimestamp, out ulong factor) { factor = 0UL; @@ -2724,22 +2755,23 @@ internal static partial class PrimeOrderCalculator } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong AdvancePolynomialCpu(ulong x, ulong c, ulong modulus) { UInt128 value = (UInt128)x * x + c; return (ulong)value.ReduceCycleRemainder(modulus); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong AdvancePolynomialTwiceCpu(ulong x, ulong c, ulong modulus) { UInt128 value = (UInt128)x * x + c; - var remainder = (ulong)value.ReduceCycleRemainder(modulus); - value = (UInt128)remainder * remainder + c; + x = (ulong)value.ReduceCycleRemainder(modulus); + value = (UInt128)x * x + c; return (ulong)value.ReduceCycleRemainder(modulus); } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong BinaryGcd(ulong a, ulong b) { if (a == 0UL) @@ -2771,6 +2803,7 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static void AddFactor(Dictionary counts, ulong prime, int exponent) { if (counts.TryGetValue(prime, out int existing)) @@ -2783,6 +2816,7 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void AddFactorToCollector( Span primes, Span exponents, @@ -2813,7 +2847,7 @@ internal static partial class PrimeOrderCalculator count++; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static void EnsureCollectorCapacity( Span primes, int requiredCapacity) @@ -2821,6 +2855,7 @@ internal static partial class PrimeOrderCalculator ArgumentOutOfRangeException.ThrowIfGreaterThan(requiredCapacity, primes.Length); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong CalculateByFactorizationCpu(ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; @@ -2868,6 +2903,7 @@ internal static partial class PrimeOrderCalculator return order; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static void FactorCompletelyCpu(ulong value, Dictionary counts, bool knownComposite) { if (value <= 1UL) @@ -2927,6 +2963,7 @@ internal static partial class PrimeOrderCalculator } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong PollardRhoStrict(ulong n) { if ((n & 1UL) == 0UL) @@ -2944,8 +2981,7 @@ internal static partial class PrimeOrderCalculator while (d == 1UL) { x = AdvancePolynomialCpu(x, c, n); - y = AdvancePolynomialCpu(y, c, n); - y = AdvancePolynomialCpu(y, c, n); + y = AdvancePolynomialTwiceCpu(y, c, n); ulong diff = x > y ? x - y : y - x; d = BinaryGcd(diff, n); } @@ -2957,76 +2993,72 @@ internal static partial class PrimeOrderCalculator } } - private static UInt128 Pow2ModWideCpu(in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData? divisorData) - { - if (modulus == UInt128.One) - { - return UInt128.Zero; - } - - return exponent.Pow2MontgomeryModWindowed(modulus); - } - - private static bool TryConfirmCandidateWideCpu(in UInt128 prime, in UInt128 candidate, in PrimeOrderCalculatorConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData? divisorData) - { - PartialFactorResult128 factorization = PartialFactorWide(candidate, config); - if (factorization.Factors is null) - { - return false; - } - - if (!factorization.FullyFactored) - { - if (factorization.Cofactor <= UInt128.One) - { - return false; - } - - if (!IsPrimeWide(factorization.Cofactor)) - { - return false; - } - - factorization = factorization.WithAdditionalPrime(factorization.Cofactor); - } - - ReadOnlySpan span = factorization.Factors; - int length = factorization.Count; - for (int i = 0; i < length; i++) - { - UInt128 primeFactor = span[i].Value; - UInt128 reduced = candidate; - for (int iteration = 0; iteration < span[i].Exponent; iteration++) - { - if (reduced.ReduceCycleRemainder(primeFactor) != UInt128.Zero) - { - break; - } - - reduced /= primeFactor; - if (powUsed >= powBudget && powBudget > 0) - { - return false; - } - - powUsed++; - if (Pow2ModWideCpu(reduced, prime, divisorData) == UInt128.One) - { - return false; - } - } - } - - return true; - } - - private readonly struct CandidateKey(int primary, long secondary, long tertiary) + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static UInt128 Pow2ModWideCpu(in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData? divisorData) { - public static readonly CandidateKey Max = new(int.MaxValue, long.MaxValue, long.MaxValue); + if (modulus == UInt128.One) + { + return UInt128.Zero; + } - public readonly int Primary = primary; - public readonly long Secondary = secondary; - public readonly long Tertiary = tertiary; + return exponent.Pow2MontgomeryModWindowed(modulus); } + + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private static bool TryConfirmCandidateWideCpu(in UInt128 prime, in UInt128 candidate, in PrimeOrderCalculatorConfig config, ref int powUsed, int powBudget, in MontgomeryDivisorData? divisorData) + { + PartialFactorResult128 factorization = PartialFactorWide(candidate, config); + if (factorization.Factors is null) + { + return false; + } + + if (!factorization.FullyFactored) + { + if (factorization.Cofactor <= UInt128.One) + { + return false; + } + + if (!IsPrimeWide(factorization.Cofactor)) + { + return false; + } + + factorization = factorization.WithAdditionalPrime(factorization.Cofactor); + } + + ReadOnlySpan span = factorization.Factors; + int length = factorization.Count; + for (int i = 0; i < length; i++) + { + UInt128 primeFactor = span[i].Value; + UInt128 reduced = candidate; + for (int iteration = 0; iteration < span[i].Exponent; iteration++) + { + if (reduced.ReduceCycleRemainder(primeFactor) != UInt128.Zero) + { + break; + } + + reduced /= primeFactor; + if (powUsed >= powBudget && powBudget > 0) + { + return false; + } + + powUsed++; + if (Pow2ModWideCpu(reduced, prime, divisorData) == UInt128.One) + { + return false; + } + } + } + + return true; + } + } + + diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs index 5d82202..bf3ffc3 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.Gpu.cs @@ -1,4 +1,5 @@ using System.Buffers; +using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu; @@ -86,6 +87,7 @@ internal static partial class PrimeOrderCalculator return result; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static UInt128 CalculateWideInternalGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 prime, in UInt128? previousOrder, in MontgomeryDivisorData? divisorData, in PrimeOrderCalculatorConfig config) { if (prime <= UInt128.One) @@ -361,7 +363,7 @@ internal static partial class PrimeOrderCalculator orderFactors = orderFactors.WithAdditionalPrime(orderFactors.Cofactor); } - int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecks * 4; + int capacity = config.MaxPowChecks <= 0 ? 64 : config.MaxPowChecksCapacity; List candidates = new(capacity); FactorEntry128[] factorArray = orderFactors.Factors!; BuildCandidatesWide(order, factorArray, orderFactors.Count, candidates, capacity); @@ -403,6 +405,7 @@ internal static partial class PrimeOrderCalculator return false; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong CalculateByFactorizationGpu(PrimeOrderCalculatorAccelerator gpu, ulong prime, in MontgomeryDivisorData divisorData) { ulong phi = prime - 1UL; @@ -445,6 +448,7 @@ internal static partial class PrimeOrderCalculator return order; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryPopulateSmallPrimeFactorsGpu(PrimeOrderCalculatorAccelerator gpu, ulong value, uint limit, Dictionary counts, out int factorCount, out ulong remaining) { var primeBufferArray = ThreadStaticPools.UlongPool.Rent(GpuSmallPrimeFactorSlots); @@ -511,6 +515,7 @@ internal static partial class PrimeOrderCalculator return true; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool EvaluateSpecialMaxCandidatesGpu( PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan factors, @@ -548,6 +553,7 @@ internal static partial class PrimeOrderCalculator return result != 0; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static UInt128 Pow2ModWideGpu(PrimeOrderCalculatorAccelerator gpu, in UInt128 exponent, in UInt128 modulus, in MontgomeryDivisorData? divisorData) { if (modulus == UInt128.One) diff --git a/PerfectNumbers.Core/PrimeOrderCalculator.cs b/PerfectNumbers.Core/PrimeOrderCalculator.cs index 10ea057..9b73069 100644 --- a/PerfectNumbers.Core/PrimeOrderCalculator.cs +++ b/PerfectNumbers.Core/PrimeOrderCalculator.cs @@ -30,6 +30,7 @@ internal static partial class PrimeOrderCalculator public readonly uint SmallFactorLimit = smallFactorLimit; public readonly int PollardRhoMilliseconds = pollardRhoMilliseconds; public readonly int MaxPowChecks = maxPowChecks; + public readonly int MaxPowChecksCapacity = maxPowChecks << 2; public readonly bool StrictMode = strictMode; } diff --git a/PerfectNumbers.Core/PrimeTesterByLastDigit.cs b/PerfectNumbers.Core/PrimeTesterByLastDigit.cs index 06f4cd1..7cb64da 100644 --- a/PerfectNumbers.Core/PrimeTesterByLastDigit.cs +++ b/PerfectNumbers.Core/PrimeTesterByLastDigit.cs @@ -10,7 +10,7 @@ public sealed class PrimeTesterByLastDigit [ThreadStatic] private static bool _mod10Initialized; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static byte GetMod10(ulong value) { if (!_mod10Initialized || _mod10Stepper.Modulus != 10UL) @@ -24,6 +24,7 @@ public sealed class PrimeTesterByLastDigit return (byte)_mod10Stepper.Step(value); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static bool IsPrimeCpu(ulong n) { // The below IFs never trigger in production code of EvenPerfectBitScanner on --mersenne=bydivisor path. diff --git a/PerfectNumbers.Core/ULongExtensions.Cpu.cs b/PerfectNumbers.Core/ULongExtensions.Cpu.cs index 8d92b18..f5af06a 100644 --- a/PerfectNumbers.Core/ULongExtensions.Cpu.cs +++ b/PerfectNumbers.Core/ULongExtensions.Cpu.cs @@ -5,7 +5,7 @@ namespace PerfectNumbers.Core; public static partial class ULongExtensions { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModWindowedKeepMontgomeryCpu(this ulong exponent, in MontgomeryDivisorData divisor) { ulong modulus = divisor.Modulus; @@ -75,7 +75,7 @@ public static partial class ULongExtensions return result; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModWindowedConvertToStandardCpu(this ulong exponent, in MontgomeryDivisorData divisor) { ulong modulus = divisor.Modulus; @@ -146,7 +146,7 @@ public static partial class ULongExtensions return result; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static void InitializeMontgomeryOddPowersCpu(in MontgomeryDivisorData divisor, ulong modulus, ulong nPrime, Span oddPowers) { oddPowers[0] = divisor.MontgomeryTwo; @@ -165,20 +165,20 @@ public static partial class ULongExtensions } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModWithCycleConvertToStandardCpu(this ulong exponent, ulong cycleLength, in MontgomeryDivisorData divisor) { ulong rotationCount = exponent % cycleLength; return Pow2MontgomeryModWindowedConvertToStandardCpu(rotationCount, divisor); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModFromCycleRemainderCpu(this ulong reducedExponent, in MontgomeryDivisorData divisor) { return Pow2MontgomeryModWindowedConvertToStandardCpu(reducedExponent, divisor); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong CalculateMersenneDivisorCycleLengthUnrolledHexCpu(this ulong divisor) { // EvenPerfectBitScanner only routes odd divisors here; keep the guard commented out for benchmarks. diff --git a/PerfectNumbers.Core/ULongExtensions.Gpu.cs b/PerfectNumbers.Core/ULongExtensions.Gpu.cs index 098a300..91323fb 100644 --- a/PerfectNumbers.Core/ULongExtensions.Gpu.cs +++ b/PerfectNumbers.Core/ULongExtensions.Gpu.cs @@ -7,7 +7,7 @@ namespace PerfectNumbers.Core; public static partial class ULongExtensions { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [Obsolete("Use ULongExtensions.MulHighCpu or ULongExtensions.MulHighGpu.")] public static ulong MulHighGpuCompatible(this ulong x, ulong y) { @@ -16,7 +16,7 @@ public static partial class ULongExtensions return product.High; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [Obsolete("Use ULongExtensions.MulModGpu for GPU-compatible host code or GpuUInt128.MulMod inside kernels.")] public static ulong MulMod64Gpu(this ulong a, ulong b, ulong modulus) { @@ -26,7 +26,7 @@ public static partial class ULongExtensions return state.MulMod(b, modulus); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [Obsolete("Use ULongExtensions.MulModGpu for GPU-compatible host code or GpuUInt128.MulMod inside kernels.")] public static ulong MulMod64GpuDeferred(this ulong a, ulong b, ulong modulus) { @@ -36,29 +36,29 @@ public static partial class ULongExtensions return state.MulModWithNativeModulo(b, modulus); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong MulModGpu(this ulong left, ulong right, ulong modulus) { GpuUInt128 state = new(left); return state.MulMod(right, modulus); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModWindowedKeepGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor) => Pow2MontgomeryGpuCalculator.CalculateKeep(gpu, exponent, divisor); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModWindowedConvertGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor) => Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, exponent, divisor); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModWithCycleGpu(this ulong exponent, PrimeOrderCalculatorAccelerator gpu, ulong cycleLength, in MontgomeryDivisorData divisor) { ulong rotationCount = exponent % cycleLength; return Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, rotationCount, divisor); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong Pow2MontgomeryModFromCycleRemainderGpu(this ulong reducedExponent, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisor) { return Pow2MontgomeryGpuCalculator.CalculateConvert(gpu, reducedExponent, divisor); diff --git a/PerfectNumbers.Core/ULongExtensions.cs b/PerfectNumbers.Core/ULongExtensions.cs index 931056a..090593d 100644 --- a/PerfectNumbers.Core/ULongExtensions.cs +++ b/PerfectNumbers.Core/ULongExtensions.cs @@ -642,7 +642,7 @@ public static partial class ULongExtensions return (ulong)(((UInt128)a * b) % modulus); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong MontgomeryMultiplyCpu(this ulong a, ulong b, ulong modulus, ulong nPrime) { ulong tLow = unchecked(a * b); @@ -1133,7 +1133,7 @@ public static partial class ULongExtensions return x1; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static ulong ReduceCycleRemainder(this ulong value, ulong modulus) { // BigIntegerExtensions.Track(modulus);