From 8c877cecb3409b766e9121a1d1f1198494a2ba17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Tue, 30 Sep 2025 15:31:20 +0200 Subject: [PATCH] Add operand reduction benchmark variants for MulMod64 --- .../MulMod64Benchmarks.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 EvenPerfectBitScanner.Benchmarks/MulMod64Benchmarks.cs diff --git a/EvenPerfectBitScanner.Benchmarks/MulMod64Benchmarks.cs b/EvenPerfectBitScanner.Benchmarks/MulMod64Benchmarks.cs new file mode 100644 index 0000000..e8fd914 --- /dev/null +++ b/EvenPerfectBitScanner.Benchmarks/MulMod64Benchmarks.cs @@ -0,0 +1,122 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using PerfectNumbers.Core; + +namespace EvenPerfectBitScanner.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net80)] +[MemoryDiagnoser] +public class MulMod64Benchmarks +{ + private static readonly MulMod64Input[] Inputs = + [ + new(0UL, 0UL, 3UL, "ZeroOperands"), + new(ulong.MaxValue, ulong.MaxValue - 1UL, ulong.MaxValue - 58UL, "NearFullRange"), + new(0xFFFF_FFFF_0000_0000UL, 0x0000_0000_FFFF_FFFFUL, 0xFFFF_FFFF_0000_002BUL, "CrossWordBlend"), + new(0x1234_5678_9ABC_DEF0UL, 0x0FED_CBA9_8765_4321UL, 0x1FFF_FFFF_FFFF_FFFBUL, "MixedBitPattern"), + new(0x0000_0001_0000_0000UL, 0x0000_0000_0000_0003UL, 0x0000_0000_FFFF_FFC3UL, "SparseOperands"), + new(0x7FFF_FFFF_FFFF_FFA3UL, 0x6FFF_FFFF_FFFF_FF81UL, 0x7FFF_FFFF_FFFF_FFE7UL, "PrimeSizedModulus"), + ]; + + [ParamsSource(nameof(GetInputs))] + public MulMod64Input Input { get; set; } + + public static IEnumerable GetInputs() + { + return Inputs; + } + + [Benchmark(Baseline = true)] + public ulong ExtensionBaseline() + { + return Input.Left.MulMod64(Input.Right, Input.Modulus); + } + + [Benchmark] + public ulong InlineUInt128Operands() + { + return MulMod64Inline(Input.Left, Input.Right, Input.Modulus); + } + + [Benchmark] + public ulong InlineUInt128OperandsWithOperandReduction() + { + return MulMod64InlineWithOperandReduction(Input.Left, Input.Right, Input.Modulus); + } + + [Benchmark] + public ulong InlineUInt128WithLocals() + { + return MulMod64InlineWithLocals(Input.Left, Input.Right, Input.Modulus); + } + + [Benchmark] + public ulong InlineUInt128WithLocalsAndOperandReduction() + { + return MulMod64InlineWithLocalsAndOperandReduction(Input.Left, Input.Right, Input.Modulus); + } + + [Benchmark] + public ulong MultiplyHighDecomposition() + { + return MulMod64MulHigh(Input.Left, Input.Right, Input.Modulus); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong MulMod64Inline(ulong a, ulong b, ulong modulus) + { + return (ulong)(((UInt128)a * b) % modulus); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong MulMod64InlineWithOperandReduction(ulong a, ulong b, ulong modulus) + { + ulong left = a % modulus; + ulong right = b % modulus; + return (ulong)(((UInt128)left * right) % modulus); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong MulMod64InlineWithLocals(ulong a, ulong b, ulong modulus) + { + UInt128 left128 = a; + UInt128 right128 = b; + UInt128 product = left128 * right128; + UInt128 reduced = product % modulus; + return (ulong)reduced; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong MulMod64InlineWithLocalsAndOperandReduction(ulong a, ulong b, ulong modulus) + { + UInt128 left128 = a; + UInt128 right128 = b; + left128 %= modulus; + right128 %= modulus; + UInt128 product = left128 * right128; + UInt128 reduced = product % modulus; + return (ulong)reduced; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong MulMod64MulHigh(ulong a, ulong b, ulong modulus) + { + ulong left = a % modulus; + ulong right = b % modulus; + ulong low = left * right; + ulong high = left.MulHigh(right); + UInt128 product = ((UInt128)high << 64) | low; + return (ulong)(product % modulus); + } + + public readonly record struct MulMod64Input(ulong Left, ulong Right, ulong Modulus, string Name) + { + public override string ToString() + { + return Name; + } + } +} +