Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a few more benchmarks #1449

Open
wants to merge 1 commit into
base: release/v6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 : 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 : 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
Loading