From 81ed84cd769630690dab962d03483fb72672c6b5 Mon Sep 17 00:00:00 2001 From: lipchev Date: Wed, 11 Dec 2024 09:43:57 +0200 Subject: [PATCH 1/2] add a few more benchmarks --- UnitsNet.Benchmark/BenchmarkHelpers.cs | 82 +++++++++++++ .../Comparisons/ComparisonBenchmarks.cs | 68 +++++++++++ .../FromString/ParseUnitBenchmarks.cs | 112 ++++++++++++++++++ .../TryParseInvalidUnitBenchmarks.cs | 102 ++++++++++++++++ .../FromUnit/QuantityFromUnitBenchmarks.cs | 32 +++++ .../InverseElectricConductivityBenchmarks.cs | 23 ++++ .../InverseElectricResistivityBenchmarks.cs | 22 ++++ .../Relations/InverseOperationBenchmarks.cs | 63 ++++++++++ .../MassQuantityConversionBenchmarks.cs | 36 ++++++ .../ToUnit/QuantityConversionBenchmarks.cs | 30 +++++ .../ToUnit/QuantityToUnitBenchmarks.cs | 54 +++++++++ .../ToValue/ConvertValueBenchmarks.cs | 57 +++++++++ .../ToValue/QuantityAsBenchmarks.cs | 103 ++++++++++++++++ ...dToTemperatureWithRandomUnitsBenchmarks.cs | 46 +++++++ .../AddTwoMassesWithRandomUnitsBenchmarks.cs | 46 +++++++ .../AddTwoMassesWithSameUnitsBenchmarks.cs | 44 +++++++ ...peratureDeltasWithRandomUnitsBenchmarks.cs | 46 +++++++ ...emperatureDeltasWithSameUnitsBenchmarks.cs | 49 ++++++++ .../AddTwoVolumesWithRandomUnitsBenchmarks.cs | 46 +++++++ .../AddTwoVolumesWithSameUnitsBenchmarks.cs | 44 +++++++ .../SumOfMassesWithRandomUnitsBenchmarks.cs | 58 +++++++++ .../SumOfMassesWithSameUnitsBenchmarks.cs | 59 +++++++++ ...peratureDeltasWithRandomUnitsBenchmarks.cs | 58 +++++++++ ...emperatureDeltasWithSameUnitsBenchmarks.cs | 59 +++++++++ .../SumOfVolumesWithRandomUnitsBenchmarks.cs | 60 ++++++++++ ...esWithRandomUnitsWithIteratorBenchmarks.cs | 41 +++++++ .../SumOfVolumesWithSameUnitsBenchmarks.cs | 59 +++++++++ ...lumesWithSimilarImperialUnitsBenchmarks.cs | 66 +++++++++++ ...umOfVolumesWithSimilarSIUnitsBenchmarks.cs | 66 +++++++++++ .../MassDividedByVolumeBenchmarks.cs | 32 +++++ ...ividedByVolumeWithRandomUnitsBenchmarks.cs | 45 +++++++ .../VolumeTimesDensityBenchmarks.cs | 50 ++++++++ ...meTimesDensityWithRandomUnitsBenchmarks.cs | 58 +++++++++ UnitsNet.Benchmark/Program.cs | 100 +++------------- UnitsNet.Benchmark/UnitsNetBenchmarks.cs | 82 +++++++++++++ 35 files changed, 1917 insertions(+), 81 deletions(-) create mode 100644 UnitsNet.Benchmark/BenchmarkHelpers.cs create mode 100644 UnitsNet.Benchmark/Comparisons/ComparisonBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/FromString/ParseUnitBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/FromString/TryParseInvalidUnitBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/FromUnit/QuantityFromUnitBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/Relations/InverseElectricConductivityBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/Relations/InverseElectricResistivityBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/Relations/InverseOperationBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/ToUnit/MassQuantityConversionBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/ToUnit/QuantityConversionBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/ToUnit/QuantityToUnitBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/ToValue/ConvertValueBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Conversions/ToValue/QuantityAsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/AddToTemperatureWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/AddTwoMassesWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/AddTwoMassesWithSameUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/AddTwoTemperatureDeltasWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/AddTwoTemperatureDeltasWithSameUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/AddTwoVolumesWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/AddTwoVolumesWithSameUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfMassesWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfMassesWithSameUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfTemperatureDeltasWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfTemperatureDeltasWithSameUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithRandomUnitsWithIteratorBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSameUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSimilarImperialUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSimilarSIUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Divisions/MassDividedByVolumeBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Divisions/MassDividedByVolumeWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Multiplications/VolumeTimesDensityBenchmarks.cs create mode 100644 UnitsNet.Benchmark/Operators/Multiplications/VolumeTimesDensityWithRandomUnitsBenchmarks.cs create mode 100644 UnitsNet.Benchmark/UnitsNetBenchmarks.cs diff --git a/UnitsNet.Benchmark/BenchmarkHelpers.cs b/UnitsNet.Benchmark/BenchmarkHelpers.cs new file mode 100644 index 0000000000..020788317a --- /dev/null +++ b/UnitsNet.Benchmark/BenchmarkHelpers.cs @@ -0,0 +1,82 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). 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(this Random random, UnitAbbreviationsCache abbreviations, int nbAbbreviations) + { + return random.GetItems(abbreviations.GetAllUnitAbbreviationsForQuantity(typeof(TQuantity)).ToArray(), nbAbbreviations); + } + + public static (TQuantity Quantity, TUnit Unit)[] GetRandomConversions(this Random random, double value, TUnit[] options, + int nbConversions) + where TQuantity : IQuantity + where TUnit : Enum + { + var quantities = GetRandomQuantities(random, value, options, nbConversions); + TUnit[] units = random.GetItems(options, nbConversions); + return quantities.Zip(units, (quantity, unit) => (quantity, unit)).ToArray(); + } + + public static IEnumerable GetRandomQuantities(this Random random, double value, TUnit[] units, int nbQuantities) + where TQuantity : IQuantity where TUnit : Enum + { + IEnumerable quantities = random.GetItems(units, nbQuantities).Select(unit => (TQuantity)Quantity.From(value, unit)); + return quantities; + } + +#if !NET + /// Creates an array populated with items chosen at random from the provided set of choices. + /// The random number generator used to select items. + /// The items to use to populate the array. + /// The length of array to return. + /// The type of array. + /// + /// is empty. + /// + /// is . + /// + /// is not zero or a positive number. + /// An array populated with random items. + public static T[] GetItems(this Random random, T[] choices, int length) + { + return GetItems(random, new ReadOnlySpan(choices), length); + } + + /// + /// Generates an array of specified length with items chosen at random from the provided set of choices. + /// + /// The type of the items. + /// The random number generator used to select items. + /// The set of items to choose from. + /// The length of the resulting array. + /// An array of randomly selected items. + /// Thrown when is empty. + public static T[] GetItems(this Random random, ReadOnlySpan choices, int length) + { + T[] array = new T[length]; + GetItems(random, choices, array.AsSpan()); + return array; + } + + /// Fills the elements of a specified span with items chosen at random from the provided set of choices. + /// The random number generator used to select items. + /// The items to use to populate the span. + /// The span to be filled with items. + /// The type of span. + /// + /// is empty. + public static void GetItems(this Random random, ReadOnlySpan choices, Span destination) + { + for (int index = 0; index < destination.Length; ++index) + destination[index] = choices[random.Next(choices.Length)]; + } + #endif +} diff --git a/UnitsNet.Benchmark/Comparisons/ComparisonBenchmarks.cs b/UnitsNet.Benchmark/Comparisons/ComparisonBenchmarks.cs new file mode 100644 index 0000000000..6bbc84741d --- /dev/null +++ b/UnitsNet.Benchmark/Comparisons/ComparisonBenchmarks.cs @@ -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 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); + } +} diff --git a/UnitsNet.Benchmark/Conversions/FromString/ParseUnitBenchmarks.cs b/UnitsNet.Benchmark/Conversions/FromString/ParseUnitBenchmarks.cs new file mode 100644 index 0000000000..cf4ab78ec4 --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/FromString/ParseUnitBenchmarks.cs @@ -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(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations); + } + + [GlobalSetup(Target = nameof(ParsePressureUnit))] + public void PreparePressureUnits() + { + _pressureUnits = _random.GetRandomAbbreviations(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations); + } + + [GlobalSetup(Target = nameof(ParseVolumeFlowUnit))] + public void PrepareVolumeFlowUnits() + { + _volumeFlowUnits = _random.GetRandomAbbreviations(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; + } +} diff --git a/UnitsNet.Benchmark/Conversions/FromString/TryParseInvalidUnitBenchmarks.cs b/UnitsNet.Benchmark/Conversions/FromString/TryParseInvalidUnitBenchmarks.cs new file mode 100644 index 0000000000..13515c63a7 --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/FromString/TryParseInvalidUnitBenchmarks.cs @@ -0,0 +1,102 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). 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; + } +} diff --git a/UnitsNet.Benchmark/Conversions/FromUnit/QuantityFromUnitBenchmarks.cs b/UnitsNet.Benchmark/Conversions/FromUnit/QuantityFromUnitBenchmarks.cs new file mode 100644 index 0000000000..a7226e9f0a --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/FromUnit/QuantityFromUnitBenchmarks.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; + +namespace UnitsNet.Benchmark.Conversions.FromUnit; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class QuantityFromUnitBenchmarks +{ + private static readonly Enum[] BaseUnits = Quantity.Infos.Select(x => x.BaseUnitInfo.Value).ToArray(); + + [Benchmark(Baseline = true)] + public void QuantityFromUnit() + { + foreach (Enum baseUnit in BaseUnits) + { + IQuantity quantity = Quantity.From(1, baseUnit); + } + } + + [Benchmark] + public void QuantityTryFromUnit() + { + foreach (Enum baseUnit in BaseUnits) + { + var success = Quantity.TryFrom(1, baseUnit, out _); + } + } +} diff --git a/UnitsNet.Benchmark/Conversions/Relations/InverseElectricConductivityBenchmarks.cs b/UnitsNet.Benchmark/Conversions/Relations/InverseElectricConductivityBenchmarks.cs new file mode 100644 index 0000000000..58f4d60c46 --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/Relations/InverseElectricConductivityBenchmarks.cs @@ -0,0 +1,23 @@ +using BenchmarkDotNet.Attributes; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Conversions.Relations; + +[MemoryDiagnoser] +[ShortRunJob] +// [DryJob] +public class InverseElectricConductivityBenchmarks +{ + private static readonly double Value = 123.456; + + [ParamsAllValues] + public ElectricConductivityUnit Unit { get; set; } + private ElectricConductivity TestQuantity => new(Value, Unit); + + + [Benchmark(Baseline = true)] + public ElectricResistivity Inverse() + { + return TestQuantity.Inverse(); + } +} diff --git a/UnitsNet.Benchmark/Conversions/Relations/InverseElectricResistivityBenchmarks.cs b/UnitsNet.Benchmark/Conversions/Relations/InverseElectricResistivityBenchmarks.cs new file mode 100644 index 0000000000..2d238a8ae7 --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/Relations/InverseElectricResistivityBenchmarks.cs @@ -0,0 +1,22 @@ +using BenchmarkDotNet.Attributes; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Conversions.Relations; + +[MemoryDiagnoser] +[ShortRunJob] +public class InverseElectricResistivityBenchmarks +{ + private static readonly double Value = 123.456; + + [ParamsAllValues] + public ElectricResistivityUnit Unit { get; set; } + private ElectricResistivity TestQuantity => new(Value, Unit); + + + [Benchmark(Baseline = true)] + public ElectricConductivity Inverse() + { + return TestQuantity.Inverse(); + } +} diff --git a/UnitsNet.Benchmark/Conversions/Relations/InverseOperationBenchmarks.cs b/UnitsNet.Benchmark/Conversions/Relations/InverseOperationBenchmarks.cs new file mode 100644 index 0000000000..7d19981c05 --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/Relations/InverseOperationBenchmarks.cs @@ -0,0 +1,63 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Conversions.Relations; + +[MemoryDiagnoser] +[ShortRunJob(RuntimeMoniker.Net48)] +[ShortRunJob(RuntimeMoniker.Net80)] +public class InverseOperationBenchmarks +{ + private static readonly double Value = 123.456; + + // new (typeof(Area), typeof(ReciprocalArea)), + // new (typeof(Density), typeof(SpecificVolume)), + // new (typeof(ElectricConductivity), typeof(ElectricResistivity)), + // new (typeof(Length), typeof(ReciprocalLength)), + + [Params(typeof(Area), typeof(Length))] + public Type TypeToTest { get; set; } + + + [Benchmark(Baseline = true)] + public IQuantity ConvertWithoutInverse() + { + IQuantity result = default; + if (TypeToTest == typeof(Area)) + { + foreach (AreaUnit fromUnit in Area.Units) + { + var quantity = Area.From(Value, fromUnit); + result = quantity.Inverse(); + } + + foreach (ReciprocalAreaUnit fromUnit in ReciprocalArea.Units) + { + var quantity = ReciprocalArea.From(Value, fromUnit); + result = quantity.Inverse(); + } + } + else if (TypeToTest == typeof(Length)) + { + foreach (LengthUnit fromUnit in Length.Units) + { + var quantity = Length.From(Value, fromUnit); + result = quantity.Inverse(); + } + + foreach (ReciprocalLengthUnit fromUnit in ReciprocalLength.Units) + { + var quantity = ReciprocalLength.From(Value, fromUnit); + result = quantity.Inverse(); + } + } + + return result; + } + +} diff --git a/UnitsNet.Benchmark/Conversions/ToUnit/MassQuantityConversionBenchmarks.cs b/UnitsNet.Benchmark/Conversions/ToUnit/MassQuantityConversionBenchmarks.cs new file mode 100644 index 0000000000..9dedce791a --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/ToUnit/MassQuantityConversionBenchmarks.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Conversions.ToUnit; + +[MemoryDiagnoser] +public class MassQuantityConversionBenchmarks +{ + private static readonly Mass FromMass = new(12345.67, MassUnit.Milligram); + private static readonly Dictionary<(MassUnit, MassUnit), Func> ConversionFunctions = GetConversionsFunctions(); + + private static Dictionary<(MassUnit, MassUnit), Func> GetConversionsFunctions() + { + var functions = new Dictionary<(MassUnit, MassUnit), Func>(); + foreach (MassUnit unit in Mass.Units) + { + foreach (MassUnit otherUnit in Mass.Units) + { + functions.Add((unit, otherUnit), mass => mass.ToUnit(otherUnit)); + } + } + + // functions[(MassUnit.Milligram, MassUnit.Gram)] = mass => new Mass(mass.Value * new QuantityValue(1, 1000), MassUnit.Gram); + functions[(MassUnit.Milligram, MassUnit.Gram)] = mass => new Mass(mass.Value / 1000, MassUnit.Gram); + + return functions; + } + + [Benchmark(Baseline = true)] + public Mass ConvertFromMilligramToGram() => FromMass.ToUnit(MassUnit.Gram); + + [Benchmark] + public Mass ConvertFromMilligramToGramWithDictionary() => ConversionFunctions[(MassUnit.Milligram, MassUnit.Gram)](FromMass); +} diff --git a/UnitsNet.Benchmark/Conversions/ToUnit/QuantityConversionBenchmarks.cs b/UnitsNet.Benchmark/Conversions/ToUnit/QuantityConversionBenchmarks.cs new file mode 100644 index 0000000000..926ca0f2d3 --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/ToUnit/QuantityConversionBenchmarks.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; + +namespace UnitsNet.Benchmark.Conversions.ToUnit; + +[MemoryDiagnoser] +[ShortRunJob(RuntimeMoniker.Net48)] +[ShortRunJob(RuntimeMoniker.Net80)] +public class QuantityConversionBenchmarks +{ + private static readonly IReadOnlyCollection Quantities = + Quantity.Infos.SelectMany(x => x.UnitInfos).Select(u => Quantity.From(123.456, u.Value)).ToList(); + + [Benchmark(Baseline = true)] + public double ConvertOnce() + { + double result = 0; + foreach (IQuantity quantity in Quantities) + { + foreach (UnitInfo unitInfo in quantity.QuantityInfo.UnitInfos) + { + result = quantity.As(unitInfo.Value); + } + } + + return result; + } +} diff --git a/UnitsNet.Benchmark/Conversions/ToUnit/QuantityToUnitBenchmarks.cs b/UnitsNet.Benchmark/Conversions/ToUnit/QuantityToUnitBenchmarks.cs new file mode 100644 index 0000000000..31160857f1 --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/ToUnit/QuantityToUnitBenchmarks.cs @@ -0,0 +1,54 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Conversions.ToUnit; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class QuantityToUnitBenchmarks +{ + private static readonly double Value = 123.456; + private readonly Random _random = new(42); + + private (Mass Quantity, MassUnit Unit)[] _massConversions = []; + private (VolumeFlow Quantity, VolumeFlowUnit Unit)[] _volumeFlowConversions = []; + + [Params(1000)] + public int NbConversions { get; set; } + + [GlobalSetup(Target = nameof(MassToUnit))] + public void PrepareMassConversionsToTest() + { + _massConversions = _random.GetRandomConversions(Value, Mass.Units, NbConversions); + } + + [GlobalSetup(Target = nameof(VolumeFlowToUnit))] + public void PrepareVolumeFlowConversionsToTest() + { + _volumeFlowConversions = _random.GetRandomConversions(Value, VolumeFlow.Units, NbConversions); + } + + [Benchmark(Baseline = true)] + public void MassToUnit() + { + foreach ((Mass quantity, MassUnit unit) in _massConversions) + { + quantity.ToUnit(unit); + } + } + + [Benchmark] + public void VolumeFlowToUnit() + { + foreach ((VolumeFlow quantity, VolumeFlowUnit unit) in _volumeFlowConversions) + { + quantity.ToUnit(unit); + } + } +} diff --git a/UnitsNet.Benchmark/Conversions/ToValue/ConvertValueBenchmarks.cs b/UnitsNet.Benchmark/Conversions/ToValue/ConvertValueBenchmarks.cs new file mode 100644 index 0000000000..5c3ca7586c --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/ToValue/ConvertValueBenchmarks.cs @@ -0,0 +1,57 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; + +namespace UnitsNet.Benchmark.Conversions.ToValue; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class ConvertValueBenchmarks +{ + private static readonly double Value = 123.456; + + + [Benchmark(Baseline = true)] + public double ConvertWithConverter() + { + double result = default; + foreach (QuantityInfo quantityInfo in Quantity.Infos) + { + foreach (UnitInfo fromUnitInfo in quantityInfo.UnitInfos) + { + foreach (UnitInfo toUnitInfo in quantityInfo.UnitInfos) + { + result = UnitConverter.Convert(Value, fromUnitInfo.Value, toUnitInfo.Value); + } + } + } + + return result; + } + + [Benchmark(Baseline = false)] + public double ConvertFromQuantity() + { + double result = default; + foreach (QuantityInfo quantityInfo in Quantity.Infos) + { + foreach (UnitInfo fromUnitInfo in quantityInfo.UnitInfos) + { + foreach (UnitInfo toUnitInfo in quantityInfo.UnitInfos) + { + result = Quantity.From(Value, fromUnitInfo.Value).As(toUnitInfo.Value); + } + } + } + + return result; + } + + + [GlobalSetup] + public void PrepareTo_ConvertWith_FullyCachedFrozenDictionary() + { + var nbQuantities = Quantity.Infos.Length; + } + +} diff --git a/UnitsNet.Benchmark/Conversions/ToValue/QuantityAsBenchmarks.cs b/UnitsNet.Benchmark/Conversions/ToValue/QuantityAsBenchmarks.cs new file mode 100644 index 0000000000..bbab51b9a6 --- /dev/null +++ b/UnitsNet.Benchmark/Conversions/ToValue/QuantityAsBenchmarks.cs @@ -0,0 +1,103 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Conversions.ToValue; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net90)] +public class QuantityAsBenchmarks +{ + private readonly Random _random = new Random(42); + private static readonly double Value = 123.456; + + [Params(1000)] + public int NbConversions { get; set; } + + private (Mass Quantity, MassUnit Unit)[] _massConversions = []; + private (Volume Quantity, VolumeUnit Unit)[] _volumeConversions = []; + private (Density Quantity, DensityUnit Unit)[] _densityConversions = []; + private (Pressure Quantity, PressureUnit Unit)[] _pressureConversions = []; + private (VolumeFlow Quantity, VolumeFlowUnit Unit)[] _volumeFlowConversions = []; + + + [GlobalSetup(Target = nameof(MassAs))] + public void PrepareMassConversionsToTest() + { + _massConversions = _random.GetRandomConversions(Value, Mass.Units, NbConversions); + } + + [GlobalSetup(Target = nameof(VolumeAs))] + public void PrepareVolumeConversionsToTest() + { + _volumeConversions = _random.GetRandomConversions(Value, Volume.Units, NbConversions); + } + + [GlobalSetup(Target = nameof(DensityAs))] + public void PrepareDensityConversionsToTest() + { + _densityConversions = _random.GetRandomConversions(Value, Density.Units, NbConversions); + } + + [GlobalSetup(Target = nameof(PressureAs))] + public void PreparePressureConversionsToTest() + { + _pressureConversions = _random.GetRandomConversions(Value, Pressure.Units, NbConversions); + } + + [GlobalSetup(Target = nameof(VolumeFlowAs))] + public void PrepareVolumeFlowConversionsToTest() + { + _volumeFlowConversions = _random.GetRandomConversions(Value, VolumeFlow.Units, NbConversions); + } + + [Benchmark(Baseline = true)] + public void MassAs() + { + foreach (var conversion in _massConversions) + { + conversion.Quantity.As(conversion.Unit); + } + } + + [Benchmark] + public void VolumeAs() + { + foreach (var conversion in _volumeConversions) + { + conversion.Quantity.As(conversion.Unit); + } + } + + [Benchmark] + public void DensityAs() + { + foreach (var conversion in _densityConversions) + { + conversion.Quantity.As(conversion.Unit); + } + } + + [Benchmark] + public void PressureAs() + { + foreach (var conversion in _pressureConversions) + { + conversion.Quantity.As(conversion.Unit); + } + } + + [Benchmark] + public void VolumeFlowAs() + { + foreach (var conversion in _volumeFlowConversions) + { + conversion.Quantity.As(conversion.Unit); + } + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/AddToTemperatureWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/AddToTemperatureWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..443da4b645 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/AddToTemperatureWithRandomUnitsBenchmarks.cs @@ -0,0 +1,46 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net90)] +public class AddToTemperatureWithRandomUnitsBenchmarks +{ + private static readonly double LeftValue = 1.23; + private static readonly double RightValue = 4.56; + + private readonly Random _random = new(42); + private (Temperature left, TemperatureDelta right)[] _operands; + + [Params(1_000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = _random.GetRandomQuantities(LeftValue, Temperature.Units, NbOperations) + .Zip(_random.GetRandomQuantities(RightValue, TemperatureDelta.Units, NbOperations), + (left, right) => (left, right)) + .ToArray(); + } + + [Benchmark(Baseline = true)] + public Temperature AddToTemperature() + { + Temperature sum = default; + foreach ((Temperature left, TemperatureDelta right) in _operands) + { + sum = left + right; // intentionally not summing the results + } + + return sum; + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/AddTwoMassesWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/AddTwoMassesWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..1c456fb9ac --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/AddTwoMassesWithRandomUnitsBenchmarks.cs @@ -0,0 +1,46 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class AddTwoMassesWithRandomUnitsBenchmarks +{ + private static readonly double LeftValue = 1.23; + private static readonly double RightValue = 4.56; + + private readonly Random _random = new(42); + private (Mass left, Mass right)[] _operands; + + [Params(1_000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = _random.GetRandomQuantities(LeftValue, Mass.Units, NbOperations) + .Zip(_random.GetRandomQuantities(RightValue, Mass.Units, NbOperations), + (left, right) => (left, right)) + .ToArray(); + } + + [Benchmark(Baseline = true)] + public Mass AddTwoMasses() + { + Mass sum = default; + foreach ((Mass left, Mass right) in _operands) + { + sum = left + right; // intentionally not summing the results + } + + return sum; + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/AddTwoMassesWithSameUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/AddTwoMassesWithSameUnitsBenchmarks.cs new file mode 100644 index 0000000000..b0b73bf36d --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/AddTwoMassesWithSameUnitsBenchmarks.cs @@ -0,0 +1,44 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class AddTwoMassesWithSameUnitsBenchmarks +{ + private static readonly double LeftValue = 1.23; + private static readonly double RightValue = 4.56; + + private (Mass left, Mass right)[] _operands; + + [Params(1_000)] + public int NbOperations { get; set; } + + [Params(MassUnit.Kilogram, MassUnit.Gram, MassUnit.Milligram)] + public MassUnit Unit { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = Enumerable.Range(0, NbOperations).Select(_ => (Mass.From(LeftValue, Unit), Mass.From(RightValue, Unit))).ToArray(); + } + + [Benchmark(Baseline = true)] + public Mass AddTwoMasses() + { + Mass sum = default; + foreach ((Mass left, Mass right) in _operands) + { + sum = left + right; // intentionally not summing the results + } + + return sum; + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/AddTwoTemperatureDeltasWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/AddTwoTemperatureDeltasWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..a3239d3010 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/AddTwoTemperatureDeltasWithRandomUnitsBenchmarks.cs @@ -0,0 +1,46 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class AddTwoTemperatureDeltasWithRandomUnitsBenchmarks +{ + private static readonly double LeftValue = 1.23; + private static readonly double RightValue = 4.56; + + private readonly Random _random = new(42); + private (TemperatureDelta left, TemperatureDelta right)[] _operands; + + [Params(1_000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = _random.GetRandomQuantities(LeftValue, TemperatureDelta.Units, NbOperations) + .Zip(_random.GetRandomQuantities(RightValue, TemperatureDelta.Units, NbOperations), + (left, right) => (left, right)) + .ToArray(); + } + + [Benchmark(Baseline = true)] + public TemperatureDelta AddTwoDeltas() + { + TemperatureDelta sum = default; + foreach ((TemperatureDelta left, TemperatureDelta right) in _operands) + { + sum = left + right; // intentionally not summing the results + } + + return sum; + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/AddTwoTemperatureDeltasWithSameUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/AddTwoTemperatureDeltasWithSameUnitsBenchmarks.cs new file mode 100644 index 0000000000..03c93cfee9 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/AddTwoTemperatureDeltasWithSameUnitsBenchmarks.cs @@ -0,0 +1,49 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class AddTwoTemperatureDeltasWithSameUnitsBenchmarks +{ + private static readonly double LeftValue = 1.23; + private static readonly double RightValue = 4.56; + + private readonly Random _random = new(42); + private (TemperatureDelta left, TemperatureDelta right)[] _operands; + + [Params(1_000)] + public int NbOperations { get; set; } + + [Params(TemperatureDeltaUnit.Kelvin, TemperatureDeltaUnit.DegreeCelsius, TemperatureDeltaUnit.DegreeFahrenheit)] + public TemperatureDeltaUnit Unit { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = _random.GetRandomQuantities(LeftValue, TemperatureDelta.Units, NbOperations) + .Zip(_random.GetRandomQuantities(RightValue, TemperatureDelta.Units, NbOperations), + (left, right) => (left, right)) + .ToArray(); + } + + [Benchmark(Baseline = true)] + public TemperatureDelta AddTwoDeltas() + { + TemperatureDelta sum = default; + foreach ((TemperatureDelta left, TemperatureDelta right) in _operands) + { + sum = left + right; // intentionally not summing the results + } + + return sum; + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/AddTwoVolumesWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/AddTwoVolumesWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..dcf3224e0b --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/AddTwoVolumesWithRandomUnitsBenchmarks.cs @@ -0,0 +1,46 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class AddTwoVolumesWithRandomUnitsBenchmarks +{ + private static readonly double LeftValue = 1.23; + private static readonly double RightValue = 4.56; + + private readonly Random _random = new(42); + private (Volume left, Volume right)[] _operands; + + [Params(1_000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = _random.GetRandomQuantities(LeftValue, Volume.Units, NbOperations) + .Zip(_random.GetRandomQuantities(RightValue, Volume.Units, NbOperations), + (left, right) => (left, right)) + .ToArray(); + } + + [Benchmark] + public Volume AddTwoVolumes() + { + Volume sum = default; + foreach ((Volume left, Volume right) in _operands) + { + sum = left + right; // intentionally not summing the results + } + + return sum; + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/AddTwoVolumesWithSameUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/AddTwoVolumesWithSameUnitsBenchmarks.cs new file mode 100644 index 0000000000..a55151199a --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/AddTwoVolumesWithSameUnitsBenchmarks.cs @@ -0,0 +1,44 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class AddTwoVolumesWithSameUnitsBenchmarks +{ + private static readonly double LeftValue = 1.23; + private static readonly double RightValue = 4.56; + + private (Volume left, Volume right)[] _operands; + + [Params(1_000)] + public int NbOperations { get; set; } + + [Params(VolumeUnit.CubicMeter, VolumeUnit.Liter, VolumeUnit.Milliliter)] + public VolumeUnit Unit { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = Enumerable.Range(0, NbOperations).Select(_ => (Volume.From(LeftValue, Unit), Volume.From(RightValue, Unit))).ToArray(); + } + + [Benchmark] + public Volume AddTwoVolumes() + { + Volume sum = default; + foreach ((Volume left, Volume right) in _operands) + { + sum = left + right; // intentionally not summing the results + } + + return sum; + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfMassesWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfMassesWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..696242b9b7 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfMassesWithRandomUnitsBenchmarks.cs @@ -0,0 +1,58 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class SumOfMassesWithRandomUnitsBenchmarks +{ + private static readonly double Value = 1.23; + + private readonly Random _random = new(42); + private Mass[] _quantities; + + [Params(1000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _quantities = _random.GetRandomQuantities(Value, Mass.Units, NbOperations).ToArray(); + } + + [Benchmark(Baseline = true)] + public Mass SumOfMasses() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + Mass sum = Mass.Zero; + foreach (var quantity in _quantities) + { + sum = quantity + sum; + } + + return sum; +#endif + } + + [Benchmark(Baseline = false)] + public Mass SumOfMassesWithBaseUnit() + { + return _quantities.Sum(Mass.BaseUnit); + } + + [Benchmark(Baseline = false)] + public Mass SumOfMassesWithInMilligrams() + { + return _quantities.Sum(MassUnit.Milligram); + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfMassesWithSameUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfMassesWithSameUnitsBenchmarks.cs new file mode 100644 index 0000000000..2080183fa9 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfMassesWithSameUnitsBenchmarks.cs @@ -0,0 +1,59 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class SumOfMassesWithSameUnitsBenchmarks +{ + private static readonly double Value = 1.23; + + private Mass[] _quantities; + + [Params(1000)] + public int NbOperations { get; set; } + + [Params(MassUnit.Kilogram, MassUnit.Gram, MassUnit.Milligram)] + public MassUnit Unit { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _quantities = Enumerable.Range(0, NbOperations).Select(_ => Mass.From(Value, Unit)).ToArray(); + } + + [Benchmark(Baseline = true)] + public Mass SumOfMasses() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + Mass sum = Mass.Zero; + foreach (var quantity in _quantities) + { + sum = quantity + sum; + } + + return sum; +#endif + } + + [Benchmark(Baseline = false)] + public Mass SumOfMassesInBaseUnit() + { + return _quantities.Sum(Mass.BaseUnit); + } + + [Benchmark(Baseline = false)] + public Mass SumOfMassesInMilligram() + { + return _quantities.Sum(MassUnit.Milligram); + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfTemperatureDeltasWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfTemperatureDeltasWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..65e135e3ae --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfTemperatureDeltasWithRandomUnitsBenchmarks.cs @@ -0,0 +1,58 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class SumOfTemperatureDeltasWithRandomUnitsBenchmarks +{ + private static readonly double Value = 1.23; + + private readonly Random _random = new(42); + private TemperatureDelta[] _quantities; + + [Params(10, 1000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _quantities = _random.GetRandomQuantities(Value, TemperatureDelta.Units, NbOperations).ToArray(); + } + + [Benchmark(Baseline = true)] + public TemperatureDelta SumOfDeltas() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + TemperatureDelta sum = TemperatureDelta.Zero; + foreach (TemperatureDelta quantity in _quantities) + { + sum = quantity + sum; + } + + return sum; +#endif + } + + [Benchmark(Baseline = false)] + public TemperatureDelta SumOfVolumesInBaseUnit() + { + return _quantities.Sum(TemperatureDelta.BaseUnit); + } + + [Benchmark(Baseline = false)] + public TemperatureDelta SumOfTemperatureDeltasInDegreeFahrenheit() + { + return _quantities.Sum(TemperatureDeltaUnit.DegreeFahrenheit); + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfTemperatureDeltasWithSameUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfTemperatureDeltasWithSameUnitsBenchmarks.cs new file mode 100644 index 0000000000..5625d53c58 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfTemperatureDeltasWithSameUnitsBenchmarks.cs @@ -0,0 +1,59 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class SumOfTemperatureDeltasWithSameUnitsBenchmarks +{ + private static readonly double Value = 1.23; + + private TemperatureDelta[] _quantities; + + [Params(10, 1000)] + public int NbOperations { get; set; } + + [Params(TemperatureDeltaUnit.Kelvin, TemperatureDeltaUnit.DegreeCelsius, TemperatureDeltaUnit.DegreeFahrenheit)] + public TemperatureDeltaUnit Unit { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _quantities = Enumerable.Range(0, NbOperations).Select(_ => TemperatureDelta.From(Value, Unit)).ToArray(); + } + + [Benchmark(Baseline = true)] + public TemperatureDelta SumOfDeltas() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + TemperatureDelta sum = TemperatureDelta.Zero; + foreach (TemperatureDelta quantity in _quantities) + { + sum = quantity + sum; + } + + return sum; +#endif + } + + [Benchmark(Baseline = false)] + public TemperatureDelta SumOfVolumesInBaseUnit() + { + return _quantities.Sum(TemperatureDelta.BaseUnit); + } + + [Benchmark(Baseline = false)] + public TemperatureDelta SumOfTemperatureDeltasInDegreeFahrenheit() + { + return _quantities.Sum(TemperatureDeltaUnit.DegreeFahrenheit); + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..76beab1238 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithRandomUnitsBenchmarks.cs @@ -0,0 +1,60 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net90)] +public class SumOfVolumesWithRandomUnitsBenchmarks +{ + private static readonly double Value = 1.23; + + private readonly Random _random = new(41); + private Volume[] _quantities; + + [Params(10, 100, 1000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _quantities = _random.GetRandomQuantities(Value, Volume.Units, NbOperations).ToArray(); + Quantity.From(Value, Volume.BaseUnit); // TODO we need a better way to "disable" the lazy loading of the _quantitiesByUnitType (QuantityInfoLookup) + Console.Out.WriteLine("Quantities prepared: starting unit = {0}", _quantities[0].Unit); + } + + [Benchmark(Baseline = true)] + public Volume SumOfVolumes() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + Volume sum = Volume.Zero; + foreach (var quantity in _quantities) + { + sum = quantity + sum; + } + + return sum; +#endif + } + + [Benchmark(Baseline = false)] + public Volume SumOfVolumesInBaseUnit() + { + return _quantities.Sum(Volume.BaseUnit); + } + + [Benchmark(Baseline = false)] + public Volume SumOfVolumesInMilliliter() + { + return _quantities.Sum(VolumeUnit.Milliliter); + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithRandomUnitsWithIteratorBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithRandomUnitsWithIteratorBenchmarks.cs new file mode 100644 index 0000000000..624a0ee775 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithRandomUnitsWithIteratorBenchmarks.cs @@ -0,0 +1,41 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net80)] +public class SumOfVolumesWithRandomUnitsWithIteratorBenchmarks +{ + private static readonly double Value = 1.23; + + private readonly Random _random = new(41); + private Volume[] _quantities; + + [Params(10, 100, 1000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _quantities = _random.GetRandomQuantities(Value, Volume.Units, NbOperations).ToArray(); + Quantity.From(Value, Volume.BaseUnit); // TODO we need a better way to "disable" the lazy loading of the _quantitiesByUnitType (QuantityInfoLookup) + Console.Out.WriteLine("Quantities prepared: starting unit = {0}", _quantities[0].Unit); + } + + [Benchmark(Baseline = true)] + public Volume SumOfVolumes() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + throw new NotImplementedException(); +#endif + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSameUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSameUnitsBenchmarks.cs new file mode 100644 index 0000000000..94f4765f4e --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSameUnitsBenchmarks.cs @@ -0,0 +1,59 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class SumOfVolumesWithSameUnitsBenchmarks +{ + private static readonly double Value = 1.23; + + private Volume[] _quantities; + + [Params(10, 1000)] + public int NbOperations { get; set; } + + [Params(VolumeUnit.CubicMeter, VolumeUnit.Liter, VolumeUnit.Milliliter)] + public VolumeUnit Unit { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _quantities = Enumerable.Range(0, NbOperations).Select(_ => Volume.From(Value, Unit)).ToArray(); + } + + [Benchmark(Baseline = true)] + public Volume SumOfVolumes() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + Volume sum = Volume.Zero; + foreach (var quantity in _quantities) + { + sum = quantity + sum; + } + + return sum; +#endif + } + + [Benchmark(Baseline = false)] + public Volume SumOfVolumesInBaseUnit() + { + return _quantities.Sum(Volume.BaseUnit); + } + + [Benchmark(Baseline = false)] + public Volume SumOfVolumesInMilliliter() + { + return _quantities.Sum(VolumeUnit.Milliliter); + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSimilarImperialUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSimilarImperialUnitsBenchmarks.cs new file mode 100644 index 0000000000..a9cec2e553 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSimilarImperialUnitsBenchmarks.cs @@ -0,0 +1,66 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net90)] +public class SumOfVolumesWithSimilarImperialUnitsBenchmarks +{ + private static readonly double Value = 1.23; + + private readonly Random _random = new(42); + private Volume[] _quantities; + + [Params(1000)] + public int NbOperations { get; set; } + + [Params(VolumeUnit.ImperialOunce, VolumeUnit.ImperialGallon)] + public VolumeUnit StartingUnit { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + Quantity.From(Value, Volume.BaseUnit); // TODO we need a better way to "disable" the lazy loading of the _quantitiesByUnitType (QuantityInfoLookup) + + _quantities = _random.GetRandomQuantities(Value, [VolumeUnit.ImperialOunce, VolumeUnit.ImperialQuart, VolumeUnit.ImperialGallon], NbOperations - 1) + .Prepend(Volume.From(Value, StartingUnit)).ToArray(); + Volume firstQuantity = _quantities[0]; + Console.Out.WriteLine("Quantities prepared: starting unit = {0}", firstQuantity.Unit); + } + + [Benchmark(Baseline = true)] + public Volume SumOfVolumes() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + Volume sum = Volume.Zero; + foreach (var quantity in _quantities) + { + sum = quantity + sum; + } + + return sum; +#endif + } + + [Benchmark(Baseline = false)] + public Volume SumOfVolumesInBaseUnit() + { + return _quantities.Sum(Volume.BaseUnit); + } + + [Benchmark(Baseline = false)] + public Volume SumOfVolumesInStartingUnit() + { + return _quantities.Sum(StartingUnit); + } +} diff --git a/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSimilarSIUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSimilarSIUnitsBenchmarks.cs new file mode 100644 index 0000000000..39a69e3076 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Additions/SumOfVolumesWithSimilarSIUnitsBenchmarks.cs @@ -0,0 +1,66 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Additions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net90)] +public class SumOfVolumesWithSimilarSIUnitsBenchmarks +{ + private static readonly double Value = 1.23; + + private readonly Random _random = new(42); + private Volume[] _quantities; + + [Params(1000)] + public int NbOperations { get; set; } + + [Params(VolumeUnit.Milliliter, VolumeUnit.Liter)] + public VolumeUnit StartingUnit { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + Quantity.From(Value, Volume.BaseUnit); // TODO we need a better way to "disable" the lazy loading of the _quantitiesByUnitType (QuantityInfoLookup) + + _quantities = _random.GetRandomQuantities(Value, [VolumeUnit.Microliter, VolumeUnit.Milliliter, VolumeUnit.Liter], NbOperations - 1) + .Prepend(Volume.From(Value, StartingUnit)).ToArray(); + Volume firstQuantity = _quantities[0]; + Console.Out.WriteLine("Quantities prepared: starting unit = {0}", firstQuantity.Unit); + } + + [Benchmark(Baseline = true)] + public Volume SumOfVolumes() + { +#if NET + return UnitsNet.GenericMath.GenericMathExtensions.Sum(_quantities); +#else + Volume sum = Volume.Zero; + foreach (var quantity in _quantities) + { + sum = quantity + sum; + } + + return sum; +#endif + } + + [Benchmark(Baseline = false)] + public Volume SumOfVolumesInBaseUnit() + { + return _quantities.Sum(Volume.BaseUnit); + } + + [Benchmark(Baseline = false)] + public Volume SumOfVolumesInMilliliter() + { + return _quantities.Sum(VolumeUnit.Milliliter); + } +} diff --git a/UnitsNet.Benchmark/Operators/Divisions/MassDividedByVolumeBenchmarks.cs b/UnitsNet.Benchmark/Operators/Divisions/MassDividedByVolumeBenchmarks.cs new file mode 100644 index 0000000000..ff28e8b517 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Divisions/MassDividedByVolumeBenchmarks.cs @@ -0,0 +1,32 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Divisions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class MassDividedByVolumeBenchmarks +{ + private static readonly double MassValue = 1.23; + private static readonly double VolumeValue = 9.42; + + [Benchmark] + public Density MassByVolume() + { + Density result = default; + foreach (MassUnit massUnit in Mass.Units) + { + var mass = new Mass(MassValue, massUnit); + foreach (VolumeUnit volumeUnit in Volume.Units) + { + var volume = new Volume(VolumeValue, volumeUnit); + + result = mass / volume; + } + } + + return result; + } +} diff --git a/UnitsNet.Benchmark/Operators/Divisions/MassDividedByVolumeWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Divisions/MassDividedByVolumeWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..e880d6185d --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Divisions/MassDividedByVolumeWithRandomUnitsBenchmarks.cs @@ -0,0 +1,45 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Divisions; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class MassDividedByVolumeWithRandomUnitsBenchmarks +{ + private static readonly double MassValue = 1.23; + private static readonly double VolumeValue = 9.42; + + private readonly Random _random = new(42); + private (Mass left, Volume right)[] _operands; + + [Params(1000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = _random.GetRandomQuantities(MassValue, Mass.Units, NbOperations) + .Zip(_random.GetRandomQuantities(VolumeValue, Volume.Units, NbOperations), (mass, volume) => (volume: mass, density: volume)) + .ToArray(); + } + + [Benchmark] + public Density MassByVolume() + { + Density result = default; + foreach ((Mass mass, Volume volume) in _operands) + { + result = mass / volume; + } + + return result; + } +} diff --git a/UnitsNet.Benchmark/Operators/Multiplications/VolumeTimesDensityBenchmarks.cs b/UnitsNet.Benchmark/Operators/Multiplications/VolumeTimesDensityBenchmarks.cs new file mode 100644 index 0000000000..63faa458c8 --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Multiplications/VolumeTimesDensityBenchmarks.cs @@ -0,0 +1,50 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Multiplications; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class VolumeTimesDensityBenchmarks +{ + private static readonly double DensityValue = 1.23; + private static readonly double VolumeValue = 9.42; + + [Benchmark(Baseline = true)] + public Mass VolumeTimesDensity() + { + Mass result = default; + foreach (VolumeUnit volumeUnit in Volume.Units) + { + var volume = new Volume(VolumeValue, volumeUnit); + foreach (DensityUnit densityUnit in Density.Units) + { + var density = new Density(DensityValue, densityUnit); + + result = volume * density; + } + } + + return result; + } + + [Benchmark(Baseline = false)] + public Mass DensityTimesVolume() + { + Mass result = default; + foreach (VolumeUnit volumeUnit in Volume.Units) + { + var volume = new Volume(VolumeValue, volumeUnit); + foreach (DensityUnit densityUnit in Density.Units) + { + var density = new Density(DensityValue, densityUnit); + + result = density * volume; + } + } + + return result; + } +} diff --git a/UnitsNet.Benchmark/Operators/Multiplications/VolumeTimesDensityWithRandomUnitsBenchmarks.cs b/UnitsNet.Benchmark/Operators/Multiplications/VolumeTimesDensityWithRandomUnitsBenchmarks.cs new file mode 100644 index 0000000000..db2a40c94b --- /dev/null +++ b/UnitsNet.Benchmark/Operators/Multiplications/VolumeTimesDensityWithRandomUnitsBenchmarks.cs @@ -0,0 +1,58 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark.Operators.Multiplications; + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net80)] +public class VolumeTimesDensityWithRandomUnitsBenchmarks +{ + private static readonly double DensityValue = 1.23; + private static readonly double VolumeValue = 9.42; + + private readonly Random _random = new Random(42); + private (Volume volume, Density density)[] _operands; + + [Params(1000)] + public int NbOperations { get; set; } + + [GlobalSetup] + public void PrepareQuantities() + { + _operands = _random.GetRandomQuantities(VolumeValue, Volume.Units, NbOperations) + .Zip(_random.GetRandomQuantities(DensityValue, Density.Units, NbOperations), + (volume, density) => (volume, density)) + .ToArray(); + } + + [Benchmark(Baseline = true)] + public Mass VolumeTimesDensity() + { + Mass result = default; + foreach ((Volume volume, Density density) in _operands) + { + result = volume * density; + } + + return result; + } + + [Benchmark(Baseline = false)] + public Mass DensityTimesVolume() + { + Mass result = default; + foreach ((Volume volume, Density density) in _operands) + { + result = density * volume; + } + + return result; + } +} diff --git a/UnitsNet.Benchmark/Program.cs b/UnitsNet.Benchmark/Program.cs index 75b1a89759..27e66760b3 100644 --- a/UnitsNet.Benchmark/Program.cs +++ b/UnitsNet.Benchmark/Program.cs @@ -1,87 +1,25 @@ -using BenchmarkDotNet.Attributes; +using System; +using System.Diagnostics; +using System.Reflection; +using BenchmarkDotNet.Configs; using BenchmarkDotNet.Running; -using UnitsNet.Units; -namespace UnitsNet.Benchmark -{ - [MemoryDiagnoser] - public class UnitsNetBenchmarks - { - private readonly Length _length = Length.FromMeters(3.0); - private readonly IQuantity _lengthIQuantity = Length.FromMeters(3.0); - - [Benchmark] - [BenchmarkCategory("Construction")] - public Length Constructor() => new Length(3.0, LengthUnit.Meter); - - [Benchmark] - [BenchmarkCategory("Construction")] - public Length Constructor_SI() => new Length(3.0, UnitSystem.SI); - - [Benchmark] - [BenchmarkCategory("Construction")] - public Length FromMethod() => Length.FromMeters(3.0); - - [Benchmark] - [BenchmarkCategory("Transformation")] - public double ToProperty() => _length.Centimeters; - - [Benchmark] - [BenchmarkCategory("Transformation, Value")] - public double As() => _length.As(LengthUnit.Centimeter); - - [Benchmark] - [BenchmarkCategory("Transformation, Value")] - public double As_SI() => _length.As(UnitSystem.SI); - - [Benchmark] - [BenchmarkCategory("Transformation, Quantity")] - public Length ToUnit() => _length.ToUnit(LengthUnit.Centimeter); - - [Benchmark] - [BenchmarkCategory("Transformation, Quantity")] - public Length ToUnit_SI() => _length.ToUnit(UnitSystem.SI); - - [Benchmark] - [BenchmarkCategory("ToString")] - public string ToStringTest() => _length.ToString(); +namespace UnitsNet.Benchmark; - [Benchmark] - [BenchmarkCategory("Parsing")] - public Length Parse() => Length.Parse("3.0 m"); - - [Benchmark] - [BenchmarkCategory("Parsing")] - public bool TryParseValid() => Length.TryParse("3.0 m", out _); - - [Benchmark] - [BenchmarkCategory("Parsing")] - public bool TryParseInvalid() => Length.TryParse("3.0 zoom", out _); - - [Benchmark] - [BenchmarkCategory("Construction")] - public IQuantity QuantityFrom() => Quantity.From(3.0, LengthUnit.Meter); - - [Benchmark] - [BenchmarkCategory("Transformation, Value")] - public double IQuantity_As() => _lengthIQuantity.As(LengthUnit.Centimeter); - - [Benchmark] - [BenchmarkCategory("Transformation, Value")] - public double IQuantity_As_SI() => _lengthIQuantity.As(UnitSystem.SI); - - [Benchmark] - [BenchmarkCategory("Transformation, Quantity")] - public IQuantity IQuantity_ToUnit() => _lengthIQuantity.ToUnit(LengthUnit.Centimeter); - - [Benchmark] - [BenchmarkCategory("ToString")] - public string IQuantity_ToStringTest() => _lengthIQuantity.ToString(); - } - - class Program +internal class Program +{ + private static void Main(string[] args) { - static void Main(string[] args) - => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + var switcher = new BenchmarkSwitcher(Assembly.GetExecutingAssembly()); + Console.Out.WriteLine("Starting benchmarks with args = {0}", string.Join(" ", args)); + if (Debugger.IsAttached) + { + Console.Out.WriteLine("Using an attached debugger: setting configuration to DebugInProcessConfig"); + switcher.Run(args, new DebugInProcessConfig()); + } + else + { + switcher.Run(args); + } } } diff --git a/UnitsNet.Benchmark/UnitsNetBenchmarks.cs b/UnitsNet.Benchmark/UnitsNetBenchmarks.cs new file mode 100644 index 0000000000..3b34d38ac9 --- /dev/null +++ b/UnitsNet.Benchmark/UnitsNetBenchmarks.cs @@ -0,0 +1,82 @@ +// Licensed under MIT No Attribution, see LICENSE file at the root. +// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet. + +using BenchmarkDotNet.Attributes; +using UnitsNet.Units; + +namespace UnitsNet.Benchmark; + +[MemoryDiagnoser] +public class UnitsNetBenchmarks +{ + private readonly Length _length = Length.FromMeters(3.0); + private readonly IQuantity _lengthIQuantity = Length.FromMeters(3.0); + + [Benchmark] + [BenchmarkCategory("Construction")] + public Length Constructor() => new Length(3.0, LengthUnit.Meter); + + [Benchmark] + [BenchmarkCategory("Construction")] + public Length Constructor_SI() => new Length(3.0, UnitSystem.SI); + + [Benchmark] + [BenchmarkCategory("Construction")] + public Length FromMethod() => Length.FromMeters(3.0); + + [Benchmark] + [BenchmarkCategory("Transformation")] + public double ToProperty() => _length.Centimeters; + + [Benchmark] + [BenchmarkCategory("Transformation, Value")] + public double As() => _length.As(LengthUnit.Centimeter); + + [Benchmark] + [BenchmarkCategory("Transformation, Value")] + public double As_SI() => _length.As(UnitSystem.SI); + + [Benchmark] + [BenchmarkCategory("Transformation, Quantity")] + public Length ToUnit() => _length.ToUnit(LengthUnit.Centimeter); + + [Benchmark] + [BenchmarkCategory("Transformation, Quantity")] + public Length ToUnit_SI() => _length.ToUnit(UnitSystem.SI); + + [Benchmark] + [BenchmarkCategory("ToString")] + public string ToStringTest() => _length.ToString(); + + [Benchmark] + [BenchmarkCategory("Parsing")] + public Length Parse() => Length.Parse("3.0 m"); + + [Benchmark] + [BenchmarkCategory("Parsing")] + public bool TryParseValid() => Length.TryParse("3.0 m", out _); + + [Benchmark] + [BenchmarkCategory("Parsing")] + public bool TryParseInvalid() => Length.TryParse("3.0 zoom", out _); + + [Benchmark] + [BenchmarkCategory("Construction")] + public IQuantity QuantityFrom() => Quantity.From(3.0, LengthUnit.Meter); + + [Benchmark] + [BenchmarkCategory("Transformation, Value")] + public double IQuantity_As() => _lengthIQuantity.As(LengthUnit.Centimeter); + + [Benchmark] + [BenchmarkCategory("Transformation, Value")] + public double IQuantity_As_SI() => _lengthIQuantity.As(UnitSystem.SI); + + [Benchmark] + [BenchmarkCategory("Transformation, Quantity")] + public IQuantity IQuantity_ToUnit() => _lengthIQuantity.ToUnit(LengthUnit.Centimeter); + + [Benchmark] + [BenchmarkCategory("ToString")] + public string IQuantity_ToStringTest() => _lengthIQuantity.ToString(); +} From 0801c2939aac14073a8809c6c21d186d887b641f Mon Sep 17 00:00:00 2001 From: lipchev Date: Tue, 7 Jan 2025 23:28:04 +0200 Subject: [PATCH 2/2] adding the required srtuct constraint to the BenchmarkHelpers --- UnitsNet.Benchmark/BenchmarkHelpers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitsNet.Benchmark/BenchmarkHelpers.cs b/UnitsNet.Benchmark/BenchmarkHelpers.cs index 020788317a..477c1c67e3 100644 --- a/UnitsNet.Benchmark/BenchmarkHelpers.cs +++ b/UnitsNet.Benchmark/BenchmarkHelpers.cs @@ -18,7 +18,7 @@ public static string[] GetRandomAbbreviations(this Random random, Uni public static (TQuantity Quantity, TUnit Unit)[] GetRandomConversions(this Random random, double value, TUnit[] options, int nbConversions) where TQuantity : IQuantity - where TUnit : Enum + where TUnit : struct, Enum { var quantities = GetRandomQuantities(random, value, options, nbConversions); TUnit[] units = random.GetItems(options, nbConversions); @@ -26,7 +26,7 @@ public static (TQuantity Quantity, TUnit Unit)[] GetRandomConversions GetRandomQuantities(this Random random, double value, TUnit[] units, int nbQuantities) - where TQuantity : IQuantity where TUnit : Enum + where TQuantity : IQuantity where TUnit : struct, Enum { IEnumerable quantities = random.GetItems(units, nbQuantities).Select(unit => (TQuantity)Quantity.From(value, unit)); return quantities;