Skip to content

Commit

Permalink
Add a few more benchmarks (#1449)
Browse files Browse the repository at this point in the history
In order to better compare the performance between the existing v6 and
the one with the [Fractions](https://github.com/danm-de/Fractions) I've
been using these benchmarks. They are not _very polished_ (and I haven't
had the time for the string-related stuff) but they do the trick.

You don't have to merge them if you don't want to, but at least they are
here (so I can reference the code).

I'll probably add the results from my local runs in a comment (or more
like a bunch of comments) later on (my previous results got wiped by the
build script 😆) - but I was hoping that we could update the packages and
add net9.0 to the list of targets first (there are [lots of a cool
stuff](danm-de/Fractions#90) in net9).

PS I'm not sure if that old `gh-action` would still work or not, but
even adding the results by hand
([here?](https://github.com/angularsen/UnitsNet/tree/gh-pages/benchmarks/))
is an option.

Here's what I did for the
[Fractions](https://github.com/danm-de/Fractions/blob/master/benchmarks/Readme.md)
(the `Readme.md` was 90% AI generated).
  • Loading branch information
lipchev authored Jan 19, 2025
1 parent 428b58b commit f7b4edb
Show file tree
Hide file tree
Showing 35 changed files with 1,917 additions and 81 deletions.
82 changes: 82 additions & 0 deletions UnitsNet.Benchmark/BenchmarkHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace UnitsNet.Benchmark;

public static class BenchmarkHelpers
{
public static string[] GetRandomAbbreviations<TQuantity>(this Random random, UnitAbbreviationsCache abbreviations, int nbAbbreviations)
{
return random.GetItems(abbreviations.GetAllUnitAbbreviationsForQuantity(typeof(TQuantity)).ToArray(), nbAbbreviations);
}

public static (TQuantity Quantity, TUnit Unit)[] GetRandomConversions<TQuantity, TUnit>(this Random random, double value, TUnit[] options,
int nbConversions)
where TQuantity : IQuantity<TUnit>
where TUnit : struct, Enum
{
var quantities = GetRandomQuantities<TQuantity, TUnit>(random, value, options, nbConversions);
TUnit[] units = random.GetItems(options, nbConversions);
return quantities.Zip(units, (quantity, unit) => (quantity, unit)).ToArray();
}

public static IEnumerable<TQuantity> GetRandomQuantities<TQuantity, TUnit>(this Random random, double value, TUnit[] units, int nbQuantities)
where TQuantity : IQuantity<TUnit> where TUnit : struct, Enum
{
IEnumerable<TQuantity> quantities = random.GetItems(units, nbQuantities).Select(unit => (TQuantity)Quantity.From(value, unit));
return quantities;
}

#if !NET
/// <summary>Creates an array populated with items chosen at random from the provided set of choices.</summary>
/// <param name="random">The random number generator used to select items.</param>
/// <param name="choices">The items to use to populate the array.</param>
/// <param name="length">The length of array to return.</param>
/// <typeparam name="T">The type of array.</typeparam>
/// <exception cref="T:System.ArgumentException">
/// <paramref name="choices" /> is empty.</exception>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="choices" /> is <see langword="null" />.</exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// <paramref name="length" /> is not zero or a positive number.</exception>
/// <returns>An array populated with random items.</returns>
public static T[] GetItems<T>(this Random random, T[] choices, int length)
{
return GetItems<T>(random, new ReadOnlySpan<T>(choices), length);
}

/// <summary>
/// Generates an array of specified length with items chosen at random from the provided set of choices.
/// </summary>
/// <typeparam name="T">The type of the items.</typeparam>
/// <param name="random">The random number generator used to select items.</param>
/// <param name="choices">The set of items to choose from.</param>
/// <param name="length">The length of the resulting array.</param>
/// <returns>An array of randomly selected items.</returns>
/// <exception cref="ArgumentException">Thrown when <paramref name="choices"/> is empty.</exception>
public static T[] GetItems<T>(this Random random, ReadOnlySpan<T> choices, int length)
{
T[] array = new T[length];
GetItems<T>(random, choices, array.AsSpan<T>());
return array;
}

/// <summary>Fills the elements of a specified span with items chosen at random from the provided set of choices.</summary>
/// <param name="random">The random number generator used to select items.</param>
/// <param name="choices">The items to use to populate the span.</param>
/// <param name="destination">The span to be filled with items.</param>
/// <typeparam name="T">The type of span.</typeparam>
/// <exception cref="T:System.ArgumentException">
/// <paramref name="choices" /> is empty.</exception>
public static void GetItems<T>(this Random random, ReadOnlySpan<T> choices, Span<T> destination)
{
for (int index = 0; index < destination.Length; ++index)
destination[index] = choices[random.Next(choices.Length)];
}
#endif
}
68 changes: 68 additions & 0 deletions UnitsNet.Benchmark/Comparisons/ComparisonBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace UnitsNet.Benchmark.Comparisons;

[MemoryDiagnoser]
[ShortRunJob(RuntimeMoniker.Net48)]
[ShortRunJob(RuntimeMoniker.Net80)]
public class ComparisonBenchmarks
{
private static readonly Mass Tolerance = Mass.FromNanograms(1);

public static IEnumerable<object[]> Operands()
{
// equal value and unit
yield return [Mass.From(42, Mass.BaseUnit), Mass.From(42, Mass.BaseUnit)];
// equal value and unit
yield return [Mass.FromGrams(42), Mass.FromGrams(42)];
// zero in another unit
yield return [Mass.Zero, Mass.FromGrams(0)];
// same quantity in another unit
yield return [Mass.FromGrams(42), Mass.FromMilligrams(42000)];
// same quantity in another unit (in reverse)
yield return [Mass.FromMilligrams(42000), Mass.FromGrams(42)];
// different value and same unit
yield return [Mass.FromGrams(42), Mass.FromGrams(42.1)];
// huge values, same unit
yield return [Mass.FromGrams(-1e37), Mass.FromGrams(1 / 1e12)];
// huge values, different units
yield return [Mass.FromGrams(-1e37), Mass.FromMilligrams(1 / 1e12)];
// Math.PI, same unit
yield return [Mass.FromGrams(Math.PI), Mass.FromGrams(Math.PI)];
// Math.PI, different units
yield return [Mass.FromGrams(Math.PI), Mass.FromMilligrams(Math.PI)];
// very close fractions, same units
yield return [Mass.FromGrams(12.3456789987654321), Mass.FromGrams(12.3456789987654322)];
}

[Benchmark]
[ArgumentsSource(nameof(Operands))]
public bool Equals(Mass a, Mass b)
{
return a.Equals(b);
}

[Benchmark]
[ArgumentsSource(nameof(Operands))]
public bool EqualsTolerance(Mass a, Mass b)
{
return a.Equals(b, Tolerance);
}

[Benchmark]
[ArgumentsSource(nameof(Operands))]
public bool GetHashCode(Mass a, Mass b)
{
return a.GetHashCode() == b.GetHashCode();
}

[Benchmark]
[ArgumentsSource(nameof(Operands))]
public int CompareTo(Mass a, Mass b)
{
return a.CompareTo(b);
}
}
112 changes: 112 additions & 0 deletions UnitsNet.Benchmark/Conversions/FromString/ParseUnitBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using UnitsNet.Units;

namespace UnitsNet.Benchmark.Conversions.FromString;

[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
public class ParseUnitBenchmarks
{
private readonly Random _random = new(42);
private string[] _densityUnits;
private string[] _massUnits;
private string[] _pressureUnits;
private string[] _volumeFlowUnits;
private string[] _volumeUnits = [];

[Params(1000)]
public int NbAbbreviations { get; set; }

[GlobalSetup(Target = nameof(ParseMassUnit))]
public void PrepareMassUnits()
{
_massUnits = _random.GetItems(["mg", "g", "kg", "lbs", "Mlbs"], NbAbbreviations);
}

[GlobalSetup(Target = nameof(ParseVolumeUnit))]
public void PrepareVolumeUnits()
{
_volumeUnits = _random.GetItems(["ml", "l", "L", "cm³", "m³"], NbAbbreviations);
}

[GlobalSetup(Target = nameof(ParseDensityUnit))]
public void PrepareDensityUnits()
{
_densityUnits = _random.GetRandomAbbreviations<DensityUnit>(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations);
}

[GlobalSetup(Target = nameof(ParsePressureUnit))]
public void PreparePressureUnits()
{
_pressureUnits = _random.GetRandomAbbreviations<PressureUnit>(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations);
}

[GlobalSetup(Target = nameof(ParseVolumeFlowUnit))]
public void PrepareVolumeFlowUnits()
{
_volumeFlowUnits = _random.GetRandomAbbreviations<VolumeFlowUnit>(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations);
}

[Benchmark(Baseline = true)]
public MassUnit ParseMassUnit()
{
MassUnit unit = default;
foreach (var unitToParse in _massUnits)
{
unit = Mass.ParseUnit(unitToParse);
}

return unit;
}

[Benchmark(Baseline = false)]
public VolumeUnit ParseVolumeUnit()
{
VolumeUnit unit = default;
foreach (var unitToParse in _volumeUnits)
{
unit = Volume.ParseUnit(unitToParse);
}

return unit;
}

[Benchmark(Baseline = false)]
public DensityUnit ParseDensityUnit()
{
DensityUnit unit = default;
foreach (var unitToParse in _densityUnits)
{
unit = Density.ParseUnit(unitToParse);
}

return unit;
}

[Benchmark(Baseline = false)]
public PressureUnit ParsePressureUnit()
{
PressureUnit unit = default;
foreach (var unitToParse in _pressureUnits)
{
unit = Pressure.ParseUnit(unitToParse);
}

return unit;
}

[Benchmark(Baseline = false)]
public VolumeFlowUnit ParseVolumeFlowUnit()
{
VolumeFlowUnit unit = default;
foreach (var unitToParse in _volumeFlowUnits)
{
unit = VolumeFlow.ParseUnit(unitToParse);
}

return unit;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System;
using System.Linq;
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using UnitsNet.Units;

namespace UnitsNet.Benchmark.Conversions.FromString;

[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
public class TryParseInvalidUnitBenchmarks
{
private readonly Random _random = new(42);
private string[] _invalidUnits = [];

[Params(1000)]
public int NbAbbreviations { get; set; }

[GlobalSetup]
public void Setup()
{
_invalidUnits = Enumerable.Range(0, NbAbbreviations).Select(_ => GenerateInvalidUnit()).ToArray();
}

private string GenerateInvalidUnit()
{
var sb = new StringBuilder();
var length = _random.Next(1, 10);
for (var i = 0; i < length; i++)
{
sb.Append((char)_random.Next('a', 'z'));
}

return sb.ToString();
}


[Benchmark(Baseline = true)]
public bool TryParseMassUnit()
{
var success = true;
foreach (var unitToParse in _invalidUnits)
{
success = Mass.TryParseUnit(unitToParse, out MassUnit _);
}

return success;
}

[Benchmark(Baseline = false)]
public bool TryParseVolumeUnit()
{
var success = true;
foreach (var unitToParse in _invalidUnits)
{
success = Volume.TryParseUnit(unitToParse, out _);
}

return success;
}

[Benchmark(Baseline = false)]
public bool ParseDensityUnit()
{
var success = true;
foreach (var unitToParse in _invalidUnits)
{
success = Density.TryParseUnit(unitToParse, out _);
}

return success;
}

[Benchmark(Baseline = false)]
public bool ParsePressureUnit()
{
var success = true;
foreach (var unitToParse in _invalidUnits)
{
success = Pressure.TryParseUnit(unitToParse, out _);
}

return success;
}

[Benchmark(Baseline = false)]
public bool ParseVolumeFlowUnit()
{
var success = true;
foreach (var unitToParse in _invalidUnits)
{
success = VolumeFlow.TryParseUnit(unitToParse, out _);
}

return success;
}
}
Loading

0 comments on commit f7b4edb

Please sign in to comment.