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

Enum boxing optimizations (UnitKey) #1507

Open
wants to merge 6 commits into
base: release/v6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" />
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
27 changes: 19 additions & 8 deletions UnitsNet.Benchmark/Conversions/FromString/ParseUnitBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using UnitsNet.Units;
Expand All @@ -10,44 +11,54 @@ namespace UnitsNet.Benchmark.Conversions.FromString;
[SimpleJob(RuntimeMoniker.Net80)]
public class ParseUnitBenchmarks
{
private const int NbAbbreviations = 1000;

private static readonly CultureInfo Culture = CultureInfo.InvariantCulture;
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);
// initializes the QuantityInfoLookup and the abbreviations cache
Mass.TryParseUnit("_invalid", Culture, out _);
}

[GlobalSetup(Target = nameof(ParseVolumeUnit))]
public void PrepareVolumeUnits()
{
_volumeUnits = _random.GetItems(["ml", "l", "L", "cm³", "m³"], NbAbbreviations);
// initializes the QuantityInfoLookup and the abbreviations cache
Volume.TryParseUnit("_invalid", Culture, out _);
}

[GlobalSetup(Target = nameof(ParseDensityUnit))]
public void PrepareDensityUnits()
{
_densityUnits = _random.GetRandomAbbreviations<DensityUnit>(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations);
// initializes the QuantityInfoLookup and the abbreviations cache
Density.TryParseUnit("_invalid", Culture, out _);
}

[GlobalSetup(Target = nameof(ParsePressureUnit))]
public void PreparePressureUnits()
{
_pressureUnits = _random.GetRandomAbbreviations<PressureUnit>(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations);
// initializes the QuantityInfoLookup and the abbreviations cache
Pressure.TryParseUnit("_invalid", Culture, out _);
}

[GlobalSetup(Target = nameof(ParseVolumeFlowUnit))]
public void PrepareVolumeFlowUnits()
{
_volumeFlowUnits = _random.GetRandomAbbreviations<VolumeFlowUnit>(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations);
// initializes the QuantityInfoLookup and the abbreviations cache
VolumeFlow.TryParseUnit("_invalid", Culture, out _);
}

[Benchmark(Baseline = true)]
Expand All @@ -56,7 +67,7 @@ public MassUnit ParseMassUnit()
MassUnit unit = default;
foreach (var unitToParse in _massUnits)
{
unit = Mass.ParseUnit(unitToParse);
unit = Mass.ParseUnit(unitToParse, Culture);
}

return unit;
Expand All @@ -68,7 +79,7 @@ public VolumeUnit ParseVolumeUnit()
VolumeUnit unit = default;
foreach (var unitToParse in _volumeUnits)
{
unit = Volume.ParseUnit(unitToParse);
unit = Volume.ParseUnit(unitToParse, Culture);
}

return unit;
Expand All @@ -80,7 +91,7 @@ public DensityUnit ParseDensityUnit()
DensityUnit unit = default;
foreach (var unitToParse in _densityUnits)
{
unit = Density.ParseUnit(unitToParse);
unit = Density.ParseUnit(unitToParse, Culture);
}

return unit;
Expand All @@ -92,7 +103,7 @@ public PressureUnit ParsePressureUnit()
PressureUnit unit = default;
foreach (var unitToParse in _pressureUnits)
{
unit = Pressure.ParseUnit(unitToParse);
unit = Pressure.ParseUnit(unitToParse, Culture);
}

return unit;
Expand All @@ -104,7 +115,7 @@ public VolumeFlowUnit ParseVolumeFlowUnit()
VolumeFlowUnit unit = default;
foreach (var unitToParse in _volumeFlowUnits)
{
unit = VolumeFlow.ParseUnit(unitToParse);
unit = VolumeFlow.ParseUnit(unitToParse, Culture);
}

return unit;
Expand Down
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before:

Method Job Runtime NbAbbreviations Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
FromMassString .NET 8.0 .NET 8.0 1000 29.54 ms 0.443 ms 0.415 ms 1.00 0.02 2833.3333 - 47.57 MB 1.00
FromVolumeUnitAbbreviation .NET 8.0 .NET 8.0 1000 66.68 ms 1.132 ms 1.004 ms 2.26 0.04 5750.0000 500.0000 93.34 MB 1.96
FromPressureUnitAbbreviation .NET 8.0 .NET 8.0 1000 69.09 ms 1.319 ms 1.234 ms 2.34 0.05 6000.0000 333.3333 100.31 MB 2.11
FromVolumeFlowUnitAbbreviation .NET 8.0 .NET 8.0 1000 133.05 ms 2.236 ms 2.092 ms 4.51 0.09 12000.0000 1000.0000 200.47 MB 4.21
                         
FromMassString .NET Framework 4.8 .NET Framework 4.8 1000 65.22 ms 0.542 ms 0.507 ms 1.00 0.01 13000.0000 500.0000 78.08 MB 1.00
FromVolumeUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 135.02 ms 1.001 ms 0.937 ms 2.07 0.02 25250.0000 1500.0000 152.05 MB 1.95
FromPressureUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 123.84 ms 1.260 ms 1.117 ms 1.90 0.02 23000.0000 1250.0000 139.26 MB 1.78
FromVolumeFlowUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 211.69 ms 2.007 ms 1.779 ms 3.25 0.04 44333.3333 5000.0000 267.62 MB 3.43

After:

Method Job Runtime NbAbbreviations Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
FromMassString .NET 8.0 .NET 8.0 1000 19.25 ms 0.364 ms 0.341 ms 1.00 0.02 2406.2500 93.7500 38.85 MB 1.00
FromVolumeUnitAbbreviation .NET 8.0 .NET 8.0 1000 42.83 ms 0.779 ms 0.691 ms 2.22 0.05 4800.0000 400.0000 77.39 MB 1.99
FromPressureUnitAbbreviation .NET 8.0 .NET 8.0 1000 47.75 ms 0.742 ms 0.694 ms 2.48 0.05 5250.0000 500.0000 85.49 MB 2.20
FromVolumeFlowUnitAbbreviation .NET 8.0 .NET 8.0 1000 92.35 ms 1.824 ms 1.873 ms 4.80 0.12 10500.0000 1500.0000 174 MB 4.48
                         
FromMassString .NET Framework 4.8 .NET Framework 4.8 1000 29.24 ms 0.142 ms 0.119 ms 1.00 0.01 10031.2500 437.5000 60.37 MB 1.00
FromVolumeUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 59.12 ms 0.530 ms 0.470 ms 2.02 0.02 19555.5556 1333.3333 117.49 MB 1.95
FromPressureUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 54.80 ms 0.732 ms 0.685 ms 1.87 0.02 17555.5556 1111.1111 105.38 MB 1.75
FromVolumeFlowUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 98.17 ms 0.625 ms 0.554 ms 3.36 0.02 35000.0000 4333.3333 211.02 MB 3.50

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using System.Globalization;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using UnitsNet.Units;

namespace UnitsNet.Benchmark.Conversions.FromString;

[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
public class QuantityFromStringBenchmarks
{
private static readonly CultureInfo Culture = CultureInfo.InvariantCulture;
private static readonly string ValueToParse = 123.456.ToString(Culture);

private readonly Random _random = new(42);
private string[] _quantitiesToParse;

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

[GlobalSetup(Target = nameof(FromMassString))]
public void PrepareMassStrings()
{
// can't have "mg" or "g" (see Acceleration.StandardGravity) and who knows what more...
_quantitiesToParse = _random.GetItems(["kg", "lbs", "Mlbs"], NbAbbreviations).Select(abbreviation => $"{ValueToParse} {abbreviation}").ToArray();
}

[GlobalSetup(Target = nameof(FromVolumeUnitAbbreviation))]
public void PrepareVolumeStrings()
{
_quantitiesToParse = _random.GetItems(["ml", "l", "cm³", "m³"], NbAbbreviations).Select(abbreviation => $"{ValueToParse} {abbreviation}").ToArray();;
}

[GlobalSetup(Target = nameof(FromPressureUnitAbbreviation))]
public void PreparePressureUnits()
{
_quantitiesToParse = _random.GetRandomAbbreviations<PressureUnit>(UnitsNetSetup.Default.UnitAbbreviations, NbAbbreviations).Select(abbreviation => $"{ValueToParse} {abbreviation}").ToArray();;
}

[GlobalSetup(Target = nameof(FromVolumeFlowUnitAbbreviation))]
public void PrepareVolumeFlowUnits()
{
// can't have "bpm" (see Frequency)
_quantitiesToParse =
_random.GetItems(
UnitsNetSetup.Default.UnitAbbreviations.GetAllUnitAbbreviationsForQuantity(typeof(VolumeFlowUnit)).Where(x => x != "bpm").ToArray(),
NbAbbreviations).Select(abbreviation => $"{ValueToParse} {abbreviation}").ToArray();
}

[Benchmark(Baseline = true)]
public IQuantity FromMassString()
{
IQuantity quantity = null;
foreach (var quantityString in _quantitiesToParse)
{
quantity = Quantity.Parse(Culture, typeof(Mass), quantityString);
}

return quantity;
}

[Benchmark(Baseline = false)]
public IQuantity FromVolumeUnitAbbreviation()
{
IQuantity quantity = null;
foreach (var quantityString in _quantitiesToParse)
{
quantity = Quantity.Parse(Culture, typeof(Volume), quantityString);
}

return quantity;
}

[Benchmark(Baseline = false)]
public IQuantity FromPressureUnitAbbreviation()
{
IQuantity quantity = null;
foreach (var quantityString in _quantitiesToParse)
{
quantity = Quantity.Parse(Culture, typeof(Pressure), quantityString);
}

return quantity;
}

[Benchmark(Baseline = false)]
public IQuantity FromVolumeFlowUnitAbbreviation()
{
IQuantity quantity = null;
foreach (var quantityString in _quantitiesToParse)
{
quantity = Quantity.Parse(Culture, typeof(VolumeFlow), quantityString);
}

return quantity;
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before:

Method Job Runtime NbAbbreviations Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
FromMassUnitAbbreviation .NET 8.0 .NET 8.0 1000 482.2 ms 4.45 ms 4.16 ms 1.00 0.01 28000.0000 450.85 MB 1.00
FromVolumeUnitAbbreviation .NET 8.0 .NET 8.0 1000 476.1 ms 8.37 ms 7.83 ms 0.99 0.02 28000.0000 450.85 MB 1.00
FromPressureUnitAbbreviation .NET 8.0 .NET 8.0 1000 480.2 ms 8.78 ms 8.21 ms 1.00 0.02 28000.0000 450.85 MB 1.00
FromVolumeFlowUnitAbbreviation .NET 8.0 .NET 8.0 1000 479.6 ms 3.31 ms 3.10 ms 0.99 0.01 28000.0000 450.85 MB 1.00
                       
FromMassUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 1,391.4 ms 2.97 ms 2.78 ms 1.00 0.00 129000.0000 779.46 MB 1.00
FromVolumeUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 1,392.8 ms 2.08 ms 1.84 ms 1.00 0.00 129000.0000 779.46 MB 1.00
FromPressureUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 1,397.4 ms 3.15 ms 2.95 ms 1.00 0.00 129000.0000 779.46 MB 1.00
FromVolumeFlowUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 1,406.1 ms 3.66 ms 3.42 ms 1.01 0.00 129000.0000 779.46 MB 1.00

After:

Method Job Runtime NbAbbreviations Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
FromMassUnitAbbreviation .NET 8.0 .NET 8.0 1000 104.8 ms 1.00 ms 0.94 ms 1.00 0.01 14400.0000 230.67 MB 1.00
FromVolumeUnitAbbreviation .NET 8.0 .NET 8.0 1000 104.1 ms 2.03 ms 2.25 ms 0.99 0.02 14400.0000 230.67 MB 1.00
FromPressureUnitAbbreviation .NET 8.0 .NET 8.0 1000 103.6 ms 2.01 ms 2.15 ms 0.99 0.02 14400.0000 230.67 MB 1.00
FromVolumeFlowUnitAbbreviation .NET 8.0 .NET 8.0 1000 103.4 ms 1.08 ms 0.95 ms 0.99 0.01 14400.0000 230.67 MB 1.00
                       
FromMassUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 199.5 ms 2.17 ms 2.03 ms 1.00 0.01 38333.3333 231.69 MB 1.00
FromVolumeUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 200.9 ms 2.26 ms 2.11 ms 1.01 0.01 38333.3333 231.69 MB 1.00
FromPressureUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 201.6 ms 1.68 ms 1.49 ms 1.01 0.01 38333.3333 231.69 MB 1.00
FromVolumeFlowUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 199.3 ms 2.26 ms 2.12 ms 1.00 0.01 38333.3333 231.69 MB 1.00

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System;
using System.Globalization;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using UnitsNet.Units;

namespace UnitsNet.Benchmark.Conversions.FromString;

[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net80)]
public class QuantityFromUnitAbbreviationBenchmarks
{
private static readonly CultureInfo Culture = CultureInfo.InvariantCulture;
private readonly Random _random = new(42);
private string[] _massUnits;
private string[] _pressureUnits;
private string[] _volumeFlowUnits;
private string[] _volumeUnits = [];

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

[GlobalSetup(Target = nameof(FromMassUnitAbbreviation))]
public void PrepareMassUnits()
{
// can't have "mg" or "g" (see Acceleration.StandardGravity) and who knows what more...
_massUnits = _random.GetItems(["kg", "lbs", "Mlbs"], NbAbbreviations);
}

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

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

[GlobalSetup(Target = nameof(FromVolumeFlowUnitAbbreviation))]
public void PrepareVolumeFlowUnits()
{
// can't have "bpm" (see Frequency)
_volumeFlowUnits =
_random.GetItems(
UnitsNetSetup.Default.UnitAbbreviations.GetAllUnitAbbreviationsForQuantity(typeof(VolumeFlowUnit)).Where(x => x != "bpm").ToArray(),
NbAbbreviations);
}

[Benchmark(Baseline = true)]
public IQuantity FromMassUnitAbbreviation()
{
IQuantity quantity = null;
foreach (var unitToParse in _massUnits)
{
quantity = Quantity.FromUnitAbbreviation(Culture, 1, unitToParse);
}

return quantity;
}

[Benchmark(Baseline = false)]
public IQuantity FromVolumeUnitAbbreviation()
{
IQuantity quantity = null;
foreach (var unitToParse in _volumeUnits)
{
quantity = Quantity.FromUnitAbbreviation(Culture, 1, unitToParse);
}

return quantity;
}

[Benchmark(Baseline = false)]
public IQuantity FromPressureUnitAbbreviation()
{
IQuantity quantity = null;
foreach (var unitToParse in _pressureUnits)
{
quantity = Quantity.FromUnitAbbreviation(Culture, 1, unitToParse);
}

return quantity;
}

[Benchmark(Baseline = false)]
public IQuantity FromVolumeFlowUnitAbbreviation()
{
IQuantity quantity = null;
foreach (var unitToParse in _volumeFlowUnits)
{
quantity = Quantity.FromUnitAbbreviation(Culture, 1, unitToParse);
}

return quantity;
}
}
Loading