This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace Just.PreciseMath.Benchmarks;
|
||||
|
||||
/// <summary>Compares a+b with a-(-b) using matched results and normalization paths.</summary>
|
||||
/// <remarks>Includes array and loop costs. Negation and fixture validation are outside timing.</remarks>
|
||||
[MemoryDiagnoser]
|
||||
public class AddSubtractMatchedBenchmarks
|
||||
{
|
||||
private const int Count = 64;
|
||||
private readonly DoubleDouble[] _left = new DoubleDouble[Count];
|
||||
private readonly DoubleDouble[] _right = new DoubleDouble[Count];
|
||||
private readonly DoubleDouble[] _results = new DoubleDouble[Count];
|
||||
|
||||
[Params("ZeroCorrection", "NonzeroCorrection", "Cancellation", "MixedCorrection")]
|
||||
public string Scenario { get; set; } = "NonzeroCorrection";
|
||||
|
||||
[GlobalSetup(Target = nameof(Add))]
|
||||
public void SetupAdd()
|
||||
{
|
||||
Setup(false);
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(Subtract))]
|
||||
public void SetupSubtract()
|
||||
{
|
||||
Setup(true);
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = Count)]
|
||||
public DoubleDouble[] Add()
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] + _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = Count)]
|
||||
public DoubleDouble[] Subtract()
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] - _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
private void Setup(bool subtract)
|
||||
{
|
||||
if (Scenario is not ("ZeroCorrection" or "NonzeroCorrection" or "Cancellation" or "MixedCorrection"))
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown matched add/subtract scenario: {Scenario}.");
|
||||
}
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
// Cycle both high signs and moderate scales. High sums/differences
|
||||
// are exact: the significands are small integer multiples of 1/128.
|
||||
int exponent = (((i / 4) % 3) - 1) * 80;
|
||||
double leftSign = i % 2 == 0 ? 1.0 : -1.0;
|
||||
double rightSign = i % 4 < 2 ? 1.0 : -1.0;
|
||||
double leftHigh = leftSign * Math.ScaleB(1.25 + ((i % 7) / 32.0), exponent);
|
||||
double rightHigh = rightSign * Math.ScaleB(1.0 + (((i % 5) - 2) / 128.0), exponent);
|
||||
bool zeroCorrection = Scenario == "ZeroCorrection" || (Scenario == "MixedCorrection" && i % 3 == 0);
|
||||
double leftLow;
|
||||
double rightLow;
|
||||
double expectedHigh;
|
||||
double expectedLow;
|
||||
if (Scenario == "Cancellation")
|
||||
{
|
||||
// Equal/opposite highs leave the exact normalized expansion
|
||||
// (sign*2^(e-54), sign*2^(e-108)), not a binary64-only result.
|
||||
rightHigh = -leftHigh;
|
||||
leftLow = leftSign * Math.ScaleB(1.0, exponent - 54);
|
||||
rightLow = leftSign * Math.ScaleB(1.0, exponent - 108);
|
||||
expectedHigh = leftLow;
|
||||
expectedLow = rightLow;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftLow = leftSign * Math.ScaleB(1.0, exponent - 80);
|
||||
rightLow = zeroCorrection ? -leftLow : leftLow / 2.0;
|
||||
expectedHigh = leftHigh + rightHigh;
|
||||
// This exact low sum is far below half an ulp of the high sum.
|
||||
expectedLow = zeroCorrection ? 0.0 : leftSign * Math.ScaleB(3.0, exponent - 81);
|
||||
}
|
||||
|
||||
DoubleDouble left = DoubleDouble.FromComponents(leftHigh, leftLow);
|
||||
DoubleDouble right = DoubleDouble.FromComponents(rightHigh, rightLow);
|
||||
DoubleDouble negativeRight = -right;
|
||||
CheckComponents(left, leftHigh, leftLow, i, "left input");
|
||||
CheckComponents(right, rightHigh, rightLow, i, "right input");
|
||||
CheckComponents(negativeRight, -rightHigh, -rightLow, i, "negated right input");
|
||||
if (!DoubleDouble.IsFinite(left) || !DoubleDouble.IsFinite(right)
|
||||
|| left.High == 0.0 || right.High == 0.0
|
||||
|| Math.Abs(Math.ILogB(left.High)) > 200 || Math.Abs(Math.ILogB(right.High)) > 200)
|
||||
{
|
||||
throw new InvalidOperationException($"{Scenario}[{i}] is outside the ordinary finite input range.");
|
||||
}
|
||||
// Independent dyadic expectations, not one DD operator as the other's oracle.
|
||||
CheckComponents(left + right, expectedHigh, expectedLow, i, "addition");
|
||||
CheckComponents(left - negativeRight, expectedHigh, expectedLow, i, "subtraction");
|
||||
// Both operators feed these same effective components into AddFinite.
|
||||
if ((FinalCorrection(left, right) == 0.0) != zeroCorrection)
|
||||
{
|
||||
throw new InvalidOperationException($"{Scenario}[{i}] does not select the expected normalization path.");
|
||||
}
|
||||
_left[i] = left;
|
||||
// Target-specific setup keeps array fields and timed memory access identical.
|
||||
_right[i] = subtract ? negativeRight : right;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckComponents(DoubleDouble actual, double high, double low, int index, string operation)
|
||||
{
|
||||
if (BitConverter.DoubleToInt64Bits(actual.High) != BitConverter.DoubleToInt64Bits(high)
|
||||
|| BitConverter.DoubleToInt64Bits(actual.Low) != BitConverter.DoubleToInt64Bits(low))
|
||||
{
|
||||
throw new InvalidOperationException($"{Scenario}[{index}] has unexpected {operation} components.");
|
||||
}
|
||||
}
|
||||
|
||||
// Setup-only characterization, not an accuracy oracle. Keep this path check
|
||||
// synchronized with AddFinite if the production kernel changes in the future.
|
||||
private static double FinalCorrection(DoubleDouble left, DoubleDouble right)
|
||||
{
|
||||
(double high, double highError) = PreciseMathHelper.TwoAdd(left.High, right.High);
|
||||
(double low, double lowError) = PreciseMathHelper.TwoAdd(left.Low, right.Low);
|
||||
(double middle, double middleError) = PreciseMathHelper.TwoAdd(highError, low);
|
||||
(double _, double sumError) = PreciseMathHelper.TwoAdd(high, middle);
|
||||
return sumError + (middleError + lowError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace Just.PreciseMath.Benchmarks;
|
||||
|
||||
/// <summary>Isolates ordinary/fallback exponent transitions, not special-value handling.</summary>
|
||||
[MemoryDiagnoser]
|
||||
[CategoriesColumn]
|
||||
public class ArithmeticBoundaryBenchmarks
|
||||
{
|
||||
private DoubleDouble _addLeft;
|
||||
private DoubleDouble _addRight;
|
||||
private DoubleDouble _multiplyLeft;
|
||||
private DoubleDouble _divideLeft;
|
||||
private DoubleDouble _factor;
|
||||
|
||||
[Params(false, true)]
|
||||
public bool Fallback { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
int offset = Fallback ? 1 : 0;
|
||||
_addLeft = Pair(1020 + offset);
|
||||
_addRight = Pair(1019);
|
||||
_multiplyLeft = Pair(900 + offset);
|
||||
_divideLeft = Pair(450 + offset);
|
||||
_factor = DoubleDouble.FromComponents(1.125, Math.ScaleB(1.0, -56));
|
||||
// Pin dispatch assumptions to the current source guards. Both signs/orders
|
||||
// remain finite; boundary timing is intentionally separate from ordinary data.
|
||||
if ((Math.ILogB(_addLeft.High) > 1020) != Fallback
|
||||
|| (Math.ILogB(_multiplyLeft.High) + Math.ILogB(_factor.High) > 900) != Fallback
|
||||
|| (Math.Abs(Math.ILogB(_divideLeft.High)) > 450) != Fallback)
|
||||
{
|
||||
throw new InvalidOperationException("Boundary fixture does not select the requested guard branch.");
|
||||
}
|
||||
}
|
||||
|
||||
private static DoubleDouble Pair(int exponent)
|
||||
{
|
||||
return DoubleDouble.FromComponents(Math.ScaleB(1.0, exponent), Math.ScaleB(1.0, exponent - 56));
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Addition")]
|
||||
public DoubleDouble DDAdd()
|
||||
{
|
||||
return _addLeft + _addRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Addition")]
|
||||
public DoubleDouble DDScalarAdd()
|
||||
{
|
||||
return _addLeft + _addRight.High;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Addition")]
|
||||
public DoubleDouble ScalarDDAdd()
|
||||
{
|
||||
return _addLeft.High + _addRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble DDSubtract()
|
||||
{
|
||||
return _addLeft - _addRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble DDScalarSubtract()
|
||||
{
|
||||
return _addLeft - _addRight.High;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble ScalarDDSubtract()
|
||||
{
|
||||
return _addLeft.High - _addRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble DDMultiply()
|
||||
{
|
||||
return _multiplyLeft * _factor;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble DDScalarMultiply()
|
||||
{
|
||||
return _multiplyLeft * _factor.High;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble ScalarDDMultiply()
|
||||
{
|
||||
return _multiplyLeft.High * _factor;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Division")]
|
||||
public DoubleDouble DDDivide()
|
||||
{
|
||||
return _divideLeft / _factor;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Division")]
|
||||
public DoubleDouble DDScalarDivide()
|
||||
{
|
||||
return _divideLeft / _factor.High;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Division")]
|
||||
public DoubleDouble ScalarDDDivide()
|
||||
{
|
||||
return _factor.High / _divideLeft;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
namespace Just.PreciseMath.Benchmarks;
|
||||
|
||||
// Input construction stays outside timed benchmark methods.
|
||||
internal sealed class ArithmeticInputs
|
||||
{
|
||||
internal const int Count = 64;
|
||||
|
||||
internal DoubleDouble[] Left { get; } = new DoubleDouble[Count];
|
||||
internal DoubleDouble[] Right { get; } = new DoubleDouble[Count];
|
||||
|
||||
internal static ArithmeticInputs Create(string scenario, bool chain)
|
||||
{
|
||||
if (chain && scenario is not ("BothResidual" or "Mixed"))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(scenario));
|
||||
}
|
||||
if (scenario is not ("BinaryExact" or "DecimalResidual" or "BothResidual" or "Mixed" or "SignsAndScales" or "Cancellation"))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(scenario));
|
||||
}
|
||||
|
||||
ArithmeticInputs inputs = new();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
// Exact dyadic construction, not decimal-to-double residual estimation.
|
||||
double high = 1.25 + ((i % 7) / 32.0);
|
||||
double low = Math.ScaleB(1.0, -56);
|
||||
DoubleDouble left = DoubleDouble.FromComponents(high, low);
|
||||
DoubleDouble right = DoubleDouble.FromComponents(1.0 + (((i % 5) - 2) / 128.0), -low);
|
||||
if (!chain)
|
||||
{
|
||||
switch (scenario)
|
||||
{
|
||||
case "BinaryExact":
|
||||
left = new DoubleDouble(high);
|
||||
right = new DoubleDouble(0.75);
|
||||
break;
|
||||
case "DecimalResidual":
|
||||
left = (DoubleDouble)1.1m;
|
||||
right = new DoubleDouble(0.75);
|
||||
break;
|
||||
case "SignsAndScales":
|
||||
int exponent = ((i % 7) - 3) * 40;
|
||||
double sign = i % 2 == 0 ? 1.0 : -1.0;
|
||||
left = DoubleDouble.FromComponents(Math.ScaleB(sign * high, exponent), Math.ScaleB(low, exponent));
|
||||
right = DoubleDouble.FromComponents(Math.ScaleB(1.125, -exponent), Math.ScaleB(-low, -exponent));
|
||||
break;
|
||||
case "Cancellation":
|
||||
// Alternate near cancellation in addition and subtraction.
|
||||
right = DoubleDouble.FromComponents(i % 2 == 0 ? -high : high, -low / 2.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (scenario == "Mixed")
|
||||
{
|
||||
// Alternate both residuals, right zero-low, left zero-low, both zero-low.
|
||||
if (i % 4 is 2 or 3)
|
||||
{
|
||||
left = new DoubleDouble(left.High);
|
||||
}
|
||||
if (i % 4 is 1 or 3)
|
||||
{
|
||||
right = new DoubleDouble(right.High);
|
||||
}
|
||||
}
|
||||
inputs.Left[i] = left;
|
||||
inputs.Right[i] = right;
|
||||
}
|
||||
inputs.Validate(scenario, chain);
|
||||
return inputs;
|
||||
}
|
||||
|
||||
private void Validate(string scenario, bool chain)
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
ValidateOrdinary(Left[i]);
|
||||
ValidateOrdinary(Right[i]);
|
||||
bool expectedLeftLow = scenario != "BinaryExact" && (scenario != "Mixed" || i % 4 < 2);
|
||||
bool expectedRightLow = scenario is not ("BinaryExact" or "DecimalResidual") && (scenario != "Mixed" || i % 2 == 0);
|
||||
if ((Left[i].Low != 0.0) != expectedLeftLow || (Right[i].Low != 0.0) != expectedRightLow)
|
||||
{
|
||||
throw new InvalidOperationException("Benchmark residual shape does not match its scenario.");
|
||||
}
|
||||
}
|
||||
if (!chain)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup-only guard against a chain drifting into special or exponent-boundary
|
||||
// paths. This is fixture validation, not an independent accuracy oracle.
|
||||
for (int operation = 0; operation < 12; operation++)
|
||||
{
|
||||
DoubleDouble value = Left[0];
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
DoubleDouble right = Right[i];
|
||||
value = operation switch
|
||||
{
|
||||
0 => value + right,
|
||||
1 => value - right,
|
||||
2 => value * right,
|
||||
3 => value / right,
|
||||
4 => value + right.High,
|
||||
5 => value - right.High,
|
||||
6 => value * right.High,
|
||||
7 => value / right.High,
|
||||
8 => right.High + value,
|
||||
9 => right.High - value,
|
||||
10 => right.High * value,
|
||||
11 => right.High / value,
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
ValidateOrdinary(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateOrdinary(DoubleDouble value)
|
||||
{
|
||||
if (!DoubleDouble.IsFinite(value) || value.High == 0.0 || Math.Abs(Math.ILogB(value.High)) > 200
|
||||
|| DoubleDouble.FromComponents(value.High, value.Low) != value
|
||||
|| (value.Low == 0.0 && BitConverter.DoubleToInt64Bits(value.Low) != 0))
|
||||
{
|
||||
throw new InvalidOperationException("Benchmark input/chain left the normalized ordinary finite range.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace Just.PreciseMath.Benchmarks;
|
||||
|
||||
/// <summary>Measures bounded dependent chains; each invocation resets the seed.</summary>
|
||||
[MemoryDiagnoser]
|
||||
[CategoriesColumn]
|
||||
public class ArithmeticLatencyBenchmarks
|
||||
{
|
||||
private DoubleDouble[] _left = [];
|
||||
private DoubleDouble[] _right = [];
|
||||
private double[] _scalarRight = [];
|
||||
|
||||
[Params("BothResidual", "Mixed")]
|
||||
public string Scenario { get; set; } = "BothResidual";
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
ArithmeticInputs inputs = ArithmeticInputs.Create(Scenario, true);
|
||||
_left = inputs.Left;
|
||||
_right = inputs.Right;
|
||||
_scalarRight = Array.ConvertAll(_right, value => value.High);
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Addition")]
|
||||
public DoubleDouble DDAdd()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = value + _right[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Addition")]
|
||||
public DoubleDouble DDScalarAdd()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = value + _scalarRight[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Addition")]
|
||||
public DoubleDouble ScalarDDAdd()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = _scalarRight[i] + value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble DDSubtract()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = value - _right[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble DDScalarSubtract()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = value - _scalarRight[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble ScalarDDSubtract()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = _scalarRight[i] - value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble DDMultiply()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = value * _right[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble DDScalarMultiply()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = value * _scalarRight[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble ScalarDDMultiply()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = _scalarRight[i] * value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Division")]
|
||||
public DoubleDouble DDDivide()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = value / _right[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Division")]
|
||||
public DoubleDouble DDScalarDivide()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = value / _scalarRight[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Division")]
|
||||
public DoubleDouble ScalarDDDivide()
|
||||
{
|
||||
DoubleDouble value = _left[0];
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
value = _scalarRight[i] / value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace Just.PreciseMath.Benchmarks;
|
||||
|
||||
/// <summary>Measures independent array operations, including loads, stores, and loop overhead.</summary>
|
||||
[MemoryDiagnoser]
|
||||
[CategoriesColumn]
|
||||
public class ArithmeticThroughputBenchmarks
|
||||
{
|
||||
private DoubleDouble[] _left = [];
|
||||
private DoubleDouble[] _right = [];
|
||||
private double[] _scalarLeft = [];
|
||||
private double[] _scalarRight = [];
|
||||
private readonly DoubleDouble[] _results = new DoubleDouble[ArithmeticInputs.Count];
|
||||
private readonly double[] _doubleResults = new double[ArithmeticInputs.Count];
|
||||
|
||||
[Params("BinaryExact", "DecimalResidual", "BothResidual", "Mixed", "SignsAndScales", "Cancellation")]
|
||||
public string Scenario { get; set; } = "BothResidual";
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
ArithmeticInputs inputs = ArithmeticInputs.Create(Scenario, false);
|
||||
_left = inputs.Left;
|
||||
_right = inputs.Right;
|
||||
_scalarLeft = Array.ConvertAll(_left, value => value.High);
|
||||
_scalarRight = Array.ConvertAll(_right, value => value.High);
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Addition")]
|
||||
public DoubleDouble[] DDAdd()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] + _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Addition")]
|
||||
public DoubleDouble[] DDScalarAdd()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] + _scalarRight[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Addition")]
|
||||
public DoubleDouble[] ScalarDDAdd()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _scalarLeft[i] + _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Addition")]
|
||||
public double[] DoubleAdd()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_doubleResults[i] = _scalarLeft[i] + _scalarRight[i];
|
||||
}
|
||||
return _doubleResults;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble[] DDSubtract()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] - _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble[] DDScalarSubtract()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] - _scalarRight[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble[] ScalarDDSubtract()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _scalarLeft[i] - _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Subtraction")]
|
||||
public double[] DoubleSubtract()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_doubleResults[i] = _scalarLeft[i] - _scalarRight[i];
|
||||
}
|
||||
return _doubleResults;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble[] DDMultiply()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] * _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble[] DDScalarMultiply()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] * _scalarRight[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble[] ScalarDDMultiply()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _scalarLeft[i] * _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Multiplication")]
|
||||
public double[] DoubleMultiply()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_doubleResults[i] = _scalarLeft[i] * _scalarRight[i];
|
||||
}
|
||||
return _doubleResults;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Division")]
|
||||
public DoubleDouble[] DDDivide()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] / _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Division")]
|
||||
public DoubleDouble[] DDScalarDivide()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _left[i] / _scalarRight[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Division")]
|
||||
public DoubleDouble[] ScalarDDDivide()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_results[i] = _scalarLeft[i] / _right[i];
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ArithmeticInputs.Count), BenchmarkCategory("Division")]
|
||||
public double[] DoubleDivide()
|
||||
{
|
||||
for (int i = 0; i < ArithmeticInputs.Count; i++)
|
||||
{
|
||||
_doubleResults[i] = _scalarLeft[i] / _scalarRight[i];
|
||||
}
|
||||
return _doubleResults;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user