using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using Just.PreciseMath.PerformanceComparison.Reference; namespace Just.PreciseMath.PerformanceComparison; /// /// Compares Square, Sqrt, and Abs on the same ordinary finite operands across binary64, /// the Just.PreciseMath double-double, and the blog reference implementation. /// Every category measures one function, and its binary64 method is the baseline, so /// the ratio columns read as "times the cost of a double". /// /// /// Operands are independent array elements, so the measurements include loads, stores, /// and loop overhead on purpose; input construction and fixture validation stay outside /// the timed methods. /// [MemoryDiagnoser] [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] [CategoriesColumn] public class MathComparisonBenchmarks { private double[] _doublePositive = []; private DoubleDouble[] _positive = []; private DoubleDoubleSample[] _samplePositive = []; private double[] _doubleMixed = []; private DoubleDouble[] _mixed = []; private DoubleDoubleSample[] _sampleMixed = []; private readonly double[] _doubleResults = new double[PerformanceInputs.Count]; private readonly DoubleDouble[] _results = new DoubleDouble[PerformanceInputs.Count]; private readonly DoubleDoubleSample[] _sampleResults = new DoubleDoubleSample[PerformanceInputs.Count]; [GlobalSetup] public void Setup() { PerformanceInputs inputs = PerformanceInputs.Create(); _doublePositive = inputs.DoublePositive; _positive = inputs.Positive; _samplePositive = inputs.SamplePositive; // Square and Sqrt need positive operands; Abs also covers negative ones. _doubleMixed = inputs.DoubleLeft; _mixed = inputs.Left; _sampleMixed = inputs.SampleLeft; } [Benchmark(Baseline = true, OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Square")] public double[] DoubleSquare() { for (int i = 0; i < PerformanceInputs.Count; i++) { _doubleResults[i] = _doublePositive[i] * _doublePositive[i]; } return _doubleResults; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Square")] public DoubleDouble[] DoubleDoubleSquare() { for (int i = 0; i < PerformanceInputs.Count; i++) { _results[i] = DoubleDouble.Square(_positive[i]); } return _results; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Square")] public DoubleDoubleSample[] DoubleDoubleSampleSquare() { for (int i = 0; i < PerformanceInputs.Count; i++) { _sampleResults[i] = _samplePositive[i].Square(); } return _sampleResults; } [Benchmark(Baseline = true, OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Sqrt")] public double[] DoubleSqrt() { for (int i = 0; i < PerformanceInputs.Count; i++) { _doubleResults[i] = Math.Sqrt(_doublePositive[i]); } return _doubleResults; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Sqrt")] public DoubleDouble[] DoubleDoubleSqrt() { for (int i = 0; i < PerformanceInputs.Count; i++) { _results[i] = DoubleDouble.Sqrt(_positive[i]); } return _results; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Sqrt")] public DoubleDoubleSample[] DoubleDoubleSampleSqrt() { for (int i = 0; i < PerformanceInputs.Count; i++) { _sampleResults[i] = _samplePositive[i].Sqrt(); } return _sampleResults; } [Benchmark(Baseline = true, OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Abs")] public double[] DoubleAbs() { for (int i = 0; i < PerformanceInputs.Count; i++) { _doubleResults[i] = Math.Abs(_doubleMixed[i]); } return _doubleResults; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Abs")] public DoubleDouble[] DoubleDoubleAbs() { for (int i = 0; i < PerformanceInputs.Count; i++) { _results[i] = DoubleDouble.Abs(_mixed[i]); } return _results; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Abs")] public DoubleDoubleSample[] DoubleDoubleSampleAbs() { for (int i = 0; i < PerformanceInputs.Count; i++) { _sampleResults[i] = _sampleMixed[i].Abs(); } return _sampleResults; } }