From 0cc9f6b84adc875171cf89946c8435a56a5f754a Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sun, 26 Mar 2023 23:57:23 +0200 Subject: [PATCH] Modulo benchmarks added. --- .../ModuloBenchmarks.cs | 355 ++++++++++++++++++ Recyclable.Collections.Benchmarks/Program.cs | 22 ++ 2 files changed, 377 insertions(+) create mode 100644 Recyclable.Collections.Benchmarks/ModuloBenchmarks.cs diff --git a/Recyclable.Collections.Benchmarks/ModuloBenchmarks.cs b/Recyclable.Collections.Benchmarks/ModuloBenchmarks.cs new file mode 100644 index 0000000..dfa9d3f --- /dev/null +++ b/Recyclable.Collections.Benchmarks/ModuloBenchmarks.cs @@ -0,0 +1,355 @@ +using BenchmarkDotNet.Attributes; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace Recyclable.Collections.Benchmarks +{ + [MemoryDiagnoser] + public class ModuloBenchmarks + { + private long _cutoff; + private Vector128 _increment; + private long _lastCaseIndex; + + [GlobalSetup] + public void Setup() + { + _cutoff = BlockSize << 4; + _increment = Vector128.Create(-BlockSize, 1L); + _lastCaseIndex = 0; + } + + private static void DoNothing(T result, T remainder, [CallerMemberName] string? callerName = null) + { + //Console.WriteLine($"{callerName}: result = {result}, remainder = {remainder}"); + } + + //[Params(123, 10, 2, int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 10, int.MaxValue / 15, int.MaxValue / 20)] + [Params(int.MaxValue, int.MaxValue / 2, int.MaxValue / 3, int.MaxValue / 4, int.MaxValue / 6, int.MaxValue / 8, int.MaxValue / 10, int.MaxValue / 100)] + //[Params(int.MaxValue / 10, int.MaxValue / 20, int.MaxValue / 100)] + public int BlockSize { get; set; } = int.MaxValue; + + [Params(int.MaxValue, int.MaxValue / 1000, int.MaxValue / 10000)] + //[Params(int.MaxValue)] + //[Params(int.MaxValue * 100L)] + public long TestNumber { get; set; } = int.MaxValue; + + private void CalculateDividerAndModulusWithOperator(long i) + { + DoNothing(i / BlockSize, i % BlockSize); + } + + private void CalculateWithMathDivRem(long i) + { + DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder); + } + + private void CalculateWithAddWithFor(long i) + { + int result = 0; + int remainder = 0; + int blockSize = BlockSize; + for (long iteration = 0; iteration < i; iteration++) + { + if (remainder > blockSize - 1) + { + remainder = 0; + result++; + } + + remainder++; + } + + DoNothing(result, remainder); + } + + private void CalculateWithAddWithForOptimized(long i) + { + long total = 0; + int result = 0; + int blockSize = BlockSize; + while (total + blockSize <= i) + { + total += blockSize; + result++; + } + + DoNothing(result, i - total); + } + + private void CalculateWithXDivYMulX(long i) + { + DoNothing(i / BlockSize, i - (i / BlockSize * BlockSize)); + } + + private void CalculateWithXDivYMulXWithLocalVar(long i) + { + int blockSize = BlockSize; + int result = (int)(i / blockSize); + DoNothing(result, i - (result * blockSize)); + } + + private void CalculateWithSubtractWithWhile(long i) + { + int result = 0; + long remainder = i; + int blockSize = BlockSize; + while (remainder >= blockSize) + { + remainder -= blockSize; + result++; + } + + DoNothing(result, remainder); + } + + private void CalculateWithSubractWithWhileWithSse2(long i) + { + var state = Vector128.Create(i, 0); + int blockSize = BlockSize; + var inc = _increment; + while (state.GetElement(0) >= blockSize) + { + state = Sse2.Add(state, inc); + } + + DoNothing(state.GetElement(1), state.GetElement(0)); + } + + private void CalculateWithMixedApproachWithSubractWithWhile(long i) + { + long remainder; + if (i >= _cutoff) + { + DoNothing(Math.DivRem(i, BlockSize, out remainder), remainder); + } + else + { + int result = 0; + int blockSize = BlockSize; + remainder = i; + while (remainder >= blockSize) + { + remainder -= blockSize; + result++; + } + + DoNothing(result, remainder); + } + } + + private void CalculateWithMixedApproachWithSubtractWithWhileAsVector(long i) + { + if (i >= _cutoff) + { + DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder); + } + else + { + var state = new Vector(new[] { 0L, i, 0, 0 }); + var increment = new Vector(new[] { 1L, -BlockSize, 0, 0 }); + int blockSize = BlockSize; + while (state[1] >= blockSize) + { + state += increment; + } + + DoNothing(state[0], state[1]); + } + } + + private void CalculateWithMixedApproachWithSubtractWithWhileWithSse2(long i) + { + if (i >= _cutoff) + { + DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder); + } + else + { + var increment = _increment; + var state = Vector128.Create(i, 0); + int blockSize = BlockSize; + while (state.GetElement(0) >= blockSize) + { + state = Sse2.Add(state, increment); + } + + DoNothing(state.GetElement(1), state.GetElement(0)); + } + } + + private void CalculateWithMixedApproachWithSubtractWithStackalloc(long i) + { + if (i >= _cutoff) + { + DoNothing(Math.DivRem(i, BlockSize, out long remainder), remainder); + } + else + { + Span state = stackalloc long[2] { i, 0 };// Vector128.Create(BlockSize, 0); + int blockSize = BlockSize; + while (state[0] >= blockSize) + { + state[0] -= blockSize; + state[1]++; + } + + DoNothing(state[1], state[0]); + } + } + + private void CalculateWithMixedApproachWithForLoopOptimized(long i) + { + long total; + if (i >= _cutoff) + { + DoNothing(Math.DivRem(i, BlockSize, out total), total); + } + else + { + total = 0; + int result = 0; + int blockSize = BlockSize; + while (total + blockSize <= i) + { + total += blockSize; + result++; + } + + DoNothing(result, i - total); + } + } + + [Benchmark(Baseline = true)] + public void DividerAndModulusWithOperator() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateDividerAndModulusWithOperator(i); + } + } + + [Benchmark] + public void MathDivRem() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithMathDivRem(i); + } + } + + [Benchmark] + public void AddWithFor() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithAddWithFor(i); + } + } + + [Benchmark(Baseline = true)] + public void AddWithForOptimized() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithAddWithForOptimized(i); + } + } + + [Benchmark] + public void XDivYMulX() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithXDivYMulX(i); + } + } + + [Benchmark] + public void XDivYMulXWithLocalVar() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithXDivYMulXWithLocalVar(i); + } + } + + [Benchmark] + public void SubtractWithWhile() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithSubtractWithWhile(i); + } + } + + [Benchmark] + public void SubtractWithWhileWithSse2() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithSubractWithWhileWithSse2(i); + } + } + + [Benchmark(Baseline = false)] + public void MixedApproachWithSubtractWithWhile() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithMixedApproachWithSubractWithWhile(i); + } + } + + [Benchmark(Baseline = false)] + public void MixedApproachWithSubtractWithWhileAsVector() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithMixedApproachWithSubtractWithWhileAsVector(i); + } + } + + [Benchmark(Baseline = false)] + public void MixedApproachWithSubtractWithWhileWithSse2() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithMixedApproachWithSubtractWithWhileWithSse2(i); + } + } + + [Benchmark(Baseline = false)] + public void MixedApproachWithSubtractWithStackalloc() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithMixedApproachWithSubtractWithStackalloc(i); + } + } + + [Benchmark(Baseline = false)] + public void MixedApproachWithForLoopOptimized() + { + long testNumber = TestNumber; + for (long i = testNumber; i >= _lastCaseIndex; i--) + { + CalculateWithMixedApproachWithForLoopOptimized(i); + } + } + } +} \ No newline at end of file diff --git a/Recyclable.Collections.Benchmarks/Program.cs b/Recyclable.Collections.Benchmarks/Program.cs index 93579d5..82b9a7a 100644 --- a/Recyclable.Collections.Benchmarks/Program.cs +++ b/Recyclable.Collections.Benchmarks/Program.cs @@ -10,6 +10,21 @@ using Recyclable.Collections.Benchmarks; internal class Program { + static void RunModuloBenchmarks() + { + var benchmark = new ModuloBenchmarks(); + //benchmark.AddWithFor(); + //benchmark.DividerAndModulusOperator(); + //benchmark.MathDivRem(); + //benchmark.SubtracWithWhile(); + //benchmark.XDivYMulX(); + //benchmark.XDivYMulXWithLocalVar(); + //benchmark.AddWithForOptimized(); + //benchmark.MixedApproachWithForLoopOptimized(); + //benchmark.MixedApproachWithSubtractWithWhile(); + benchmark.MixedApproachWithSubtractWithWhileAsVector(); + } + static void RunBenchmark() { var benchmark = new RecyclableCollectionsBenchmarks(); @@ -39,6 +54,13 @@ internal class Program ); //RunBenchmark(); + + + //BenchmarkRunner.Run( + // ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator | ConfigOptions.JoinSummary) + //); + + //RunModuloBenchmarks(); } } // benchmark.Setup();