From 2236c3ec35a8ccabb84737bd4508111d2b145ac7 Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Thu, 18 Dec 2025 10:29:28 +0100 Subject: [PATCH] MersenneCpuDivisorScanSessionWithCpuOrder, MersenneCpuDivisorScanSessionWithHybridOrder & MersenneCpuDivisorScanSessionWithGpuOrder converted to device dependent templates --- .../DeviceDependentGenerator.cs | 32 +-- .../Cpu/MersenneCpuDivisorScanSession.cs | 244 ------------------ ...rsenneCpuDivisorScanSessionWithTemplate.cs | 126 +++++++++ 3 files changed, 128 insertions(+), 274 deletions(-) delete mode 100644 PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs create mode 100644 PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSessionWithTemplate.cs diff --git a/DeviceDependentGenerator/DeviceDependentGenerator.cs b/DeviceDependentGenerator/DeviceDependentGenerator.cs index 814862b..ba8632b 100644 --- a/DeviceDependentGenerator/DeviceDependentGenerator.cs +++ b/DeviceDependentGenerator/DeviceDependentGenerator.cs @@ -170,7 +170,7 @@ public sealed class DeviceDependentGenerator : IIncrementalGenerator continue; } - ReportDeviceSpecificTemplateErrors(context, compilation, originalTree, deviceTree, templateSyntax.Identifier.GetLocation(), enumValueName); + ReportDeviceSpecificTemplateErrors(context, deviceTree); string generatedTypeName = baseName + enumValueName + template.Suffix; TypeDeclarationSyntax generatedTypeSyntax = TransformTemplateType(deviceTemplateSyntax, generatedTypeName); @@ -192,13 +192,7 @@ public sealed class DeviceDependentGenerator : IIncrementalGenerator } } - private static void ReportDeviceSpecificTemplateErrors( - SourceProductionContext context, - Compilation compilation, - SyntaxTree originalTree, - SyntaxTree deviceTree, - Location fallbackLocation, - string enumValueName) + private static void ReportDeviceSpecificTemplateErrors(SourceProductionContext context, SyntaxTree deviceTree) { foreach (Diagnostic diagnostic in deviceTree.GetDiagnostics(context.CancellationToken)) { @@ -207,28 +201,6 @@ public sealed class DeviceDependentGenerator : IIncrementalGenerator context.ReportDiagnostic(diagnostic); } } - - if (compilation is not CSharpCompilation csharpCompilation) - { - return; - } - - CSharpCompilation deviceCompilation = csharpCompilation.ReplaceSyntaxTree(originalTree, deviceTree); - foreach (Diagnostic diagnostic in deviceCompilation.GetDiagnostics(context.CancellationToken)) - { - if (diagnostic.Severity != DiagnosticSeverity.Error) - { - continue; - } - - if (diagnostic.Location is { Kind: LocationKind.SourceFile } location - && location.SourceTree != deviceTree) - { - continue; - } - - context.ReportDiagnostic(diagnostic); - } } private static bool IsTopLevel(TypeDeclarationSyntax typeDeclaration) diff --git a/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs b/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs deleted file mode 100644 index f07ca4c..0000000 --- a/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSession.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System.Runtime.CompilerServices; -using PerfectNumbers.Core.Gpu.Accelerators; - -namespace PerfectNumbers.Core.Cpu; - -internal struct MersenneCpuDivisorScanSessionWithCpuOrder() : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession - { - private readonly CycleRemainderStepper _cycleStepper = new(); - private ExponentRemainderStepperCpu? _exponentStepper; - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public readonly void Configure() - { - } - - private static ulong _hits; - private static ulong _misses; - - private static void PrintStats(bool wasHit) - { - if (wasHit) - { - Interlocked.Increment(ref _hits); - } - else - { - Interlocked.Increment(ref _misses); - } - - Console.WriteLine($"MersenneCpuDivisorScanSession, ExponentStepper hits: $_hits, misses: $_misses"); - } - - public bool CheckDivisor( - ulong divisor, - in MontgomeryDivisorData divisorData, - ulong divisorCycle, - in ReadOnlySpan primes) - { - int length = primes.Length; - // EvenPerfectBitScanner always provides at least one exponent on this path. - // Keep the guard commented out for tests that might reuse the session differently. - // if (length == 0) - // { - // return false; - // } - - // This never kicks off in production code - // if (divisorData.Modulus != divisor) - // { - // throw new InvalidOperationException("Divisor data is for a different modulus"); - // // cachedData = MontgomeryDivisorData.FromModulus(divisor); - // } - - if (divisorCycle == 0UL) - { - divisorCycle = DivisorCycleCache.Shared.GetCycleLengthCpu(divisor, divisorData); - - // This never kicks off in production code - // if (divisorCycle == 0UL) - // { - // // DivisorCycleCache guarantees a positive cycle for divisors greater than one. - // throw new InvalidOperationException($"Divisor cycle solver returned zero for divisor {divisor}."); - // } - } - - // Keep these remainder steppers in place so future updates continue reusing the previously computed residues. - // They are critical for avoiding repeated full Montgomery exponentiation work when scanning divisors. - CycleRemainderStepper cycleStepper = _cycleStepper; - cycleStepper.CycleLength = divisorCycle; - if (cycleStepper.Initialize(primes[0]) == 0UL) - { - return true; - } - - if (_exponentStepper is not { } exponentStepper) - { - _exponentStepper = exponentStepper = new(divisorData, divisorCycle); - } - else - { - if (!exponentStepper.MatchesDivisor(divisorData, divisorCycle)) - { - _exponentStepper = exponentStepper = new(divisorData, divisorCycle); - } - } - - if (exponentStepper.InitializeCpuIsUnity(primes[0])) - { - return true; - } - - for (int i = 1; i < length; i++) - { - if (cycleStepper.ComputeNext(primes[i]) != 0UL) - { - continue; - } - - if (exponentStepper.ComputeNextIsUnity(primes[i])) - { - return true; - } - } - - return false; - } - - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - // public void Return() => ThreadStaticPools.ReturnMersenneCpuDivisorSession(this); -} - -internal struct MersenneCpuDivisorScanSessionWithHybridOrder() : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession -{ - private PrimeOrderCalculatorAccelerator? _gpu; - - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public void Configure(PrimeOrderCalculatorAccelerator gpu) - { - _gpu = gpu; - } - - public bool CheckDivisor( - ulong divisor, - in MontgomeryDivisorData divisorData, - ulong divisorCycle, - in ReadOnlySpan primes) - { - int length = primes.Length; - var gpu = _gpu ?? throw new InvalidOperationException("Session was not configured with an accelerator."); - if (divisorCycle == 0UL) - { - divisorCycle = DivisorCycleCache.Shared.GetCycleLengthHybrid(gpu, divisor, divisorData); - } - - // Keep these remainder steppers in place so future updates continue reusing the previously computed residues. - // They are critical for avoiding repeated full Montgomery exponentiation work when scanning divisors. - var cycleStepper = new CycleRemainderStepper(divisorCycle); - if (cycleStepper.Initialize(primes[0]) == 0UL) - { - return true; - } - - var exponentStepper = new ExponentRemainderStepperCpu(divisorData, divisorCycle); - if (exponentStepper.InitializeCpuIsUnity(primes[0])) - { - return true; - } - - for (int i = 1; i < length; i++) - { - if (cycleStepper.ComputeNext(primes[i]) != 0UL) - { - continue; - } - - if (exponentStepper.ComputeNextIsUnity(primes[i])) - { - return true; - } - } - - return false; - } - - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - // public void Return() => ThreadStaticPools.ReturnMersenneCpuDivisorSession(this); -} - -internal struct MersenneCpuDivisorScanSessionWithGpuOrder() : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession -{ - private PrimeOrderCalculatorAccelerator? _gpu; - - public void Configure(PrimeOrderCalculatorAccelerator gpu) - { - _gpu = gpu; - } - - public bool CheckDivisor( - ulong divisor, - in MontgomeryDivisorData divisorData, - ulong divisorCycle, - in ReadOnlySpan primes) - { - int length = primes.Length; - var gpu = _gpu ?? throw new InvalidOperationException("Session was not configured with an accelerator."); - // EvenPerfectBitScanner always provides at least one exponent on this path. - // Keep the guard commented out for tests that might reuse the session differently. - // if (length == 0) - // { - // return false; - // } - - // This never kicks off in production code - // if (divisorData.Modulus != divisor) - // { - // throw new InvalidOperationException("Divisor data is for a different modulus"); - // // cachedData = MontgomeryDivisorData.FromModulus(divisor); - // } - - if (divisorCycle == 0UL) - { - divisorCycle = DivisorCycleCache.Shared.GetCycleLengthGpu(gpu, divisor, divisorData); - - // This never kicks off in production code - // if (divisorCycle == 0UL) - // { - // // DivisorCycleCache guarantees a positive cycle for divisors greater than one. - // throw new InvalidOperationException($"Divisor cycle solver returned zero for divisor {divisor}."); - // } - } - - // Keep these remainder steppers in place so future updates continue reusing the previously computed residues. - // They are critical for avoiding repeated full Montgomery exponentiation work when scanning divisors. - var cycleStepper = new CycleRemainderStepper(divisorCycle); - if (cycleStepper.Initialize(primes[0]) == 0UL) - { - return true; - } - - var exponentStepper = new ExponentRemainderStepperCpu(divisorData, divisorCycle); - if (exponentStepper.InitializeCpuIsUnity(primes[0])) - { - return true; - } - - for (int i = 1; i < length; i++) - { - if (cycleStepper.ComputeNext(primes[i]) != 0UL) - { - continue; - } - - if (exponentStepper.ComputeNextIsUnity(primes[i])) - { - return true; - } - } - - return false; - } - - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - // public void Return() => ThreadStaticPools.ReturnMersenneCpuDivisorSession(this); -} diff --git a/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSessionWithTemplate.cs b/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSessionWithTemplate.cs new file mode 100644 index 0000000..5292668 --- /dev/null +++ b/PerfectNumbers.Core/Cpu/MersenneCpuDivisorScanSessionWithTemplate.cs @@ -0,0 +1,126 @@ +using System.Runtime.CompilerServices; +using PerfectNumbers.Core.Gpu.Accelerators; + +namespace PerfectNumbers.Core.Cpu; + +[DeviceDependentTemplate(typeof(ComputationDevice), suffix: "Order")] +internal struct MersenneCpuDivisorScanSessionWithTemplate() : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession +{ +#if DEVICE_GPU || DEVICE_HYBRID + private PrimeOrderCalculatorAccelerator? _gpu; +#endif + private readonly CycleRemainderStepper _cycleStepper = new(); + private ExponentRemainderStepperCpu? _exponentStepper; + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#if DEVICE_GPU || DEVICE_HYBRID + public void Configure(PrimeOrderCalculatorAccelerator gpu) + { + _gpu = gpu; + } +#else + public readonly void Configure() + { + } +#endif + + private static ulong _hits; + private static ulong _misses; + + private static void PrintStats(bool wasHit) + { + if (wasHit) + { + Interlocked.Increment(ref _hits); + } + else + { + Interlocked.Increment(ref _misses); + } + + Console.WriteLine($"MersenneCpuDivisorScanSession, ExponentStepper hits: $_hits, misses: $_misses"); + } + + public bool CheckDivisor( + ulong divisor, + in MontgomeryDivisorData divisorData, + ulong divisorCycle, + in ReadOnlySpan primes) + { + int length = primes.Length; + // EvenPerfectBitScanner always provides at least one exponent on this path. + // Keep the guard commented out for tests that might reuse the session differently. + // if (length == 0) + // { + // return false; + // } + + // This never kicks off in production code + // if (divisorData.Modulus != divisor) + // { + // throw new InvalidOperationException("Divisor data is for a different modulus"); + // // cachedData = MontgomeryDivisorData.FromModulus(divisor); + // } +#if DEVICE_GPU || DEVICE_HYBRID + var gpu = _gpu; +#endif + + if (divisorCycle == 0UL) + { +#if DEVICE_CPU + divisorCycle = DivisorCycleCache.Shared.GetCycleLengthCpu(divisor, divisorData); +#elif DEVICE_HYBRID + divisorCycle = DivisorCycleCache.Shared.GetCycleLengthHybrid(gpu, divisor, divisorData); +#elif DEVICE_GPU + divisorCycle = DivisorCycleCache.Shared.GetCycleLengthGpu(gpu, divisor, divisorData); +#endif + // This never kicks off in production code + // if (divisorCycle == 0UL) + // { + // // DivisorCycleCache guarantees a positive cycle for divisors greater than one. + // throw new InvalidOperationException($"Divisor cycle solver returned zero for divisor {divisor}."); + // } + } + + // Keep these remainder steppers in place so future updates continue reusing the previously computed residues. + // They are critical for avoiding repeated full Montgomery exponentiation work when scanning divisors. + CycleRemainderStepper cycleStepper = _cycleStepper; + cycleStepper.CycleLength = divisorCycle; + if (cycleStepper.Initialize(primes[0]) == 0UL) + { + return true; + } + + if (_exponentStepper is not { } exponentStepper) + { + _exponentStepper = exponentStepper = new(divisorData, divisorCycle); + } + else + { + if (!exponentStepper.MatchesDivisor(divisorData, divisorCycle)) + { + _exponentStepper = exponentStepper = new(divisorData, divisorCycle); + } + } + + if (exponentStepper.InitializeCpuIsUnity(primes[0])) + { + return true; + } + + for (int i = 1; i < length; i++) + { + if (cycleStepper.ComputeNext(primes[i]) != 0UL) + { + continue; + } + + if (exponentStepper.ComputeNextIsUnity(primes[i])) + { + return true; + } + } + + return false; + } +}