This commit is contained in:
@@ -49,6 +49,11 @@ jobs:
|
||||
--report-xunit-trx --coverlet --coverlet-output-format cobertura
|
||||
--coverlet-include "[Just.PreciseMath]*"
|
||||
|
||||
- name: Benchmark smoke test
|
||||
run: >-
|
||||
dotnet run --project 2-benchmarks/Just.PreciseMath.Benchmarks
|
||||
--configuration Release --no-build -- --job Dry --filter '*'
|
||||
|
||||
- name: Upload test and coverage results
|
||||
uses: actions/upload-artifact@v7
|
||||
if: ${{ always() }}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Configs;
|
||||
|
||||
namespace Just.PreciseMath.Benchmarks;
|
||||
|
||||
/// <summary>Compares same-type scalar arithmetic on ordinary finite operands.</summary>
|
||||
[MemoryDiagnoser]
|
||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
|
||||
[CategoriesColumn]
|
||||
public class ArithmeticBenchmarks
|
||||
{
|
||||
private double _doubleLeft;
|
||||
private double _doubleRight;
|
||||
private decimal _decimalLeft;
|
||||
private decimal _decimalRight;
|
||||
private DoubleDouble _doubleDoubleLeft;
|
||||
private DoubleDouble _doubleDoubleRight;
|
||||
|
||||
/// <summary>One binary-exact input and one decimal input with a binary residual.</summary>
|
||||
[Params(1.25, 1.1)]
|
||||
public double Left { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
// Conversion and construction are deliberately outside the timed methods.
|
||||
// Decimal and DoubleDouble represent the same decimal input; double rounds it.
|
||||
_doubleLeft = Left;
|
||||
_doubleRight = 0.75;
|
||||
_decimalLeft = (decimal)Left;
|
||||
_decimalRight = 0.75m;
|
||||
_doubleDoubleLeft = (DoubleDouble)_decimalLeft;
|
||||
_doubleDoubleRight = (DoubleDouble)_decimalRight;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true), BenchmarkCategory("Addition")]
|
||||
public double DoubleAdd()
|
||||
{
|
||||
return _doubleLeft + _doubleRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Addition")]
|
||||
public decimal DecimalAdd()
|
||||
{
|
||||
return _decimalLeft + _decimalRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Addition")]
|
||||
public DoubleDouble DoubleDoubleAdd()
|
||||
{
|
||||
return _doubleDoubleLeft + _doubleDoubleRight;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true), BenchmarkCategory("Subtraction")]
|
||||
public double DoubleSubtract()
|
||||
{
|
||||
return _doubleLeft - _doubleRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Subtraction")]
|
||||
public decimal DecimalSubtract()
|
||||
{
|
||||
return _decimalLeft - _decimalRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Subtraction")]
|
||||
public DoubleDouble DoubleDoubleSubtract()
|
||||
{
|
||||
return _doubleDoubleLeft - _doubleDoubleRight;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true), BenchmarkCategory("Multiplication")]
|
||||
public double DoubleMultiply()
|
||||
{
|
||||
return _doubleLeft * _doubleRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Multiplication")]
|
||||
public decimal DecimalMultiply()
|
||||
{
|
||||
return _decimalLeft * _decimalRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Multiplication")]
|
||||
public DoubleDouble DoubleDoubleMultiply()
|
||||
{
|
||||
return _doubleDoubleLeft * _doubleDoubleRight;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true), BenchmarkCategory("Division")]
|
||||
public double DoubleDivide()
|
||||
{
|
||||
return _doubleLeft / _doubleRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Division")]
|
||||
public decimal DecimalDivide()
|
||||
{
|
||||
return _decimalLeft / _decimalRight;
|
||||
}
|
||||
|
||||
[Benchmark, BenchmarkCategory("Division")]
|
||||
public DoubleDouble DoubleDoubleDivide()
|
||||
{
|
||||
return _doubleDoubleLeft / _doubleDoubleRight;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,8 @@
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Just.PreciseMath.Benchmarks</AssemblyName>
|
||||
<RootNamespace>Just.PreciseMath.Benchmarks</RootNamespace>
|
||||
<Description>BenchmarkDotNet scaffold for measuring Just.PreciseMath arithmetic and mathematical functions.</Description>
|
||||
<!-- Source-free scaffold: change to Exe when adding the benchmark entry point. -->
|
||||
<OutputType>Library</OutputType>
|
||||
<Description>BenchmarkDotNet comparisons of DoubleDouble, decimal, and double arithmetic.</Description>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using BenchmarkDotNet.Reports;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace Just.PreciseMath.Benchmarks;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
BenchmarkSwitcher switcher = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly);
|
||||
// Only these standalone discovery/help forms intentionally produce no reports.
|
||||
if (args is ["--list", "flat" or "tree"] or ["--help"] or ["-h"])
|
||||
{
|
||||
switcher.Run(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Summary[] summaries = switcher.Run(args.Length == 0 ? ["--filter", "*"] : args).ToArray();
|
||||
// BenchmarkDotNet can return normally after validation, build, or execution
|
||||
// failures. Do not let CI mistake an empty or failed run for a passing smoke test.
|
||||
if (summaries.Length == 0 || summaries.Any(summary =>
|
||||
summary.HasCriticalValidationErrors || summary.Reports.Length == 0 ||
|
||||
summary.Reports.Any(report => !report.Success || report.ResultStatistics is null)))
|
||||
{
|
||||
Console.Error.WriteLine("Benchmark run failed or produced no measurements.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ Do not claim API completeness or accuracy beyond tested contracts.
|
||||
- `0-source/Just.PreciseMath/`: library implementation and package metadata.
|
||||
- `1-tests/Just.PreciseMath.Tests/`: xUnit v3 tests, Shouldly assertions, and
|
||||
Microsoft.Testing.Platform (MTP) with `coverlet.MTP` coverage.
|
||||
- `2-benchmarks/Just.PreciseMath.Benchmarks/`: source-free BenchmarkDotNet library
|
||||
scaffold, not yet a runnable benchmark suite.
|
||||
- `2-benchmarks/Just.PreciseMath.Benchmarks/`: executable BenchmarkDotNet arithmetic
|
||||
comparisons; use the Dry job for smoke validation, not performance conclusions.
|
||||
- Root `Directory.Build.props` holds shared settings. Each numbered directory's
|
||||
props explicitly imports it; preserve this import chain.
|
||||
- `review-legacy/`: optional local reference material, excluded by `.gitignore`.
|
||||
|
||||
@@ -42,7 +42,7 @@ rounded 106-bit results**. The deterministic rational-oracle tests check a
|
||||
conservative error bound of `2^-100` relative plus one minimum binary64 subnormal,
|
||||
with exact component checks for selected representable cases. Near underflow,
|
||||
extended precision necessarily decreases; overflow produces infinity. Performance
|
||||
has not been benchmarked, including the allocating exponent-boundary path.
|
||||
of the allocating exponent-boundary path is not covered by the basic benchmarks.
|
||||
|
||||
## Conversions and formatting
|
||||
|
||||
@@ -133,8 +133,8 @@ bool success = DoubleDouble.TryParse("1.25e-2".AsSpan(), CultureInfo.InvariantCu
|
||||
|
||||
The planned `PreciseMath` static class and its `Abs`, `Sqrt`, `Pow`, `Exp`, and
|
||||
`Log` functions are not implemented. Generic-math interfaces beyond `ISignedNumber`,
|
||||
additional text formats/general round-trip formatting, and performance benchmarks
|
||||
remain deferred.
|
||||
additional text formats/general round-trip formatting, and broader performance
|
||||
benchmarks remain deferred.
|
||||
Replacing allocating arithmetic boundary fallbacks is also deferred; the current
|
||||
`BigInteger` paths remain in place. That optimization does not require removing
|
||||
`BigInteger` from conversions, parsing, formatting, or independent test oracles.
|
||||
@@ -154,11 +154,46 @@ dotnet format Just.PreciseMath.slnx --verify-no-changes --no-restore
|
||||
Test results and coverage reports are available in the `test-results` artifact
|
||||
on CI workflow runs.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
The BenchmarkDotNet suite compares same-type `+`, `-`, `*`, and `/` operations for
|
||||
`DoubleDouble`, `decimal`, and `double`: 12 methods with two input cases each.
|
||||
Each operation/input group uses `double` as its baseline and reports allocations.
|
||||
Operands are stored in fields and results are returned to the harness; conversions
|
||||
and construction happen in setup, outside the timed methods.
|
||||
|
||||
The left inputs are `1.25` (binary-exact) and `1.1` (a nonzero low component in
|
||||
`DoubleDouble`); the right input is `0.75`. Decimal and double-double inputs originate
|
||||
from the same decimal values, while `double` rounds to binary64. These types have
|
||||
different precision and range contracts: this is a cost comparison, not an accuracy
|
||||
test. Exceptional values, exponent-boundary fallbacks, mixed-type operators,
|
||||
conversions, parsing, and formatting are not benchmarked yet.
|
||||
|
||||
After the Release build above, run from the repository root:
|
||||
|
||||
```sh
|
||||
# Discover benchmark methods without running them.
|
||||
dotnet run --project 2-benchmarks/Just.PreciseMath.Benchmarks -c Release --no-build -- --list flat
|
||||
|
||||
# Quick execution check (also run in CI); not useful for timing comparisons.
|
||||
dotnet run --project 2-benchmarks/Just.PreciseMath.Benchmarks -c Release --no-build -- --job Dry --filter '*'
|
||||
|
||||
# Full measurement run; optionally select an operation with --anyCategories Addition.
|
||||
dotnet run --project 2-benchmarks/Just.PreciseMath.Benchmarks -c Release --no-build -- --filter '*'
|
||||
```
|
||||
|
||||
Reports are written under the ignored `BenchmarkDotNet.Artifacts/` directory.
|
||||
Empty selections and failed benchmark runs return a nonzero exit code. CI only
|
||||
checks execution, with no performance gate. Use full runs on an idle, controlled
|
||||
machine for comparisons; a scalar `double` operation may approach harness overhead,
|
||||
so inspect BenchmarkDotNet warnings before interpreting ratios. Do not compare
|
||||
coverage-instrumented runs or treat Dry-job timings as performance measurements.
|
||||
|
||||
## Project structure
|
||||
|
||||
- `0-source/Just.PreciseMath/`: library implementation.
|
||||
- `1-tests/Just.PreciseMath.Tests/`: unit tests.
|
||||
- `2-benchmarks/Just.PreciseMath.Benchmarks/`: reserved for performance benchmarks.
|
||||
- `2-benchmarks/Just.PreciseMath.Benchmarks/`: arithmetic benchmarks against `decimal` and `double`.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
Reference in New Issue
Block a user