diff --git a/DeviceDependentGenerator/DeviceDependentGenerator.cs b/DeviceDependentGenerator/DeviceDependentGenerator.cs index ba8632b..1ae163a 100644 --- a/DeviceDependentGenerator/DeviceDependentGenerator.cs +++ b/DeviceDependentGenerator/DeviceDependentGenerator.cs @@ -235,7 +235,8 @@ public sealed class DeviceDependentGenerator : IIncrementalGenerator private static TypeDeclarationSyntax TransformTemplateType(TypeDeclarationSyntax templateType, string generatedTypeName) { TypeDeclarationSyntax withoutAttribute = RemoveDeviceDependentAttribute(templateType); - return withoutAttribute.WithIdentifier(SyntaxFactory.Identifier(generatedTypeName)); + TypeDeclarationSyntax renamed = (TypeDeclarationSyntax)new SelfIdentifierRewriter(templateType.Identifier.ValueText, generatedTypeName).Visit(withoutAttribute)!; + return renamed.WithIdentifier(SyntaxFactory.Identifier(generatedTypeName)); } private static TypeDeclarationSyntax RemoveDeviceDependentAttribute(TypeDeclarationSyntax typeDeclaration) @@ -271,6 +272,28 @@ public sealed class DeviceDependentGenerator : IIncrementalGenerator return typeDeclaration.WithAttributeLists(SyntaxFactory.List(newLists)); } + private sealed class SelfIdentifierRewriter : CSharpSyntaxRewriter + { + private readonly string templateName; + private readonly string generatedName; + + public SelfIdentifierRewriter(string templateName, string generatedName) + { + this.templateName = templateName; + this.generatedName = generatedName; + } + + public override SyntaxToken VisitToken(SyntaxToken token) + { + if (token.IsKind(SyntaxKind.IdentifierToken) && string.Equals(token.ValueText, templateName, StringComparison.Ordinal)) + { + return SyntaxFactory.Identifier(token.LeadingTrivia, generatedName, token.TrailingTrivia); + } + + return base.VisitToken(token); + } + } + private static bool IsDeviceDependentTemplateAttribute(NameSyntax attributeName) { string text = attributeName.ToString(); @@ -380,6 +403,11 @@ public sealed class DeviceDependentGenerator : IIncrementalGenerator return generatedFileName; } + if (relativeDirectory.StartsWith("..", StringComparison.Ordinal)) + { + return generatedFileName; + } + relativeDirectory = relativeDirectory.Replace('\\', '/'); return relativeDirectory.TrimEnd('/') + "/" + generatedFileName; } diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2ModBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Pow2ModBenchmarks.cs index 5ecb650..0149d29 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2ModBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2ModBenchmarks.cs @@ -161,7 +161,7 @@ public class Pow2ModBenchmarks for (int i = 0; i < SampleCount; i++) { - ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(divisors[i].Modulus); + ulong cycle = MersenneDivisorCyclesGpu.CalculateCycleLength(divisors[i].Modulus); checksum ^= exponents[i].Pow2MontgomeryModWithCycleConvertToStandardCpu(cycle, divisors[i]); } @@ -183,7 +183,7 @@ public class Pow2ModBenchmarks var gpu = PrimeOrderCalculatorAccelerator.Rent(1); for (int i = 0; i < SampleCount; i++) { - ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(divisors[i].Modulus); + ulong cycle = MersenneDivisorCyclesGpu.CalculateCycleLength(divisors[i].Modulus); checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(gpu, cycle, divisors[i]); } @@ -500,7 +500,7 @@ public class Pow2ModBenchmarks Console.WriteLine($"Calculating small cycle {smallModulus}"); smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); smallExponents[i] = NextSmallExponent(random); - smallCycles[i] = MersenneDivisorCycles.CalculateCycleLengthCpu(smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); + smallCycles[i] = MersenneDivisorCyclesCpu.CalculateCycleLength(smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); (ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(random); Console.WriteLine($"Calculating large cycle {largeModulus}"); @@ -616,7 +616,7 @@ public class Pow2ModBenchmarks MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(modulus); if (modulus <= 1UL || (modulus & 1UL) == 0UL) { - return MersenneDivisorCycles.CalculateCycleLengthCpu(modulus, divisorData); + return MersenneDivisorCyclesCpu.CalculateCycleLength(modulus, divisorData); } ulong order = PrimeOrderCalculator.CalculateCpu( @@ -632,14 +632,14 @@ public class Pow2ModBenchmarks } previousPrimeOrder = null; - return MersenneDivisorCycles.CalculateCycleLengthCpu(modulus, divisorData); + return MersenneDivisorCyclesCpu.CalculateCycleLength(modulus, divisorData); } private static UInt128 CalculateCycleLengthWithHeuristicsCpu(UInt128 modulus, ref UInt128? previousWidePrimeOrder) { if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero) { - return MersenneDivisorCycles.GetCycleCpu(modulus); + return MersenneDivisorCyclesCpu.GetCycle(modulus); } UInt128 order = PrimeOrderCalculator.CalculateCpu( @@ -654,7 +654,7 @@ public class Pow2ModBenchmarks } previousWidePrimeOrder = null; - return MersenneDivisorCycles.GetCycleCpu(modulus); + return MersenneDivisorCyclesCpu.GetCycle(modulus); } } @@ -683,7 +683,7 @@ public class Pow2ModBenchmarks MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(modulus); if (modulus <= 1UL || (modulus & 1UL) == 0UL) { - return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLengthCpu(modulus, divisorData); + return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCyclesCpu.CalculateCycleLength(modulus, divisorData); } #if DEBUG @@ -704,7 +704,7 @@ public class Pow2ModBenchmarks _previousPrimeOrder = null; Console.WriteLine($"Heuristic failed for {modulus}, falling back to full cycle calculation"); - return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLengthCpu(modulus, divisorData); + return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCyclesCpu.CalculateCycleLength(modulus, divisorData); } private ulong CalculateCycleLengthWithHeuristicsGpu(PrimeOrderCalculatorAccelerator gpu, ulong modulus, ulong fallbackOrder = 0UL) @@ -712,7 +712,7 @@ public class Pow2ModBenchmarks MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(modulus); if (modulus <= 1UL || (modulus & 1UL) == 0UL) { - return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, modulus, divisorData); + return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, modulus, divisorData); } #if DEBUG @@ -734,14 +734,14 @@ public class Pow2ModBenchmarks _previousPrimeOrder = null; Console.WriteLine($"Heuristic failed for {modulus}, falling back to full cycle calculation"); - return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, modulus, divisorData); + return fallbackOrder != 0UL ? fallbackOrder : MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, modulus, divisorData); } private UInt128 CalculateCycleLengthWithHeuristicsCpu(UInt128 modulus) { if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero) { - return MersenneDivisorCycles.GetCycleCpu(modulus); + return MersenneDivisorCyclesCpu.GetCycle(modulus); } #if DEBUG @@ -761,14 +761,14 @@ public class Pow2ModBenchmarks _previousWidePrimeOrder = null; Console.WriteLine($"Wide heuristic failed for {modulus}, falling back to full cycle calculation"); - return MersenneDivisorCycles.GetCycleCpu(modulus); + return MersenneDivisorCyclesCpu.GetCycle(modulus); } private UInt128 CalculateCycleLengthWithHeuristicsGpu(PrimeOrderCalculatorAccelerator gpu, UInt128 modulus) { if (modulus <= UInt128.One || (modulus & UInt128.One) == UInt128.Zero) { - return MersenneDivisorCycles.GetCycleGpu(gpu, modulus); + return MersenneDivisorCyclesGpu.GetCycle(gpu, modulus); } #if DEBUG @@ -789,7 +789,7 @@ public class Pow2ModBenchmarks _previousWidePrimeOrder = null; Console.WriteLine($"Wide heuristic failed for {modulus}, falling back to full cycle calculation"); - return MersenneDivisorCycles.GetCycleGpu(gpu, modulus); + return MersenneDivisorCyclesGpu.GetCycle(gpu, modulus); } private static ulong ComputeMontgomeryResidue(ulong value, ulong modulus) => (ulong)((UInt128)value * (UInt128.One << 64) % modulus); @@ -1209,3 +1209,4 @@ public class Pow2ModBenchmarks return (ulong)(product % modulus); } } + diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs index b1557c8..c31fda8 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModBenchmarks.cs @@ -39,7 +39,7 @@ public class Pow2MontgomeryModBenchmarks ulong smallModulus = NextSmallOddModulus(); _smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus); _smallExponents[i] = NextSmallExponent(); - _smallCycles[i] = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); + _smallCycles[i] = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, smallModulus, MontgomeryDivisorData.FromModulus(smallModulus)); Console.WriteLine($"Calculating large cycle {i + 1} with heuristics"); (ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle(); @@ -524,3 +524,4 @@ public class Pow2MontgomeryModBenchmarks } } + diff --git a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModWindowedBenchmarkData.cs b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModWindowedBenchmarkData.cs index 8996a74..3bce73b 100644 --- a/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModWindowedBenchmarkData.cs +++ b/EvenPerfectBitScanner.Benchmarks/Pow2MontgomeryModWindowedBenchmarkData.cs @@ -67,7 +67,7 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider Pow2MontgomeryModWindowedBenchmarkSeed seed = seeds[i]; ulong modulus = 2UL * seed.Multiplier * seed.Exponent + 1UL; MontgomeryDivisorData divisor = MontgomeryDivisorData.FromModulus(modulus); - ulong cycleLength = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, modulus, divisor); + ulong cycleLength = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, modulus, divisor); ulong reducedExponent = seed.Exponent % cycleLength; cases[i] = new Pow2MontgomeryModWindowedBenchmarkCase( seed.Exponent, @@ -94,3 +94,4 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider public ulong Multiplier { get; } } } + diff --git a/PerfectNumbers.Core.Tests/MersenneDivisorCyclesGenerateGpuTests.cs b/PerfectNumbers.Core.Tests/MersenneDivisorCyclesGenerateGpuTests.cs index 64a7f03..ecce6ab 100644 --- a/PerfectNumbers.Core.Tests/MersenneDivisorCyclesGenerateGpuTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneDivisorCyclesGenerateGpuTests.cs @@ -14,7 +14,7 @@ public class MersenneDivisorCyclesGenerateGpuTests var gpu = PrimeOrderCalculatorAccelerator.Rent(1); try { - MersenneDivisorCycles.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0); + MersenneDivisorCyclesGpu.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0); var pairs = new List<(ulong divisor, ulong cycle)>(); using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); using var reader = new BinaryReader(stream); @@ -41,7 +41,7 @@ public class MersenneDivisorCyclesGenerateGpuTests { expected.Should().Contain(pair.divisor); MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor); - pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLengthCpu(pair.divisor, divisorData)); + pair.cycle.Should().Be(MersenneDivisorCyclesCpu.CalculateCycleLength(pair.divisor, divisorData)); } } finally @@ -61,7 +61,7 @@ public class MersenneDivisorCyclesGenerateGpuTests string path = Path.GetTempFileName(); try { - MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4); + MersenneDivisorCyclesCpu.Generate(path, maxDivisor: 50UL, threads: 4); var pairs = new List<(ulong divisor, ulong cycle)>(); using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); @@ -91,12 +91,12 @@ public class MersenneDivisorCyclesGenerateGpuTests { uniqueDivisors.Add(pair.divisor).Should().BeTrue(); MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor); - pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLengthCpu(pair.divisor, divisorData)); + pair.cycle.Should().Be(MersenneDivisorCyclesCpu.CalculateCycleLength(pair.divisor, divisorData)); } uniqueDivisors.Should().BeEquivalentTo(expected); - var (_, completeCount) = MersenneDivisorCycles.FindLast(path); + var (_, completeCount) = MersenneDivisorCyclesCpu.FindLast(path); completeCount.Should().Be(expected.Count); } finally diff --git a/PerfectNumbers.Core.Tests/MersenneDivisorCyclesTests.cs b/PerfectNumbers.Core.Tests/MersenneDivisorCyclesTests.cs index b288fea..6d8af30 100644 --- a/PerfectNumbers.Core.Tests/MersenneDivisorCyclesTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneDivisorCyclesTests.cs @@ -23,8 +23,8 @@ public class MersenneDivisorCyclesTests try { MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); - MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData).Should().Be(expected); - MersenneDivisorCycles.CalculateCycleLengthGpu(divisor).Should().Be(expected); + MersenneDivisorCyclesCpu.CalculateCycleLength(divisor, divisorData).Should().Be(expected); + MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, divisor, divisorData).Should().Be(expected); } finally { diff --git a/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs index 0b4c3bb..b938c87 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberDivisorCpuTesterTests.cs @@ -41,13 +41,13 @@ public class MersenneNumberDivisorCpuTesterTests ulong[] primes = [5UL, 7UL, 11UL, 13UL]; - ulong cycle23 = MersenneDivisorCycles.CalculateCycleLengthCpu(23UL, MontgomeryDivisorData.FromModulus(23UL)); + ulong cycle23 = MersenneDivisorCyclesCpu.CalculateCycleLength(23UL, MontgomeryDivisorData.FromModulus(23UL)); session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primes).Should().BeTrue(); ulong[] primesWithoutHit = [5UL, 7UL, 13UL]; session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primesWithoutHit).Should().BeFalse(); - ulong cycle31 = MersenneDivisorCycles.CalculateCycleLengthCpu(31UL, MontgomeryDivisorData.FromModulus(31UL)); + ulong cycle31 = MersenneDivisorCyclesCpu.CalculateCycleLength(31UL, MontgomeryDivisorData.FromModulus(31UL)); session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes).Should().BeTrue(); ulong[] primesBeforeHit = [7UL, 11UL, 13UL]; @@ -63,7 +63,7 @@ public class MersenneNumberDivisorCpuTesterTests ulong[] exponents = [6UL, 7UL, 9UL, 10UL, 12UL]; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL); - ulong cycle = MersenneDivisorCycles.CalculateCycleLengthCpu(7UL, divisorData); + ulong cycle = MersenneDivisorCyclesCpu.CalculateCycleLength(7UL, divisorData); session.CheckDivisor(7UL, divisorData, cycle, exponents).Should().BeTrue(); ulong[] nonDivisibleExponents = [5UL, 11UL]; @@ -79,7 +79,7 @@ public class MersenneNumberDivisorCpuTesterTests ulong[] exponents = [10UL, 11UL, 20UL, 21UL, 30UL]; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL); - ulong cycle = MersenneDivisorCycles.CalculateCycleLengthCpu(11UL, divisorData); + ulong cycle = MersenneDivisorCyclesCpu.CalculateCycleLength(11UL, divisorData); session.CheckDivisor(11UL, divisorData, cycle, exponents).Should().BeTrue(); ulong[] nonDivisibleExponents = [5UL, 17UL]; @@ -104,3 +104,4 @@ public class MersenneNumberDivisorCpuTesterTests } } + diff --git a/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs index ab7d730..99ed521 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberDivisorGpuTesterTests.cs @@ -235,13 +235,13 @@ public class MersenneNumberDivisorGpuTesterTests ulong[] primes = [31UL, 37UL, 41UL, 43UL]; - ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL)); + ulong cycle223 = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL)); session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue(); ulong[] primesBeforeHit = [31UL]; session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse(); - ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 13367UL, MontgomeryDivisorData.FromModulus(13367UL)); + ulong cycle13367 = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 13367UL, MontgomeryDivisorData.FromModulus(13367UL)); session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primes).Should().BeTrue(); ulong[] primesWithoutMatchingExponent = [31UL, 37UL, 43UL]; @@ -271,7 +271,7 @@ public class MersenneNumberDivisorGpuTesterTests ulong[] primes = [31UL, 37UL, 41UL, 43UL, 47UL]; - ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL)); + ulong cycle223 = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL)); session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue(); ulong[] primesBeforeHit = [31UL]; @@ -302,7 +302,7 @@ public class MersenneNumberDivisorGpuTesterTests ulong[] exponents = [6UL, 7UL, 9UL, 10UL, 12UL]; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL); - ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 7UL, divisorData); + ulong cycle = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 7UL, divisorData); session.CheckDivisor(7UL, divisorData, cycle, exponents).Should().BeTrue(); @@ -329,7 +329,7 @@ public class MersenneNumberDivisorGpuTesterTests ulong[] exponents = [10UL, 11UL, 20UL, 21UL, 30UL]; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL); - ulong cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 11UL, divisorData); + ulong cycle = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 11UL, divisorData); session.CheckDivisor(11UL, divisorData, cycle, exponents).Should().BeTrue(); @@ -346,9 +346,9 @@ public class MersenneNumberDivisorGpuTesterTests [Trait("Category", "Fast")] public void ByDivisor_gpu_tester_skips_divisors_excluded_by_small_cycle_generation() { - var cycles = MersenneDivisorCycles.Shared; - var tableField = typeof(MersenneDivisorCycles).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!; - var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!; + var cycles = MersenneDivisorCyclesGpu.Shared; + var tableField = typeof(MersenneDivisorCyclesGpu).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!; + var smallCyclesField = typeof(MersenneDivisorCyclesGpu).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!; var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!; var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles); @@ -360,7 +360,7 @@ public class MersenneNumberDivisorGpuTesterTests ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1]; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(191UL); - patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, 191UL, divisorData); + patchedSmall[191] = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 191UL, divisorData); tableField.SetValue(cycles, patchedTable); smallCyclesField.SetValue(cycles, patchedSmall); @@ -393,7 +393,7 @@ public class MersenneNumberDivisorGpuTesterTests var session = new MersenneCpuDivisorScanSessionWithCpuOrder(); ulong[] primes = [31UL, 37UL, 41UL, 43UL, 47UL]; - ulong cycle223 = MersenneDivisorCycles.CalculateCycleLengthCpu(223UL, MontgomeryDivisorData.FromModulus(223UL)); + ulong cycle223 = MersenneDivisorCyclesCpu.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL)); session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue(); ulong[] primesBeforeHit = [31UL]; diff --git a/PerfectNumbers.Core.Tests/MersenneNumberOrderGpuTesterTests.cs b/PerfectNumbers.Core.Tests/MersenneNumberOrderGpuTesterTests.cs index 39709a3..4fd3e2b 100644 --- a/PerfectNumbers.Core.Tests/MersenneNumberOrderGpuTesterTests.cs +++ b/PerfectNumbers.Core.Tests/MersenneNumberOrderGpuTesterTests.cs @@ -45,9 +45,9 @@ public class MersenneNumberOrderGpuTesterTests writer.Write((ulong)uint.MaxValue); } - var cycles = MersenneDivisorCycles.Shared; - var tableField = typeof(MersenneDivisorCycles).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!; - var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!; + var cycles = MersenneDivisorCyclesGpu.Shared; + var tableField = typeof(MersenneDivisorCyclesGpu).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!; + var smallCyclesField = typeof(MersenneDivisorCyclesGpu).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!; var originalTable = ((List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!).Select(x => x).ToList(); var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles); @@ -88,4 +88,3 @@ public class MersenneNumberOrderGpuTesterTests isPrime.Should().Be(expectedPrime); } } - diff --git a/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs b/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs index 1f6ef99..9d0f1bd 100644 --- a/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs +++ b/PerfectNumbers.Core.Tests/PrimeOrderCalculatorTests.cs @@ -193,7 +193,7 @@ public class PrimeOrderCalculatorTests var gpu = PrimeOrderCalculatorAccelerator.Rent(1); try { - var mersenneDivisorCycles = MersenneDivisorCycles.GetCycleGpu(gpu, prime).Should().Be((UInt128)1229782938247309446UL); + var mersenneDivisorCycles = MersenneDivisorCyclesGpu.GetCycle(gpu, prime).Should().Be((UInt128)1229782938247309446UL); } finally { @@ -225,3 +225,4 @@ public class PrimeOrderCalculatorTests return order; } } + diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberIncrementalCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberIncrementalCpuTester.cs index 7be16a5..c2f6347 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberIncrementalCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberIncrementalCpuTester.cs @@ -4,7 +4,7 @@ namespace PerfectNumbers.Core.Cpu; public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType) { - private static readonly MersenneDivisorCycles _cycles = MersenneDivisorCycles.Shared; + private static readonly MersenneDivisorCyclesCpu _cycles = MersenneDivisorCyclesCpu.Shared; private readonly GpuKernelType _kernelType = kernelType; public void Scan(PrimeOrderCalculatorAccelerator gpu, ulong exponent, UInt128 twoP, LastDigit lastDigit, UInt128 maxK, ref bool isPrime) @@ -31,7 +31,7 @@ public class MersenneNumberIncrementalCpuTester(GpuKernelType kernelType) { // TODO: Replace this direct GetCycle call with DivisorCycleCache.Lookup so we reuse the single snapshot block // and compute missing cycles on the configured device without queuing additional block generation. - qCycle = MersenneDivisorCycles.GetCycleCpu(q); + qCycle = MersenneDivisorCyclesCpu.GetCycle(q); shouldCheck = auto.Mod10R != 5UL; if (shouldCheck) { diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs index 3e2f59b..d0fcfba 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberLucasLehmerCpuTester.cs @@ -34,7 +34,7 @@ public class MersenneNumberLucasLehmerCpuTester for (UInt128 i = UInt128.Zero; i < limit; i++) { // Lucas–Lehmer squares the evolving residue, so we stay on the Montgomery powmod path rather than the pow2-specific helper. - s = s.PowModWithCycle(two, MersenneDivisorCycles.GetCycleCpu(m)) - two; + s = s.PowModWithCycle(two, MersenneDivisorCyclesCpu.GetCycle(m)) - two; if (s < UInt128.Zero) { s += m; @@ -44,3 +44,4 @@ public class MersenneNumberLucasLehmerCpuTester return s == UInt128.Zero; } } + diff --git a/PerfectNumbers.Core/Cpu/MersenneNumberOrderCpuTester.cs b/PerfectNumbers.Core/Cpu/MersenneNumberOrderCpuTester.cs index 1f7f1f8..30d5d99 100644 --- a/PerfectNumbers.Core/Cpu/MersenneNumberOrderCpuTester.cs +++ b/PerfectNumbers.Core/Cpu/MersenneNumberOrderCpuTester.cs @@ -15,7 +15,7 @@ public class MersenneNumberOrderCpuTester(GpuKernelType kernelType) while (k <= maxK && Volatile.Read(ref isPrime)) { - qCycle = MersenneDivisorCycles.GetCycleCpu(q); + qCycle = MersenneDivisorCyclesCpu.GetCycle(q); // TODO: When this lookup misses the snapshot, invoke the configured device to compute the // single required cycle on demand without persisting it so the CPU order path retains the // cycle-stepping speedups while honoring the no-extra-cache constraint for large divisors. @@ -76,4 +76,4 @@ public class MersenneNumberOrderCpuTester(GpuKernelType kernelType) return; } -} \ No newline at end of file +} diff --git a/PerfectNumbers.Core/DivisorCycleCache.cs b/PerfectNumbers.Core/DivisorCycleCache.cs index f7a0c98..4725bf1 100644 --- a/PerfectNumbers.Core/DivisorCycleCache.cs +++ b/PerfectNumbers.Core/DivisorCycleCache.cs @@ -39,12 +39,12 @@ public sealed class DivisorCycleCache Accelerator accelerator = AcceleratorPool.Shared.Accelerators[acceleratorIndex]; _accelerator = accelerator; _gpuKernel = LoadKernel(accelerator); - _snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); + _snapshot = MersenneDivisorCyclesCpu.Shared.ExportSmallCyclesSnapshot(); } public void RefreshSnapshot() { - _snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); + _snapshot = MersenneDivisorCyclesCpu.Shared.ExportSmallCyclesSnapshot(); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] @@ -60,7 +60,7 @@ public sealed class DivisorCycleCache return cached; } - return MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData); + return MersenneDivisorCyclesCpu.CalculateCycleLength(divisor, divisorData); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] @@ -89,7 +89,7 @@ public sealed class DivisorCycleCache return cycleLength; } - cycleLength = MersenneDivisorCycles.CalculateCycleLengthHybrid(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true); + cycleLength = MersenneDivisorCyclesHybrid.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true); return cycleLength; } @@ -122,7 +122,7 @@ public sealed class DivisorCycleCache return cycleLength; } - cycleLength = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true); + cycleLength = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: true); return cycleLength; } @@ -179,7 +179,7 @@ public sealed class DivisorCycleCache return cached; } - return MersenneDivisorCycles.CalculateCycleLengthHybrid(gpu, divisor, divisorData); + return MersenneDivisorCyclesHybrid.CalculateCycleLength(gpu, divisor, divisorData); } public void GetCycleLengthsHybrid(PrimeOrderCalculatorAccelerator gpu, ReadOnlySpan divisors, Span cycles) @@ -270,7 +270,7 @@ public sealed class DivisorCycleCache int targetIndex = indices[i]; ulong divisor = divisors[targetIndex]; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); - cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData); + cycles[targetIndex] = MersenneDivisorCyclesCpu.CalculateCycleLength(divisor, divisorData); } } @@ -282,7 +282,7 @@ public sealed class DivisorCycleCache int targetIndex = indices[i]; ulong divisor = divisors[targetIndex]; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); - cycles[targetIndex] = MersenneDivisorCycles.CalculateCycleLengthHybrid(gpu, divisor, divisorData); + cycles[targetIndex] = MersenneDivisorCyclesHybrid.CalculateCycleLength(gpu, divisor, divisorData); } } diff --git a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs index bb6839a..5e4c243 100644 --- a/PerfectNumbers.Core/Gpu/GpuKernelPool.cs +++ b/PerfectNumbers.Core/Gpu/GpuKernelPool.cs @@ -150,7 +150,7 @@ public class GpuKernelPool return; } - var host = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); + var host = MersenneDivisorCyclesGpu.Shared.ExportSmallCyclesSnapshot(); MemoryBuffer1D? device; // lock (accelerator) diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs index 499d2d1..7c2f97e 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs @@ -452,9 +452,9 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong ResolveDivisorCycle(PrimeOrderCalculatorAccelerator gpu, ulong divisor, ulong prime, in MontgomeryDivisorData divisorData) { - if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentGpu(gpu, divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) + if (!MersenneDivisorCyclesGpu.TryCalculateCycleLengthForExponent(gpu, divisor, prime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) { - return MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); + return MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); } return computedCycle; @@ -601,9 +601,9 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN var gpu = _primeOrderCalculatorAccelerator; if (cycle == 0UL) { - if (!MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu(divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) + if (!MersenneDivisorCyclesCpu.TryCalculateCycleLengthForExponent(divisor, firstPrime, divisorData, out ulong computedCycle, out bool primeOrderFailed) || computedCycle == 0UL) { - cycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); + cycle = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); } else { @@ -740,3 +740,4 @@ public sealed partial class MersenneNumberDivisorByDivisorGpuTester : IMersenneN return new DivisorScanSession(gpu, this); } } + diff --git a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorGpuTester.cs b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorGpuTester.cs index b2ae032..7a812c8 100644 --- a/PerfectNumbers.Core/Gpu/MersenneNumberDivisorGpuTester.cs +++ b/PerfectNumbers.Core/Gpu/MersenneNumberDivisorGpuTester.cs @@ -35,7 +35,7 @@ public sealed class MersenneNumberDivisorGpuTester public static void BuildDivisorCandidates() { - ulong[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); + ulong[] snapshot = MersenneDivisorCyclesGpu.Shared.ExportSmallCyclesSnapshot(); (ulong divisor, uint cycle)[] list = new (ulong divisor, uint cycle)[snapshot.Length / 2]; ulong cycle; int count = 0, i, snapshotLength = snapshot.Length; @@ -142,7 +142,7 @@ public sealed class MersenneNumberDivisorGpuTester continue; } - UInt128 cycle128 = MersenneDivisorCycles.GetCycleGpu(gpu, d); + UInt128 cycle128 = MersenneDivisorCyclesGpu.GetCycle(gpu, d); if ((UInt128)p % cycle128 != UInt128.Zero) { continue; diff --git a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs index 6f37522..eb4c2b3 100644 --- a/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicCombinedPrimeTester.cs @@ -109,7 +109,7 @@ public sealed class HeuristicCombinedPrimeTester internal static readonly ulong[] CombinedDivisorsEnding7Squares; internal static readonly ulong[] CombinedDivisorsEnding9Squares; - private static readonly ulong[] HeuristicSmallCycleSnapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); + private static readonly ulong[] HeuristicSmallCycleSnapshot = MersenneDivisorCyclesCpu.Shared.ExportSmallCyclesSnapshot(); internal enum CombinedDivisorPattern : byte { @@ -507,7 +507,7 @@ public sealed class HeuristicCombinedPrimeTester ulong divisor = preparation.Candidate.Value; MontgomeryDivisorData divisorData = preparation.DivisorData; - bool trySuccess = MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + bool trySuccess = MersenneDivisorCyclesCpu.TryCalculateCycleLengthForExponent( divisor, exponent, divisorData, @@ -523,7 +523,7 @@ public sealed class HeuristicCombinedPrimeTester } primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL; - ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); + ulong resolvedCycle = MersenneDivisorCyclesCpu.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); cycleFromHint = false; cycleComputed = resolvedCycle != 0UL; return resolvedCycle; @@ -549,7 +549,7 @@ public sealed class HeuristicCombinedPrimeTester ulong divisor = preparation.Candidate.Value; MontgomeryDivisorData divisorData = preparation.DivisorData; - bool trySuccess = MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + bool trySuccess = MersenneDivisorCyclesCpu.TryCalculateCycleLengthForExponent( divisor, exponent, divisorData, @@ -565,7 +565,7 @@ public sealed class HeuristicCombinedPrimeTester } primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL; - ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); + ulong resolvedCycle = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); cycleFromHint = false; cycleComputed = resolvedCycle != 0UL; return resolvedCycle; diff --git a/PerfectNumbers.Core/HeuristicPrimeTester.cs b/PerfectNumbers.Core/HeuristicPrimeTester.cs index 7098b20..b0d66a8 100644 --- a/PerfectNumbers.Core/HeuristicPrimeTester.cs +++ b/PerfectNumbers.Core/HeuristicPrimeTester.cs @@ -28,7 +28,7 @@ public sealed class HeuristicPrimeTester private static readonly bool UseHeuristicGroupBTrialDivision = false; // Temporary fallback gate for Group B. - private static readonly ulong[] HeuristicSmallCycleSnapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot(); + private static readonly ulong[] HeuristicSmallCycleSnapshot = MersenneDivisorCyclesCpu.Shared.ExportSmallCyclesSnapshot(); [ThreadStatic] private static BidirectionalCycleRemainderStepper _mod10Stepper; @@ -437,7 +437,7 @@ public sealed class HeuristicPrimeTester ulong divisor = preparation.Candidate.Value; MontgomeryDivisorData divisorData = preparation.DivisorData; - bool trySuccess = MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + bool trySuccess = MersenneDivisorCyclesCpu.TryCalculateCycleLengthForExponent( divisor, exponent, divisorData, @@ -453,7 +453,7 @@ public sealed class HeuristicPrimeTester } primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL; - ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLengthCpu(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); + ulong resolvedCycle = MersenneDivisorCyclesCpu.CalculateCycleLength(divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); cycleFromHint = false; cycleComputed = resolvedCycle != 0UL; return resolvedCycle; @@ -479,7 +479,7 @@ public sealed class HeuristicPrimeTester ulong divisor = preparation.Candidate.Value; MontgomeryDivisorData divisorData = preparation.DivisorData; - bool trySuccess = MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + bool trySuccess = MersenneDivisorCyclesCpu.TryCalculateCycleLengthForExponent( divisor, exponent, divisorData, @@ -495,7 +495,7 @@ public sealed class HeuristicPrimeTester } primeOrderFailed = primeOrderFailedLocal || !trySuccess || computedCycle == 0UL; - ulong resolvedCycle = MersenneDivisorCycles.CalculateCycleLengthGpu(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); + ulong resolvedCycle = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, divisor, divisorData, skipPrimeOrderHeuristic: primeOrderFailed); cycleFromHint = false; cycleComputed = resolvedCycle != 0UL; return resolvedCycle; diff --git a/PerfectNumbers.Core/MersenneDivisorCycles.cs b/PerfectNumbers.Core/MersenneDivisorCyclesTemplate.cs similarity index 65% rename from PerfectNumbers.Core/MersenneDivisorCycles.cs rename to PerfectNumbers.Core/MersenneDivisorCyclesTemplate.cs index d8dbb47..18198b9 100644 --- a/PerfectNumbers.Core/MersenneDivisorCycles.cs +++ b/PerfectNumbers.Core/MersenneDivisorCyclesTemplate.cs @@ -11,15 +11,17 @@ using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core; -public class MersenneDivisorCycles +[DeviceDependentTemplate(typeof(ComputationDevice))] +public struct MersenneDivisorCyclesTemplate { - private List<(ulong divisor, ulong cycleLength)> _table = []; + private List<(ulong divisor, ulong cycleLength)> _table; // Lightweight read-mostly cache for small divisors (<= 4,000,000). 0 => unknown private ulong[]? _smallCycles; - public static MersenneDivisorCycles Shared { get; } = new MersenneDivisorCycles(); + public static readonly MersenneDivisorCyclesTemplate Shared = new(); - public MersenneDivisorCycles() + public MersenneDivisorCyclesTemplate() { + _table = []; } public void LoadFrom(string path) @@ -50,7 +52,11 @@ public class MersenneDivisorCycles return _smallCycles!; } +#if DEVICE_GPU || DEVICE_HYBRID public static bool CycleEqualsExponent(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent) +#else + public static bool CycleEqualsExponent(ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent) +#endif { if (CycleEqualsExponentForMersenneCandidate(divisor, divisorData, exponent)) { @@ -75,88 +81,20 @@ public class MersenneDivisorCycles } } - if (TryCalculateCycleLengthHeuristicCpu(divisor, divisorData, out ulong heuristicCycle) && heuristicCycle != 0UL) +#if DEVICE_GPU || DEVICE_HYBRID + if (TryCalculateCycleLengthHeuristic(gpu, divisor, divisorData, out ulong heuristicCycle, skipPrimeOrderHeuristic: false) && heuristicCycle != 0UL) +#else + if (TryCalculateCycleLengthHeuristic(divisor, divisorData, out ulong heuristicCycle, skipPrimeOrderHeuristic: false) && heuristicCycle != 0UL) +#endif { return heuristicCycle == exponent; } - if (TryCalculateCycleLengthForExponentCpu(divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL) - { - return cycleLength == exponent; - } - - return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; - } - - public static bool CycleEqualsExponentGpu(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent) - { - if (CycleEqualsExponentForMersenneCandidate(divisor, divisorData, exponent)) - { - return true; - } - - if (exponent <= 1UL || divisor <= 1UL || (divisor & 1UL) == 0UL) - { - return false; - } - - if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles) - { - var small = Shared._smallCycles; - if (small is not null) - { - ulong cached = small[(int)divisor]; - if (cached != 0UL) - { - return cached == exponent; - } - } - } - - if (TryCalculateCycleLengthHeuristicGpu(gpu, divisor, divisorData, out ulong heuristicCycle) && heuristicCycle != 0UL) - { - return heuristicCycle == exponent; - } - - if (TryCalculateCycleLengthForExponentCpu(divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL) - { - return cycleLength == exponent; - } - - return exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisorData) == 1UL; - } - - public static bool CycleEqualsExponentHybrid(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, ulong exponent) - { - if (CycleEqualsExponentForMersenneCandidate(divisor, divisorData, exponent)) - { - return true; - } - - if (exponent <= 1UL || divisor <= 1UL || (divisor & 1UL) == 0UL) - { - return false; - } - - if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles) - { - var small = Shared._smallCycles; - if (small is not null) - { - ulong cached = small[(int)divisor]; - if (cached != 0UL) - { - return cached == exponent; - } - } - } - - if (TryCalculateCycleLengthHeuristicHybrid(gpu, divisor, divisorData, out ulong heuristicCycle) && heuristicCycle != 0UL) - { - return heuristicCycle == exponent; - } - - if (TryCalculateCycleLengthForExponentCpu(divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL) +#if DEVICE_GPU || DEVICE_HYBRID + if (TryCalculateCycleLengthForExponent(gpu, divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL) +#else + if (TryCalculateCycleLengthForExponent(divisor, exponent, divisorData, out ulong cycleLength, out bool _) && cycleLength != 0UL) +#endif { return cycleLength == exponent; } @@ -175,7 +113,7 @@ public class MersenneDivisorCycles if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles) { - MersenneDivisorCycles shared = Shared; + MersenneDivisorCyclesTemplate shared = Shared; ulong[]? small = shared._smallCycles; if (small is not null) { @@ -239,7 +177,12 @@ public class MersenneDivisorCycles } } - public ulong GetCycle(ulong divisor, in MontgomeryDivisorData divisorData) + public readonly ulong GetCycle( +#if DEVICE_GPU || DEVICE_HYBRID + PrimeOrderCalculatorAccelerator gpu, +#endif + ulong divisor, + in MontgomeryDivisorData divisorData) { // Fast-path: in-memory array for small divisors if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles) @@ -284,106 +227,18 @@ public class MersenneDivisorCycles // TODO: Route this miss to an ephemeral single-cycle computation on the device selected by the current // configuration (and skip persisting the result) when the divisor falls outside the in-memory snapshot // so we respect the large-p memory limits and avoid extra cache locks. - return CalculateCycleLengthCpu(divisor, divisorData); +#if DEVICE_GPU || DEVICE_HYBRID + return CalculateCycleLength(gpu, divisor, divisorData); +#else + return CalculateCycleLength(divisor, divisorData); +#endif } - public ulong GetCycleGpu(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData) - { - // Fast-path: in-memory array for small divisors - if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles) - { - var arr = _smallCycles; - if (arr is not null) - { - ulong cached = arr[(int)divisor]; - if (cached != 0UL) - { - return cached; - } - } - } - - // Binary search for divisor in sorted _table - int mid, right, left = 0; - ulong d; - List<(ulong divisor, ulong cycleLength)> cycles = _table; - - right = cycles.Count - 1; - while (left <= right) - { - mid = left + ((right - left) >> 1); - d = cycles[mid].divisor; - - if (d == divisor) - { - return cycles[mid].cycleLength; - } - - if (d < divisor) - { - left = mid + 1; - } - else - { - right = mid - 1; - } - } - - // TODO: Route this miss to an ephemeral single-cycle computation on the device selected by the current - // configuration (and skip persisting the result) when the divisor falls outside the in-memory snapshot - // so we respect the large-p memory limits and avoid extra cache locks. - return CalculateCycleLengthGpu(gpu, divisor, divisorData); - } - - public ulong GetCycleHybrid(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData) - { - // Fast-path: in-memory array for small divisors - if (divisor <= PerfectNumberConstants.MaxQForDivisorCycles) - { - var arr = _smallCycles; - if (arr is not null) - { - ulong cached = arr[(int)divisor]; - if (cached != 0UL) - { - return cached; - } - } - } - - // Binary search for divisor in sorted _table - int mid, right, left = 0; - ulong d; - List<(ulong divisor, ulong cycleLength)> cycles = _table; - - right = cycles.Count - 1; - while (left <= right) - { - mid = left + ((right - left) >> 1); - d = cycles[mid].divisor; - - if (d == divisor) - { - return cycles[mid].cycleLength; - } - - if (d < divisor) - { - left = mid + 1; - } - else - { - right = mid - 1; - } - } - - // TODO: Route this miss to an ephemeral single-cycle computation on the device selected by the current - // configuration (and skip persisting the result) when the divisor falls outside the in-memory snapshot - // so we respect the large-p memory limits and avoid extra cache locks. - return CalculateCycleLengthHybrid(gpu, divisor, divisorData); - } - - public static UInt128 GetCycleCpu(UInt128 divisor) +#if DEVICE_GPU || DEVICE_HYBRID + public static UInt128 GetCycle(PrimeOrderCalculatorAccelerator gpu, UInt128 divisor) +#else + public static UInt128 GetCycle(UInt128 divisor) +#endif { // For divisor = 2^k, cycle is 1 if ((divisor & (divisor - UInt128.One)) == UInt128.Zero) @@ -395,106 +250,32 @@ public class MersenneDivisorCycles { ulong divisor64 = (ulong)divisor; MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64); - ulong cycle = CalculateCycleLengthCpu(divisor64, divisorData); - return (UInt128)cycle; - } - - UInt128 wideOrder = PrimeOrderCalculator.CalculateCpu( - divisor, - previousOrder: null, - PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); - if (wideOrder != UInt128.Zero) - { - return wideOrder; - } - - // Otherwise, find order of 2 mod divisor - // TODO: Port this UInt128 path to the unrolled-hex cycle calculator so wide - // divisors stop relying on the slow shift-and-subtract loop measured in the - // GPU benchmarks. - UInt128 order = UInt128.One; - UInt128 pow = UInt128Numbers.Two; - while (pow != UInt128.One) - { - pow <<= 1; - if (pow >= divisor) - { - pow -= divisor; - } - - order++; - } - - return order; - } - - public static UInt128 GetCycleHybrid(PrimeOrderCalculatorAccelerator gpu, UInt128 divisor) - { - // For divisor = 2^k, cycle is 1 - if ((divisor & (divisor - UInt128.One)) == UInt128.Zero) - { - return UInt128.One; - } - - if (divisor <= ulong.MaxValue) - { - ulong divisor64 = (ulong)divisor; - MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64); - ulong cycle = CalculateCycleLengthHybrid(gpu, divisor64, divisorData); - return (UInt128)cycle; - } - - UInt128 wideOrder = PrimeOrderCalculator.CalculateHybrid( - gpu, - divisor, - previousOrder: null, - PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); - if (wideOrder != UInt128.Zero) - { - return wideOrder; - } - - // Otherwise, find order of 2 mod divisor - // TODO: Port this UInt128 path to the unrolled-hex cycle calculator so wide - // divisors stop relying on the slow shift-and-subtract loop measured in the - // GPU benchmarks. - UInt128 order = UInt128.One; - UInt128 pow = UInt128Numbers.Two; - while (pow != UInt128.One) - { - pow <<= 1; - if (pow >= divisor) - { - pow -= divisor; - } - - order++; - } - - return order; - } - - public static UInt128 GetCycleGpu(PrimeOrderCalculatorAccelerator gpu, UInt128 divisor) - { - // For divisor = 2^k, cycle is 1 - if ((divisor & (divisor - UInt128.One)) == UInt128.Zero) - { - return UInt128.One; - } - - if (divisor <= ulong.MaxValue) - { - ulong divisor64 = (ulong)divisor; - MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor64); - ulong cycle = CalculateCycleLengthGpu(gpu, divisor64, divisorData); +#if DEVICE_GPU || DEVICE_HYBRID + ulong cycle = CalculateCycleLength(gpu, divisor64, divisorData); +#else + ulong cycle = CalculateCycleLength(divisor64, divisorData); +#endif return (UInt128)cycle; } +#if DEVICE_GPU UInt128 wideOrder = PrimeOrderCalculator.CalculateGpu( gpu, divisor, previousOrder: null, PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); +#elif DEVICE_HYBRID + UInt128 wideOrder = PrimeOrderCalculator.CalculateHybrid( + gpu, + divisor, + previousOrder: null, + PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); +#else + UInt128 wideOrder = PrimeOrderCalculator.CalculateCpu( + divisor, + previousOrder: null, + PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); +#endif if (wideOrder != UInt128.Zero) { return wideOrder; @@ -520,176 +301,10 @@ public class MersenneDivisorCycles return order; } - public static bool TryCalculateCycleLengthForExponentCpu( - ulong divisor, - ulong exponent, - in MontgomeryDivisorData divisorData, - out ulong cycleLength, - out bool primeOrderFailed) - { - cycleLength = 0UL; - primeOrderFailed = false; - - // EvenPerfectBitScanner never feeds powers of two into this helper, so leave the branch commented out to avoid redundant work. - // if ((divisor & (divisor - 1UL)) == 0UL) - // { - // cycleLength = 1UL; - // return true; - // } - - // The scanner only calls this path with prime exponents greater than one and odd divisors above three, keeping the small-parameter guard unreachable. - // if (divisor <= 3UL || exponent <= 1UL) - // { - // cycleLength = CalculateCycleLength(divisor, divisorData); - // return true; - // } - - ulong computedOrder = PrimeOrderCalculator.CalculateCpu( - divisor, - previousOrder: null, - divisorData, - PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); - if (computedOrder != 0UL) - { - cycleLength = computedOrder; - return true; - } - - primeOrderFailed = true; - - ulong phi = divisor - 1UL; - // A zero totient would imply divisor == 0, which never occurs on the tested path. Keep the guard documented without - // branching in the hot loop. - // if (phi == 0UL) - // { - // return false; - // } - - int twoCount = BitOperations.TrailingZeroCount(phi); - ulong reducedPhi = phi >> twoCount; - if (reducedPhi == 0UL || (reducedPhi % exponent) != 0UL) - { - return false; - } - - ulong k = reducedPhi / exponent; - // k == 0 would indicate reducedPhi < exponent, which the EvenPerfectBitScanner call flow prevents. Document the - // assumption instead of branching. - // if (k == 0UL) - // { - // return false; - // } - - Dictionary factorCounts = ThreadStaticPools.RentFactorCountDictionary(); - bool success = false; - - if (twoCount > 0) - { - factorCounts[2UL] = twoCount; - } - - bool exponentAccumulated = AccumulateFactors(exponent, factorCounts); - bool factorsAccumulated = exponentAccumulated && AccumulateFactors(k, factorCounts); - - if (factorsAccumulated) - { - cycleLength = ReduceOrder(divisorData, phi, factorCounts); - success = true; - primeOrderFailed = false; - } - - factorCounts.Clear(); - ThreadStaticPools.ReturnFactorCountDictionary(factorCounts); - return success; - } - - public static bool TryCalculateCycleLengthForExponentHybrid( - PrimeOrderCalculatorAccelerator gpu, - ulong divisor, - ulong exponent, - in MontgomeryDivisorData divisorData, - out ulong cycleLength, - out bool primeOrderFailed) - { - cycleLength = 0UL; - primeOrderFailed = false; - - // EvenPerfectBitScanner never feeds powers of two into this helper, so leave the branch commented out to avoid redundant work. - // if ((divisor & (divisor - 1UL)) == 0UL) - // { - // cycleLength = 1UL; - // return true; - // } - - // The scanner only calls this path with prime exponents greater than one and odd divisors above three, keeping the small-parameter guard unreachable. - // if (divisor <= 3UL || exponent <= 1UL) - // { - // cycleLength = CalculateCycleLength(divisor, divisorData); - // return true; - // } - - ulong computedOrder = PrimeOrderCalculator.CalculateHybrid( - gpu, - divisor, - previousOrder: null, - divisorData, - PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); - if (computedOrder != 0UL) - { - cycleLength = computedOrder; - return true; - } - - primeOrderFailed = true; - - ulong phi = divisor - 1UL; - // A zero totient would imply divisor == 0, which never occurs on the tested path. Keep the guard documented without - // branching in the hot loop. - // if (phi == 0UL) - // { - // return false; - // } - - int twoCount = BitOperations.TrailingZeroCount(phi); - ulong reducedPhi = phi >> twoCount; - if (reducedPhi == 0UL || (reducedPhi % exponent) != 0UL) - { - return false; - } - - ulong k = reducedPhi / exponent; - // k == 0 would indicate reducedPhi < exponent, which the EvenPerfectBitScanner call flow prevents. Document the - // assumption instead of branching. - // if (k == 0UL) - // { - // return false; - // } - - Dictionary factorCounts = ThreadStaticPools.RentFactorCountDictionary(); - bool success = false; - - if (twoCount > 0) - { - factorCounts[2UL] = twoCount; - } - - bool exponentAccumulated = AccumulateFactors(exponent, factorCounts); - bool factorsAccumulated = exponentAccumulated && AccumulateFactors(k, factorCounts); - - if (factorsAccumulated) - { - cycleLength = ReduceOrder(divisorData, phi, factorCounts); - success = true; - primeOrderFailed = false; - } - - factorCounts.Clear(); - ThreadStaticPools.ReturnFactorCountDictionary(factorCounts); - return success; - } - - public static bool TryCalculateCycleLengthForExponentGpu( + public static bool TryCalculateCycleLengthForExponent( +#if DEVICE_GPU || DEVICE_HYBRID PrimeOrderCalculatorAccelerator gpu, +#endif ulong divisor, ulong exponent, in MontgomeryDivisorData divisorData, @@ -713,12 +328,27 @@ public class MersenneDivisorCycles // return true; // } +#if DEVICE_GPU ulong computedOrder = PrimeOrderCalculator.CalculateGpu( gpu, divisor, previousOrder: null, divisorData, PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); +#elif DEVICE_HYBRID + ulong computedOrder = PrimeOrderCalculator.CalculateHybrid( + gpu, + divisor, + previousOrder: null, + divisorData, + PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); +#else + ulong computedOrder = PrimeOrderCalculator.CalculateCpu( + divisor, + previousOrder: null, + divisorData, + PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); +#endif if (computedOrder != 0UL) { cycleLength = computedOrder; @@ -773,6 +403,8 @@ public class MersenneDivisorCycles return success; } + + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool AccumulateFactors( ulong value, Dictionary counts) @@ -805,6 +437,7 @@ public class MersenneDivisorCycles // private static ulong _tryFactorIntoCountsInternalHits; // private static ulong _tryCalculateCycleLengthHeuristicHits; + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static bool TryFactorIntoCountsInternal(ulong value, Dictionary counts) { // The EvenPerfectBitScanner only requests factors for values >= 2 here. Leave the guard documented without executing it. @@ -873,18 +506,21 @@ public class MersenneDivisorCycles return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static void AddFactor(Dictionary counts, ulong factor) { ref int entry = ref CollectionsMarshal.GetValueRefOrAddDefault(counts, factor, out bool exists); entry = exists ? entry + 1 : 1; } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static void AddFactor(Dictionary counts, ulong factor, int multiplicity) { ref int entry = ref CollectionsMarshal.GetValueRefOrAddDefault(counts, factor, out bool exists); entry = exists ? entry + multiplicity : multiplicity; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong ReduceOrder(in MontgomeryDivisorData divisorData, ulong initialOrder, Dictionary factorCounts) { if (factorCounts.Count == 0) @@ -1007,6 +643,7 @@ public class MersenneDivisorCycles } } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong PollardRho64(ulong n) { // The EvenPerfectBitScanner path removes all factors of two before invoking Pollard-Rho, so this branch never executes @@ -1045,13 +682,14 @@ public class MersenneDivisorCycles } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong AdvancePolynomial(ulong x, ulong c, ulong modulus) { UInt128 value = (UInt128)x * x + c; return (ulong)(value % modulus); } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private static ulong BinaryGcd(ulong a, ulong b) { if (a == 0UL) @@ -1083,6 +721,7 @@ public class MersenneDivisorCycles } } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static ulong NextRandomUInt64() { Span buffer = stackalloc byte[8]; @@ -1090,30 +729,6 @@ public class MersenneDivisorCycles return BinaryPrimitives.ReadUInt64LittleEndian(buffer); } - private sealed class FactorPairValueComparer : IComparer> - { - public static readonly FactorPairValueComparer Instance = new FactorPairValueComparer(); - - private FactorPairValueComparer() - { - } - - public int Compare(KeyValuePair x, KeyValuePair y) - { - if (x.Key < y.Key) - { - return -1; - } - - if (x.Key > y.Key) - { - return 1; - } - - return 0; - } - } - public static (long nextPosition, long completeCount) FindLast(string path) { using Stream outputStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, BufferSize10M, useAsync: false); @@ -1122,6 +737,7 @@ public class MersenneDivisorCycles { count++; } + return (outputStream.Position, count); } @@ -1182,7 +798,11 @@ public class MersenneDivisorCycles // unrolled-hex calculator so the CPU generator matches // the GPU benchmark leader for large divisors. MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor); - ulong cycle = CalculateCycleLengthGpu(gpu, divisor, divisorData); +#if DEVICE_GPU || DEVICE_HYBRID + ulong cycle = CalculateCycleLength(gpu, divisor, divisorData); +#else + ulong cycle = CalculateCycleLength(divisor, divisorData); +#endif localCycles[localCycleIndex++] = (divisor, cycle); } @@ -1347,98 +967,36 @@ public class MersenneDivisorCycles return loaded; } - public static ulong CalculateCycleLengthGpu(ulong divisor) => DivisorCycleKernels.CalculateCycleLengthGpu(divisor); +#if DEVICE_GPU + public static ulong CalculateCycleLength(ulong divisor) => DivisorCycleKernels.CalculateCycleLengthGpu(divisor); +#endif - public static ulong CalculateCycleLengthCpu(ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false) +#if DEVICE_GPU || DEVICE_HYBRID + public static ulong CalculateCycleLength(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false) +#else + public static ulong CalculateCycleLength(ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false) +#endif { - if (TryCalculateCycleLengthHeuristicCpu(divisor, divisorData, out ulong cycleLength, skipPrimeOrderHeuristic)) + if (TryCalculateCycleLengthHeuristic( +#if DEVICE_GPU || DEVICE_HYBRID + gpu, +#endif + divisor, + divisorData, + out ulong cycleLength, + skipPrimeOrderHeuristic)) + { return cycleLength; + } return CalculateCycleLengthFallback(divisor); } - public static ulong CalculateCycleLengthGpu(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false) - { - if (TryCalculateCycleLengthHeuristicGpu(gpu, divisor, divisorData, out ulong cycleLength, skipPrimeOrderHeuristic)) - return cycleLength; - - return CalculateCycleLengthFallback(divisor); - } - - public static ulong CalculateCycleLengthHybrid(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, bool skipPrimeOrderHeuristic = false) - { - if (TryCalculateCycleLengthHeuristicHybrid(gpu, divisor, divisorData, out ulong cycleLength, skipPrimeOrderHeuristic)) - return cycleLength; - - return CalculateCycleLengthFallback(divisor); - } - - internal static bool TryCalculateCycleLengthHeuristicCpu(ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false) - { - if ((divisor & (divisor - 1UL)) == 0UL) - { - cycleLength = 1UL; - return true; - } - - if (divisor <= 3UL) - { - cycleLength = CalculateCycleLengthFallback(divisor); - return true; - } - - if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.IsPrimeCpu(divisor)) - { - ulong computedOrder = PrimeOrderCalculator.CalculateCpu( - divisor, - previousOrder: null, - divisorData, - PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); - if (computedOrder != 0UL) - { - cycleLength = computedOrder; - return true; - } - } - - cycleLength = 0UL; - return false; - } - - internal static bool TryCalculateCycleLengthHeuristicHybrid(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false) - { - if ((divisor & (divisor - 1UL)) == 0UL) - { - cycleLength = 1UL; - return true; - } - - if (divisor <= 3UL) - { - cycleLength = CalculateCycleLengthFallback(divisor); - return true; - } - - if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.IsPrimeCpu(divisor)) - { - ulong computedOrder = PrimeOrderCalculator.CalculateHybrid( - gpu, - divisor, - previousOrder: null, - divisorData, - PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); - if (computedOrder != 0UL) - { - cycleLength = computedOrder; - return true; - } - } - - cycleLength = 0UL; - return false; - } - - internal static bool TryCalculateCycleLengthHeuristicGpu(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false) +#if DEVICE_GPU || DEVICE_HYBRID + internal static bool TryCalculateCycleLengthHeuristic(PrimeOrderCalculatorAccelerator gpu, ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false) +#else + internal static bool TryCalculateCycleLengthHeuristic(ulong divisor, in MontgomeryDivisorData divisorData, out ulong cycleLength, bool skipPrimeOrderHeuristic = false) +#endif { if ((divisor & (divisor - 1UL)) == 0UL) { @@ -1452,6 +1010,7 @@ public class MersenneDivisorCycles return true; } +#if DEVICE_GPU if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.IsPrimeGpu(gpu, divisor)) { ulong computedOrder = PrimeOrderCalculator.CalculateGpu( @@ -1466,16 +1025,45 @@ public class MersenneDivisorCycles return true; } } +#elif DEVICE_HYBRID + if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.IsPrimeCpu(divisor)) + { + ulong computedOrder = PrimeOrderCalculator.CalculateHybrid( + gpu, + divisor, + previousOrder: null, + divisorData, + PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); + if (computedOrder != 0UL) + { + cycleLength = computedOrder; + return true; + } + } +#else + if (!skipPrimeOrderHeuristic && HeuristicPrimeTester.IsPrimeCpu(divisor)) + { + ulong computedOrder = PrimeOrderCalculator.CalculateCpu( + divisor, + previousOrder: null, + divisorData, + PrimeOrderCalculator.PrimeOrderCalculatorConfig.HeuristicDefault); + if (computedOrder != 0UL) + { + cycleLength = computedOrder; + return true; + } + } +#endif cycleLength = 0UL; return false; } - private static ulong CalculateCycleLengthFallback(ulong divisor) - { + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static ulong CalculateCycleLengthFallback(ulong divisor) => // Otherwise, find order of 2 mod divisor using the shared unrolled-hex stepping sequence. - return divisor.CalculateMersenneDivisorCycleLengthUnrolledHexCpu(); - } + divisor.CalculateMersenneDivisorCycleLengthUnrolledHexCpu(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureSmallBuffer() diff --git a/PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs b/PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs index 4691fff..b2c7b7f 100644 --- a/PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs +++ b/PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs @@ -270,7 +270,7 @@ public static class MersenneNumberDivisorByDivisorTester ulong exponent, in MontgomeryDivisorData divisorData, out ulong cycleLength, - out bool primeOrderFailed) => MersenneDivisorCycles.TryCalculateCycleLengthForExponentCpu( + out bool primeOrderFailed) => MersenneDivisorCyclesCpu.TryCalculateCycleLengthForExponent( divisor, exponent, divisorData, @@ -284,7 +284,7 @@ public static class MersenneNumberDivisorByDivisorTester ulong exponent, in MontgomeryDivisorData divisorData, out ulong cycleLength, - out bool primeOrderFailed) => MersenneDivisorCycles.TryCalculateCycleLengthForExponentHybrid( + out bool primeOrderFailed) => MersenneDivisorCyclesHybrid.TryCalculateCycleLengthForExponent( gpu, divisor, exponent, @@ -299,7 +299,7 @@ public static class MersenneNumberDivisorByDivisorTester ulong exponent, in MontgomeryDivisorData divisorData, out ulong cycleLength, - out bool primeOrderFailed) => MersenneDivisorCycles.TryCalculateCycleLengthForExponentGpu( + out bool primeOrderFailed) => MersenneDivisorCyclesGpu.TryCalculateCycleLengthForExponent( gpu, divisor, exponent, @@ -311,7 +311,7 @@ public static class MersenneNumberDivisorByDivisorTester public static ulong CalculateCycleLengthCpu( ulong divisor, in MontgomeryDivisorData divisorData, - bool skipPrimeOrderHeuristic) => MersenneDivisorCycles.CalculateCycleLengthCpu( + bool skipPrimeOrderHeuristic) => MersenneDivisorCyclesCpu.CalculateCycleLength( divisor, divisorData, skipPrimeOrderHeuristic); @@ -321,7 +321,7 @@ public static class MersenneNumberDivisorByDivisorTester ulong divisor, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisorData, - bool skipPrimeOrderHeuristic) => MersenneDivisorCycles.CalculateCycleLengthHybrid( + bool skipPrimeOrderHeuristic) => MersenneDivisorCyclesHybrid.CalculateCycleLength( gpu, divisor, divisorData, @@ -332,7 +332,7 @@ public static class MersenneNumberDivisorByDivisorTester ulong divisor, PrimeOrderCalculatorAccelerator gpu, in MontgomeryDivisorData divisorData, - bool skipPrimeOrderHeuristic) => MersenneDivisorCycles.CalculateCycleLengthGpu( + bool skipPrimeOrderHeuristic) => MersenneDivisorCyclesGpu.CalculateCycleLength( gpu, divisor, divisorData, @@ -1232,3 +1232,4 @@ public static class MersenneNumberDivisorByDivisorTester startGate.Dispose(); } } + diff --git a/PerfectNumbers.Core/UInt128Extensions.cs b/PerfectNumbers.Core/UInt128Extensions.cs index a355a43..2374bc4 100644 --- a/PerfectNumbers.Core/UInt128Extensions.cs +++ b/PerfectNumbers.Core/UInt128Extensions.cs @@ -583,7 +583,7 @@ public static class UInt128Extensions ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; int i = 0, primesLength = smallPrimes.Length; - UInt128 cycle = MersenneDivisorCycles.GetCycleCpu(q); + UInt128 cycle = MersenneDivisorCyclesCpu.GetCycle(q); // TODO: If the cache lacks this cycle, immediately schedule the configured device // (GPU by default) to compute it on the fly and skip inserting it into the cache so @@ -634,7 +634,7 @@ public static class UInt128Extensions ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; int i = 0, primesLength = smallPrimes.Length; - UInt128 cycle = MersenneDivisorCycles.GetCycleGpu(gpu, q); + UInt128 cycle = MersenneDivisorCyclesGpu.GetCycle(gpu, q); // TODO: If the cache lacks this cycle, immediately schedule the configured device // (GPU by default) to compute it on the fly and skip inserting it into the cache so // wide-order factoring can still leverage cycle stepping without breaching the @@ -684,7 +684,7 @@ public static class UInt128Extensions ulong[] smallPrimesPow2 = PrimesGenerator.SmallPrimesPow2; int i = 0, primesLength = smallPrimes.Length; - UInt128 cycle = MersenneDivisorCycles.GetCycleHybrid(gpu, q); + UInt128 cycle = MersenneDivisorCyclesHybrid.GetCycle(gpu, q); // TODO: If the cache lacks this cycle, immediately schedule the configured device // (GPU by default) to compute it on the fly and skip inserting it into the cache so // wide-order factoring can still leverage cycle stepping without breaching the @@ -1013,3 +1013,4 @@ public static class UInt128Extensions return result; } } + diff --git a/PerfectNumbers.Core/ULongExtensions.cs b/PerfectNumbers.Core/ULongExtensions.cs index d40d81a..9e704e2 100644 --- a/PerfectNumbers.Core/ULongExtensions.cs +++ b/PerfectNumbers.Core/ULongExtensions.cs @@ -111,7 +111,7 @@ public static partial class ULongExtensions int i = 0, primesLength = smallPrimes.Length; UInt128 q128 = q, - cycle = MersenneDivisorCycles.GetCycleCpu(q128); + cycle = MersenneDivisorCyclesCpu.GetCycle(q128); // TODO: When the shared cycle snapshot cannot serve this divisor, trigger an on-demand // GPU computation (respecting the configured device) without promoting the result into // the cache so the order calculator still benefits from cycle stepping while keeping the @@ -158,7 +158,7 @@ public static partial class ULongExtensions int i = 0, primesLength = smallPrimes.Length; UInt128 q128 = q, - cycle = MersenneDivisorCycles.GetCycleGpu(gpu, q128); + cycle = MersenneDivisorCyclesGpu.GetCycle(gpu, q128); // TODO: When the shared cycle snapshot cannot serve this divisor, trigger an on-demand // GPU computation (respecting the configured device) without promoting the result into // the cache so the order calculator still benefits from cycle stepping while keeping the @@ -205,7 +205,7 @@ public static partial class ULongExtensions int i = 0, primesLength = smallPrimes.Length; UInt128 q128 = q, - cycle = MersenneDivisorCycles.GetCycleHybrid(gpu, q128); + cycle = MersenneDivisorCyclesHybrid.GetCycle(gpu, q128); // TODO: When the shared cycle snapshot cannot serve this divisor, trigger an on-demand // GPU computation (respecting the configured device) without promoting the result into // the cache so the order calculator still benefits from cycle stepping while keeping the @@ -986,7 +986,7 @@ public static partial class ULongExtensions for (; modulusCandidate <= modulus; modulusCandidate++) { - cycle = MersenneDivisorCycles.GetCycleCpu(modulusCandidate); + cycle = MersenneDivisorCyclesCpu.GetCycle(modulusCandidate); remainderForCandidate = cycle > zero ? PowModWithCycle(exponent, modulusCandidate, cycle) : PowMod(exponent, modulusCandidate); @@ -1030,7 +1030,7 @@ public static partial class ULongExtensions for (; modulusCandidate <= modulus; modulusCandidate++) { - cycle = MersenneDivisorCycles.GetCycleGpu(gpu, modulusCandidate); + cycle = MersenneDivisorCyclesGpu.GetCycle(gpu, modulusCandidate); remainderForCandidate = cycle > zero ? PowModWithCycle(exponent, modulusCandidate, cycle) : PowMod(exponent, modulusCandidate); @@ -1074,7 +1074,7 @@ public static partial class ULongExtensions for (; modulusCandidate <= modulus; modulusCandidate++) { - cycle = MersenneDivisorCycles.GetCycleHybrid(gpu, modulusCandidate); + cycle = MersenneDivisorCyclesHybrid.GetCycle(gpu, modulusCandidate); remainderForCandidate = cycle > zero ? PowModWithCycle(exponent, modulusCandidate, cycle) : PowMod(exponent, modulusCandidate); @@ -1310,3 +1310,4 @@ public static partial class ULongExtensions } } +