diff --git a/.vscode/settings.json b/.vscode/settings.json index 4d01735..99b770c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,10 @@ "dotnet.defaultSolution": "FlexiCoder.sln", "chatgpt.openOnStartup": true, "cSpell.words": [ - "ILGPU" + "CCCCCCCDUL", + "ILGPU", + "mers", + "primality", + "suff" ] } \ No newline at end of file diff --git a/EvenPerfectBitScanner.Benchmarks/GpuUInt128BinaryGcdBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/GpuUInt128BinaryGcdBenchmarks.cs index 062af47..2fa7947 100644 --- a/EvenPerfectBitScanner.Benchmarks/GpuUInt128BinaryGcdBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/GpuUInt128BinaryGcdBenchmarks.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; @@ -29,17 +29,17 @@ public class GpuUInt128BinaryGcdBenchmarks [Benchmark(Baseline = true)] public GpuUInt128 ReusedVariables() { - return GpuUInt128.BinaryGcd(Input.Left, Input.Right); + return BinaryGcdWithReusedVariables(Input.Left, Input.Right); } [Benchmark] public GpuUInt128 TemporaryStructPerIteration() { - return BinaryGcdWithTemporary(Input.Left, Input.Right); + return GpuUInt128.BinaryGcd(Input.Left, Input.Right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static GpuUInt128 BinaryGcdWithTemporary(GpuUInt128 u, GpuUInt128 v) + private static GpuUInt128 BinaryGcdWithReusedVariables(GpuUInt128 u, GpuUInt128 v) { if (u.IsZero) { @@ -51,7 +51,12 @@ public class GpuUInt128BinaryGcdBenchmarks return u; } - int shift = GpuUInt128.TrailingZeroCount(new GpuUInt128(u.High | v.High, u.Low | v.Low)); + ulong combinedLow = u.Low | v.Low; + ulong combinedHigh = u.High | v.High; + int shift = combinedLow != 0UL + ? BitOperations.TrailingZeroCount(combinedLow) + : 64 + BitOperations.TrailingZeroCount(combinedHigh); + int zu = GpuUInt128.TrailingZeroCount(u); u >>= zu; diff --git a/EvenPerfectBitScanner.Benchmarks/GpuUInt128Mul64Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/GpuUInt128Mul64Benchmarks.cs index e822560..0939a84 100644 --- a/EvenPerfectBitScanner.Benchmarks/GpuUInt128Mul64Benchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/GpuUInt128Mul64Benchmarks.cs @@ -24,7 +24,7 @@ public class GpuUInt128Mul64Benchmarks public static IEnumerable GetInputs() => Inputs; [Benchmark(Baseline = true)] - public GpuUInt128 HighProductMaterialisedInLocal() + public GpuUInt128 HighProductMaterializedInLocal() { GpuUInt128 value = Input.Multiplicand; value.Mul64(Input.Multiplier); diff --git a/EvenPerfectBitScanner.Benchmarks/GpuUInt128MulModBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/GpuUInt128MulModBenchmarks.cs index 6dc0547..100d293 100644 --- a/EvenPerfectBitScanner.Benchmarks/GpuUInt128MulModBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/GpuUInt128MulModBenchmarks.cs @@ -10,8 +10,8 @@ namespace EvenPerfectBitScanner.Benchmarks; [MemoryDiagnoser] public class GpuUInt128MulModBenchmarks { - private static readonly MulModInput[] Inputs = new MulModInput[] - { + private static readonly MulModInput[] Inputs = + [ new( new GpuUInt128(0UL, 5UL), new GpuUInt128(0UL, 7UL), @@ -32,7 +32,7 @@ public class GpuUInt128MulModBenchmarks new GpuUInt128(0x7FFF_FFFF_FFFF_FFFFUL, 0x8000_0000_0000_0000UL), new GpuUInt128(0x7FFF_FFFF_FFFF_FFFFUL, 0xFFFF_FFFF_FFFF_FFF1UL), "MixedMagnitude"), - }; + ]; [ParamsSource(nameof(GetInputs))] public MulModInput Input { get; set; } @@ -42,6 +42,7 @@ public class GpuUInt128MulModBenchmarks [Benchmark(Baseline = true)] public GpuUInt128 InPlaceMulMod() { + // TODO: Switch the extension method to allocate per iteration, as it's faster. Keep the benchmarks. GpuUInt128 value = Input.Left; value.MulMod(Input.Right, Input.Modulus); return value; @@ -56,6 +57,7 @@ public class GpuUInt128MulModBenchmarks [MethodImpl(MethodImplOptions.AggressiveInlining)] private static GpuUInt128 MulModInline(GpuUInt128 left, GpuUInt128 right, GpuUInt128 modulus) { + // Reuse the variables to new instances and lower registry pressure, if possible. GpuUInt128 a = left; GpuUInt128 b = right; GpuUInt128 result = new(); diff --git a/EvenPerfectBitScanner.Benchmarks/GpuUInt128MulModByLimbBenchmarks.cs b/EvenPerfectBitScanner.Benchmarks/GpuUInt128MulModByLimbBenchmarks.cs index 99745a8..a2baa51 100644 --- a/EvenPerfectBitScanner.Benchmarks/GpuUInt128MulModByLimbBenchmarks.cs +++ b/EvenPerfectBitScanner.Benchmarks/GpuUInt128MulModByLimbBenchmarks.cs @@ -42,6 +42,7 @@ public class GpuUInt128MulModByLimbBenchmarks [Benchmark] public GpuUInt128 InPlaceReduction() { + // TODO: Switch the extension method back to legacy method, as it's faster. Keep the benchmarks. GpuUInt128 value = Input.Left; value.MulModByLimb(Input.Right, Input.Modulus); return value; diff --git a/PerfectNumbers.Core/Gpu/GpuUInt128.cs b/PerfectNumbers.Core/Gpu/GpuUInt128.cs index ff9cbaa..45c6364 100644 --- a/PerfectNumbers.Core/Gpu/GpuUInt128.cs +++ b/PerfectNumbers.Core/Gpu/GpuUInt128.cs @@ -535,12 +535,7 @@ public struct GpuUInt128 : IComparable, IEquatable return u; } - ulong combinedLow = u.Low | v.Low; - ulong combinedHigh = u.High | v.High; - int shift = combinedLow != 0UL - ? BitOperations.TrailingZeroCount(combinedLow) - : 64 + BitOperations.TrailingZeroCount(combinedHigh); - + int shift = TrailingZeroCount(new GpuUInt128(u.High | v.High, u.Low | v.Low)); int zu = TrailingZeroCount(u); u >>= zu; @@ -762,7 +757,7 @@ public struct GpuUInt128 : IComparable, IEquatable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong MulMod(GpuUInt128 other, ulong modulus) + public readonly ulong MulMod(GpuUInt128 other, ulong modulus) { ulong modulusLocal = modulus; ulong a = Low % modulusLocal; @@ -779,24 +774,24 @@ public struct GpuUInt128 : IComparable, IEquatable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong MulMod(ulong value, ulong modulus) + public readonly ulong MulMod(ulong value, ulong modulus) { - ulong modulusLocal = modulus; - ulong a = Low % modulusLocal; - ulong b = value % modulusLocal; + ulong a = Low % modulus; + ulong b = value % modulus; if (a == 0UL || b == 0UL) { return 0UL; } - return b <= ulong.MaxValue / a - ? (a * b) % modulusLocal - : MulMod64(a, b, modulusLocal); + ulong ulongRange = ulong.MaxValue / a; + return b <= ulongRange + ? (a * b) % modulus + : MulMod64(a, b, modulus); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong MulModSimplified(ulong value, ulong modulus) + public readonly ulong MulModSimplified(ulong value, ulong modulus) { ulong modulusLocal = modulus; ulong a = Low % modulusLocal; @@ -811,37 +806,33 @@ public struct GpuUInt128 : IComparable, IEquatable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong MulModWithNativeModulo(ulong value, ulong modulus) + public readonly ulong MulModWithNativeModulo(ulong value, ulong modulus) { - ulong modulusLocal = modulus; - ulong multiplicand = Low % modulusLocal; - ulong multiplier = value % modulusLocal; + ulong multiplicand = Low % modulus; + var remainder = value % modulus; - if (multiplicand == 0UL || multiplier == 0UL) + if (multiplicand == 0UL || remainder == 0UL) { return 0UL; } ulong result = 0UL; - ulong current = multiplicand; - ulong remaining = multiplier; - while (true) { - ulong chunk = remaining & NativeModuloChunkMask; + ulong chunk = remainder & NativeModuloChunkMask; if (chunk != 0UL) { - ulong chunkContribution = MultiplyChunkModulo(current, chunk, modulusLocal); - result = (result + chunkContribution) % modulusLocal; + chunk = MultiplyChunkModulo(multiplicand, chunk, modulus); + result = (result + chunk) % modulus; } - remaining >>= NativeModuloChunkBits; - if (remaining == 0UL) + remainder >>= NativeModuloChunkBits; + if (remainder == 0UL) { break; } - current = ShiftLeftByNativeChunk(current, modulusLocal); + multiplicand = ShiftLeftByNativeChunk(multiplicand, modulus); } return result; @@ -936,7 +927,7 @@ public struct GpuUInt128 : IComparable, IEquatable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong MulModMontgomery64(GpuUInt128 other, ulong modulus, ulong nPrime, ulong r2) + public readonly ulong MulModMontgomery64(GpuUInt128 other, ulong modulus, ulong nPrime, ulong r2) { ulong modulusLocal = modulus; ulong a = Low % modulusLocal; @@ -954,7 +945,7 @@ public struct GpuUInt128 : IComparable, IEquatable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong MulModMontgomery64(ulong other, ulong modulus, ulong nPrime, ulong r2) + public readonly ulong MulModMontgomery64(ulong other, ulong modulus, ulong nPrime, ulong r2) { ulong modulusLocal = modulus; ulong a = Low % modulusLocal;