using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using Just.PreciseMath.PerformanceComparison.Reference; namespace Just.PreciseMath.PerformanceComparison; /// /// Compares the four arithmetic operators on the same ordinary finite operands across /// binary64, the Just.PreciseMath double-double, and the blog reference /// implementation. Every category measures one operator, 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, conversion, and fixture validation /// stay outside the timed methods. /// [MemoryDiagnoser] [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] [CategoriesColumn] public class ArithmeticComparisonBenchmarks { private double[] _doubleLeft = []; private double[] _doubleRight = []; private DoubleDouble[] _left = []; private DoubleDouble[] _right = []; private DoubleDoubleSample[] _sampleLeft = []; private DoubleDoubleSample[] _sampleRight = []; 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(); _doubleLeft = inputs.DoubleLeft; _doubleRight = inputs.DoubleRight; _left = inputs.Left; _right = inputs.Right; _sampleLeft = inputs.SampleLeft; _sampleRight = inputs.SampleRight; } [Benchmark(Baseline = true, OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Addition")] public double[] DoubleAdd() { for (int i = 0; i < PerformanceInputs.Count; i++) { _doubleResults[i] = _doubleLeft[i] + _doubleRight[i]; } return _doubleResults; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Addition")] public DoubleDouble[] DoubleDoubleAdd() { for (int i = 0; i < PerformanceInputs.Count; i++) { _results[i] = _left[i] + _right[i]; } return _results; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Addition")] public DoubleDoubleSample[] DoubleDoubleSampleAdd() { for (int i = 0; i < PerformanceInputs.Count; i++) { _sampleResults[i] = _sampleLeft[i] + _sampleRight[i]; } return _sampleResults; } [Benchmark(Baseline = true, OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Subtraction")] public double[] DoubleSubtract() { for (int i = 0; i < PerformanceInputs.Count; i++) { _doubleResults[i] = _doubleLeft[i] - _doubleRight[i]; } return _doubleResults; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Subtraction")] public DoubleDouble[] DoubleDoubleSubtract() { for (int i = 0; i < PerformanceInputs.Count; i++) { _results[i] = _left[i] - _right[i]; } return _results; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Subtraction")] public DoubleDoubleSample[] DoubleDoubleSampleSubtract() { for (int i = 0; i < PerformanceInputs.Count; i++) { _sampleResults[i] = _sampleLeft[i] - _sampleRight[i]; } return _sampleResults; } [Benchmark(Baseline = true, OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Multiplication")] public double[] DoubleMultiply() { for (int i = 0; i < PerformanceInputs.Count; i++) { _doubleResults[i] = _doubleLeft[i] * _doubleRight[i]; } return _doubleResults; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Multiplication")] public DoubleDouble[] DoubleDoubleMultiply() { for (int i = 0; i < PerformanceInputs.Count; i++) { _results[i] = _left[i] * _right[i]; } return _results; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Multiplication")] public DoubleDoubleSample[] DoubleDoubleSampleMultiply() { for (int i = 0; i < PerformanceInputs.Count; i++) { _sampleResults[i] = _sampleLeft[i] * _sampleRight[i]; } return _sampleResults; } [Benchmark(Baseline = true, OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Division")] public double[] DoubleDivide() { for (int i = 0; i < PerformanceInputs.Count; i++) { _doubleResults[i] = _doubleLeft[i] / _doubleRight[i]; } return _doubleResults; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Division")] public DoubleDouble[] DoubleDoubleDivide() { for (int i = 0; i < PerformanceInputs.Count; i++) { _results[i] = _left[i] / _right[i]; } return _results; } [Benchmark(OperationsPerInvoke = PerformanceInputs.Count), BenchmarkCategory("Division")] public DoubleDoubleSample[] DoubleDoubleSampleDivide() { for (int i = 0; i < PerformanceInputs.Count; i++) { _sampleResults[i] = _sampleLeft[i] / _sampleRight[i]; } return _sampleResults; } }