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

Conversation

lipchev
Copy link
Collaborator

@lipchev lipchev commented Jan 31, 2025

Enum boxing optimizations:

  • introduce the UnitKey struct as a replacement for the conversions from TUnit to Enum
  • re-mapped the UnitAbbreviationCache using the UnitKey for the concurrent dictionary
  • removed all Convert.ToInt32(..) calls
  • optimized the QuantityInfoLookup collections
  • refactored some of the Quantity/UnitConverter code (using the UnitParser/QuantityInfoLookup)
  • added some tests and benchmarks covering the new modifications

Breaking changes:

  • changing the Quantity.Names from string[] to IReadOnlyCollection<string>
  • changing the Quantity.Infos from QuantityInfo[] to IReadOnlyList<QuantityInfo>
  • marking the UnitConverter.ConvertByAbbreviation method that takes a string? for the IFormatProvider? as [Obsolete] (created an overload)
  • UnitsNetSetup: replaced the ICollection<QuantityInfo> constructor parameter with IEnumerable<QuantityInfo>
  • renamed the QuantityInfo.ValueType to QuantityInfo.QuantityType (making the old property [Obsolete])
  • renamed two of the parameters of UnitConverter.ConvertByName

- introduce the UnitKey struct as a replacement for the conversions from TUnit to Enum
- re-map the UnitAbbreviationCache using the UnitKey for the concurrent dictionary
- remove all Convert.ToInt32(..) calls
- optimize the QuantityInfoLookup collections
- refactored some of the Quantity/UnitConverter code (using the UnitParser/UnitAbbreviationsCache)
- added some tests and benchmarks covering the new modifications
@lipchev lipchev changed the title Enum boxing optimizations: Enum boxing optimizations (UnitKey) Jan 31, 2025
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

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

Copy link
Collaborator Author

@lipchev lipchev Jan 31, 2025

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
FromMassUnitName .NET 8.0 .NET 8.0 1000 453.0 μs 8.92 μs 9.55 μs 1.00 0.03 31.7383 - 523.49 KB 1.00
FromVolumeUnitAbbreviation .NET 8.0 .NET 8.0 1000 715.0 μs 2.71 μs 2.40 μs 1.58 0.03 44.9219 - 734.53 KB 1.40
FromPressureUnitAbbreviation .NET 8.0 .NET 8.0 1000 601.0 μs 3.31 μs 2.93 μs 1.33 0.03 41.9922 - 695.47 KB 1.33
FromVolumeFlowUnitAbbreviation .NET 8.0 .NET 8.0 1000 825.9 μs 13.83 μs 12.26 μs 1.82 0.05 54.6875 0.9766 898.74 KB 1.72
                         
FromMassUnitName .NET Framework 4.8 .NET Framework 4.8 1000 779.2 μs 4.34 μs 4.06 μs 1.00 0.01 103.5156 - 638.86 KB 1.00
FromVolumeUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 1,257.2 μs 11.20 μs 10.47 μs 1.61 0.02 138.6719 - 861.88 KB 1.35
FromPressureUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 1,044.7 μs 13.83 μs 12.93 μs 1.34 0.02 136.7188 - 846.97 KB 1.33
FromVolumeFlowUnitAbbreviation .NET Framework 4.8 .NET Framework 4.8 1000 1,446.6 μs 17.30 μs 16.18 μs 1.86 0.02 169.9219 - 1049.27 KB 1.64

After:

Method Job Runtime NbAbbreviations Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
FromMassUnitName .NET 8.0 .NET 8.0 1000 124.5 μs 0.94 μs 0.88 μs 1.00 0.01 9.0332 148.44 KB 1.00
FromVolumeUnitName .NET 8.0 .NET 8.0 1000 186.9 μs 1.44 μs 1.35 μs 1.50 0.01 9.0332 148.44 KB 1.00
FromPressureUnitName .NET 8.0 .NET 8.0 1000 156.0 μs 1.22 μs 1.14 μs 1.25 0.01 9.0332 148.44 KB 1.00
FromVolumeFlowUnitName .NET 8.0 .NET 8.0 1000 215.1 μs 1.16 μs 1.08 μs 1.73 0.01 9.0332 148.44 KB 1.00
                       
FromMassUnitName .NET Framework 4.8 .NET Framework 4.8 1000 190.1 μs 1.67 μs 1.56 μs 1.00 0.01 24.1699 148.88 KB 1.00
FromVolumeUnitName .NET Framework 4.8 .NET Framework 4.8 1000 272.5 μs 2.01 μs 1.68 μs 1.43 0.01 23.9258 148.88 KB 1.00
FromPressureUnitName .NET Framework 4.8 .NET Framework 4.8 1000 247.6 μs 2.30 μs 1.92 μs 1.30 0.01 23.9258 148.88 KB 1.00
FromVolumeFlowUnitName .NET Framework 4.8 .NET Framework 4.8 1000 343.3 μs 1.85 μs 1.64 μs 1.81 0.02 23.9258 148.88 KB 1.00

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

...must have missed to rename some of these methods..

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed in 03973bf

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 NbConversions Format Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
MassToUnit .NET 8.0 .NET 8.0 1000 A 218.4 μs 2.06 μs 1.82 μs 1.00 0.01 18.3105 299.32 KB 1.00
VolumeFlowToUnit .NET 8.0 .NET 8.0 1000 A 259.7 μs 1.56 μs 1.31 μs 1.19 0.01 18.5547 303.12 KB 1.01
                         
MassToUnit .NET Framework 4.8 .NET Framework 4.8 1000 A 688.3 μs 6.91 μs 6.13 μs 1.00 0.01 74.2188 460.19 KB 1.00
VolumeFlowToUnit .NET Framework 4.8 .NET Framework 4.8 1000 A 787.3 μs 12.25 μs 11.46 μs 1.14 0.02 81.0547 498.97 KB 1.08
                         
MassToUnit .NET 8.0 .NET 8.0 1000 E 328.7 μs 2.78 μs 2.32 μs 1.00 0.01 25.3906 417.59 KB 1.00
VolumeFlowToUnit .NET 8.0 .NET 8.0 1000 E 385.6 μs 3.53 μs 3.30 μs 1.17 0.01 25.8789 429.64 KB 1.03
                         
MassToUnit .NET Framework 4.8 .NET Framework 4.8 1000 E 1,204.0 μs 6.33 μs 5.62 μs 1.00 0.01 126.9531 781.48 KB 1.00
VolumeFlowToUnit .NET Framework 4.8 .NET Framework 4.8 1000 E 1,293.8 μs 10.65 μs 9.96 μs 1.07 0.01 132.8125 825.57 KB 1.06
                         
MassToUnit .NET 8.0 .NET 8.0 1000 G 323.7 μs 4.11 μs 3.85 μs 1.00 0.02 24.9023 408.62 KB 1.00
VolumeFlowToUnit .NET 8.0 .NET 8.0 1000 G 380.4 μs 3.00 μs 2.66 μs 1.18 0.02 25.3906 417.5 KB 1.02
                         
MassToUnit .NET Framework 4.8 .NET Framework 4.8 1000 G 1,221.0 μs 10.35 μs 9.68 μs 1.00 0.01 121.0938 751.29 KB 1.00
VolumeFlowToUnit .NET Framework 4.8 .NET Framework 4.8 1000 G 1,302.3 μs 15.53 μs 13.77 μs 1.07 0.01 128.9063 798.54 KB 1.06
                         
MassToUnit .NET 8.0 .NET 8.0 1000 N 353.0 μs 1.31 μs 1.16 μs 1.00 0.00 24.4141 403.69 KB 1.00
VolumeFlowToUnit .NET 8.0 .NET 8.0 1000 N 428.3 μs 3.79 μs 3.55 μs 1.21 0.01 25.3906 415.38 KB 1.03
                         
MassToUnit .NET Framework 4.8 .NET Framework 4.8 1000 N 1,217.6 μs 10.66 μs 9.97 μs 1.00 0.01 121.0938 750.96 KB 1.00
VolumeFlowToUnit .NET Framework 4.8 .NET Framework 4.8 1000 N 1,295.9 μs 9.27 μs 8.67 μs 1.06 0.01 128.9063 795.49 KB 1.06
                         
MassToUnit .NET 8.0 .NET 8.0 1000 S 437.3 μs 4.77 μs 4.23 μs 1.00 0.01 36.1328 596.14 KB 1.00
VolumeFlowToUnit .NET 8.0 .NET 8.0 1000 S 500.5 μs 4.92 μs 4.61 μs 1.14 0.01 36.1328 605.06 KB 1.01
                         
MassToUnit .NET Framework 4.8 .NET Framework 4.8 1000 S 1,389.8 μs 12.66 μs 11.23 μs 1.00 0.01 160.1563 986.45 KB 1.00
VolumeFlowToUnit .NET Framework 4.8 .NET Framework 4.8 1000 S 1,476.5 μs 8.74 μs 7.75 μs 1.06 0.01 167.9688 1033.87 KB 1.05

After:

Method Job Runtime NbConversions Format Mean Error StdDev Median Ratio RatioSD Gen0 Allocated Alloc Ratio
MassToString .NET 8.0 .NET 8.0 1000 A 32.53 μs 0.624 μs 1.656 μs 31.90 μs 1.00 0.07 8.1177 132.81 KB 1.00
VolumeFlowToString .NET 8.0 .NET 8.0 1000 A 33.46 μs 0.664 μs 1.604 μs 33.08 μs 1.03 0.07 8.1177 132.81 KB 1.00
                           
MassToString .NET Framework 4.8 .NET Framework 4.8 1000 A 95.63 μs 0.780 μs 0.651 μs 95.73 μs 1.00 0.01 21.6064 133.2 KB 1.00
VolumeFlowToString .NET Framework 4.8 .NET Framework 4.8 1000 A 121.64 μs 1.682 μs 1.574 μs 121.86 μs 1.27 0.02 21.4844 133.2 KB 1.00
                           
MassToString .NET 8.0 .NET 8.0 1000 E 136.75 μs 2.122 μs 1.984 μs 136.12 μs 1.00 0.02 15.1367 251.08 KB 1.00
VolumeFlowToString .NET 8.0 .NET 8.0 1000 E 136.72 μs 2.512 μs 2.350 μs 135.72 μs 1.00 0.02 15.8691 259.3 KB 1.03
                           
MassToString .NET Framework 4.8 .NET Framework 4.8 1000 E 518.30 μs 5.143 μs 4.294 μs 518.16 μs 1.00 0.01 73.2422 454.38 KB 1.00
VolumeFlowToString .NET Framework 4.8 .NET Framework 4.8 1000 E 584.19 μs 3.707 μs 3.467 μs 584.19 μs 1.13 0.01 74.2188 459.44 KB 1.01
                           
MassToString .NET 8.0 .NET 8.0 1000 G 138.71 μs 2.147 μs 2.008 μs 138.01 μs 1.00 0.02 14.6484 242.1 KB 1.00
VolumeFlowToString .NET 8.0 .NET 8.0 1000 G 135.84 μs 1.515 μs 1.343 μs 135.90 μs 0.98 0.02 14.8926 247.16 KB 1.02
                           
MassToString .NET Framework 4.8 .NET Framework 4.8 1000 G 513.74 μs 5.896 μs 5.515 μs 511.84 μs 1.00 0.01 68.3594 424.2 KB 1.00
VolumeFlowToString .NET Framework 4.8 .NET Framework 4.8 1000 G 530.12 μs 4.532 μs 4.018 μs 529.22 μs 1.03 0.01 70.3125 432.44 KB 1.02
                           
MassToString .NET 8.0 .NET 8.0 1000 N 148.71 μs 1.951 μs 1.825 μs 148.45 μs 1.00 0.02 14.4043 237.18 KB 1.00
VolumeFlowToString .NET 8.0 .NET 8.0 1000 N 148.93 μs 1.158 μs 1.026 μs 148.83 μs 1.00 0.01 14.8926 245.03 KB 1.03
                           
MassToString .NET Framework 4.8 .NET Framework 4.8 1000 N 511.71 μs 5.712 μs 5.343 μs 511.12 μs 1.00 0.01 68.3594 423.86 KB 1.00
VolumeFlowToString .NET Framework 4.8 .NET Framework 4.8 1000 N 537.45 μs 2.468 μs 1.927 μs 537.13 μs 1.05 0.01 69.3359 429.4 KB 1.01
                           
MassToString .NET 8.0 .NET 8.0 1000 S 232.09 μs 3.130 μs 2.928 μs 231.70 μs 1.00 0.02 26.1230 429.6 KB 1.00
VolumeFlowToString .NET 8.0 .NET 8.0 1000 S 233.04 μs 3.050 μs 2.704 μs 232.35 μs 1.00 0.02 26.3672 434.66 KB 1.01
                           
MassToString .NET Framework 4.8 .NET Framework 4.8 1000 S 669.36 μs 5.894 μs 5.514 μs 667.54 μs 1.00 0.01 106.4453 659.27 KB 1.00
VolumeFlowToString .NET Framework 4.8 .NET Framework 4.8 1000 S 700.49 μs 8.046 μs 7.526 μs 697.33 μs 1.05 0.01 108.3984 667.51 KB 1.01

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

.. I fixed the method names, but didn't bother re-running the benchmark..

public static string[] Names { get => Default.Names; }
public static IReadOnlyCollection<string> Names { get => Quantities.Names; }
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Please note this breaking change: I think there simply is no way around it - we'd have to do it at some point..

I could have probably gotten away with an IReadOnlyList but I think this is better: the backing dictionary serves a dual purpose.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've changed my mind on this, fuck the SortedDictionary I think I'm gonna go with the IReadOnlyList variant (if not for the names, then at least for the QuantityInfo collection):

Method Job Runtime Mean Error StdDev Median Ratio RatioSD Gen0 Allocated Alloc Ratio
ForLoopList .NET 8.0 .NET 8.0 53.52 μs 0.279 μs 0.261 μs 53.51 μs 1.00 0.01 - - NA
EnumerateList .NET 8.0 .NET 8.0 225.59 μs 4.511 μs 11.804 μs 221.78 μs 4.21 0.22 1.9531 40000 B NA
EnumerateSet .NET 8.0 .NET 8.0 128.22 μs 0.310 μs 0.275 μs 128.21 μs 2.40 0.01 1.7090 32000 B NA
EnumerateDictionary .NET 8.0 .NET 8.0 144.13 μs 0.240 μs 0.225 μs 144.07 μs 2.69 0.01 3.1738 56000 B NA
EnumerateSortedDictionary .NET 8.0 .NET 8.0 928.59 μs 17.819 μs 19.805 μs 928.18 μs 17.35 0.37 11.7188 208001 B NA
                       
ForLoopList .NET 9.0 .NET 9.0 54.08 μs 0.107 μs 0.095 μs 54.08 μs 1.00 0.00 - - NA
EnumerateList .NET 9.0 .NET 9.0 191.29 μs 1.693 μs 1.584 μs 190.84 μs 3.54 0.03 2.1973 40000 B NA
EnumerateSet .NET 9.0 .NET 9.0 138.43 μs 0.810 μs 0.718 μs 138.57 μs 2.56 0.01 1.7090 32000 B NA
EnumerateDictionary .NET 9.0 .NET 9.0 169.38 μs 1.550 μs 1.450 μs 168.91 μs 3.13 0.03 3.1738 56000 B NA
EnumerateSortedDictionary .NET 9.0 .NET 9.0 808.44 μs 15.826 μs 27.719 μs 811.42 μs 14.95 0.51 11.7188 208001 B NA
                       
ForLoopList .NET Framework 4.8 .NET Framework 4.8 172.25 μs 0.732 μs 0.685 μs 172.41 μs 1.00 0.01 - - NA
EnumerateList .NET Framework 4.8 .NET Framework 4.8 605.17 μs 5.267 μs 4.669 μs 606.06 μs 3.51 0.03 5.8594 40120 B NA
EnumerateSet .NET Framework 4.8 .NET Framework 4.8 623.37 μs 10.318 μs 9.147 μs 622.94 μs 3.62 0.05 5.8594 40120 B NA
EnumerateDictionary .NET Framework 4.8 .NET Framework 4.8 664.64 μs 11.315 μs 10.585 μs 664.30 μs 3.86 0.06 5.8594 40120 B NA
EnumerateSortedDictionary .NET Framework 4.8 .NET Framework 4.8 1,935.67 μs 22.253 μs 20.816 μs 1,938.44 μs 11.24 0.12 35.1563 240708 B NA

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I ended up keeping the IReadOnlyCollection<string> and just changing the IReadOnlyCollection<QuantityInfo> into IReadOnlyList<QuantityInfo> (fixed in 6dc5f09), and the Names are now backed by a regular Dictionary, meaning they should come out sorted in whatever order they were provided.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I ended up keeping the IReadOnlyCollection<string> and just changing the IReadOnlyCollection<QuantityInfo> into IReadOnlyList<QuantityInfo> (fixed in 6dc5f09), and the Names are now backed by a regular Dictionary, meaning they should come out sorted in whatever order they were provided.

Actually this isn't true - the FrozenDictionary is going to sort the names alphabetically, not in the order in which they are provided. This is mostly irrelevant for the now, as there is no way to provide anything to the UnitsNetSetup.Default..

public static QuantityInfo[] Infos => Default.Infos;
public static IReadOnlyCollection<QuantityInfo> Infos => Quantities.Infos;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the second breaking change: same deal- can't have users setting the values of the array.

I would also like to make Quantity.ByName dictionary read-only:

        /// <summary>
        /// All QuantityInfo instances mapped by quantity name that are present in the <see cref="UnitsNetSetup.Default"/> configuration.
        /// </summary>
        public static IReadOnlyDictionary<string, QuantityInfo> ByName => Quantities.ByName;

The generated part of the Quantity class should only contain a single collection of QuantityInfo that we should pass to the QuantityInfoLookup. Here's what I have in the other project:

public partial class Quantity
{
    /// <summary>
    ///     Serves as a repository for predefined quantity conversion mappings, facilitating the automatic generation and retrieval of unit conversions in the UnitsNet library.
    /// </summary>
    internal static class Provider
    {
        /// <summary>
        ///     All QuantityInfo instances that are present in UnitsNet by default.
        /// </summary>
        internal static IReadOnlyCollection<QuantityInfo> DefaultQuantities => new QuantityInfo[]
        {
            AbsorbedDoseOfIonizingRadiation.Info,

Comment on lines +190 to +197
// TODO Support custom units (via the QuantityParser), currently only hardcoded built-in quantities are supported.
if (!typeof(IQuantity).IsAssignableFrom(quantityType))
throw new ArgumentException($"Type {quantityType} must be of type UnitsNet.IQuantity.");

if (TryParse(formatProvider, quantityType, quantityString, out IQuantity? quantity))
return quantity;

throw new UnitNotFoundException($"Quantity string '{quantityString}' could not be parsed to quantity '{quantityType}'.");
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In my other project- the QuantityParser can directly instantiate an IQuantity (by calling the QuantityInfoLookup.From(..)).

Comment on lines +12 to +26
/// <summary>
/// Get a list of quantities having the given base dimensions.
/// </summary>
/// <param name="quantityInfos">The type of quantity mapping information.</param>
/// <param name="baseDimensions">The base dimensions to match.</param>
public static IEnumerable<QuantityInfo> GetQuantitiesWithBaseDimensions(this IEnumerable<QuantityInfo> quantityInfos,
BaseDimensions baseDimensions)
{
if (baseDimensions is null)
{
throw new ArgumentNullException(nameof(baseDimensions));
}

return quantityInfos.Where(info => info.BaseDimensions.Equals(baseDimensions));
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This extensions doesn't have to be public: it's currently only used from the Quantity class.

Comment on lines -73 to +84
/// Quantity value type, such as <see cref="Length"/> or <see cref="Mass"/>.
/// Quantity value type, such as <see cref="Length" /> or <see cref="Mass" />.
/// </summary>
public Type ValueType { get; }
public Type QuantityType { get; }

/// <inheritdoc cref="QuantityType" />
[Obsolete("Replaced by the QuantityType property.")]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public Type ValueType
{
get => QuantityType;
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I hope you don't mind- this has been bugging me for ages...

Comment on lines -334 to +336
public static double ConvertByName(double fromValue, string quantityName, string fromUnit, string toUnit)
public static double ConvertByName(double fromValue, string quantityName, string fromUnitName, string toUnitName)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Technically this is another breaking change..

Comment on lines +427 to +459
[Obsolete("Methods accepting a culture name are deprecated in favor of using an instance of the IFormatProvider.")]
public static double ConvertByAbbreviation(double fromValue, string quantityName, string fromUnitAbbrev, string toUnitAbbrev, string? culture)
{
if (!TryGetUnitType(quantityName, out Type? unitType))
throw new UnitNotFoundException($"The unit type for the given quantity was not found: {quantityName}");

var cultureInfo = CultureHelper.GetCultureOrInvariant(culture);

var fromUnit = UnitsNetSetup.Default.UnitParser.Parse(fromUnitAbbrev, unitType, cultureInfo); // ex: ("m", LengthUnit) => LengthUnit.Meter
var fromQuantity = Quantity.From(fromValue, fromUnit);
return ConvertByAbbreviation(fromValue, quantityName, fromUnitAbbrev, toUnitAbbrev, CultureHelper.GetCultureOrInvariant(culture));
}

var toUnit = UnitsNetSetup.Default.UnitParser.Parse(toUnitAbbrev, unitType, cultureInfo); // ex:("cm", LengthUnit) => LengthUnit.Centimeter
return fromQuantity.As(toUnit);
/// <summary>
/// Convert between any two quantity units by their abbreviations, such as converting a "Length" of N "m" to "cm".
/// This is particularly useful for creating things like a generated unit conversion UI,
/// where you list some selectors:
/// a) Quantity: Length, Mass, Force etc.
/// b) From unit: Meter, Centimeter etc if Length is selected
/// c) To unit: Meter, Centimeter etc if Length is selected
/// </summary>
/// <param name="fromValue">
/// Input value, which together with <paramref name="fromUnitAbbrev" /> represents the quantity to
/// convert from.
/// </param>
/// <param name="quantityName">The invariant quantity name, such as "Length". Does not support localization.</param>
/// <param name="fromUnitAbbrev">The abbreviation of the unit in the given culture, such as "m".</param>
/// <param name="toUnitAbbrev">The abbreviation of the unit in the given culture, such as "m".</param>
/// <param name="formatProvider">
/// The format provider to use for lookup. Defaults to <see cref="System.Globalization.CultureInfo.CurrentCulture" />
/// if null.
/// </param>
/// <example>double centimeters = ConvertByName(5, "Length", "m", "cm"); // 500</example>
/// <returns>Output value as the result of converting to <paramref name="toUnitAbbrev" />.</returns>
/// <exception cref="QuantityNotFoundException">
/// Thrown when no quantity information is found for the specified quantity name.
/// </exception>
/// <exception cref="UnitNotFoundException">No units match the abbreviation.</exception>
/// <exception cref="AmbiguousUnitParseException">More than one unit matches the abbreviation.</exception>
public static double ConvertByAbbreviation(double fromValue, string quantityName, string fromUnitAbbrev, string toUnitAbbrev, IFormatProvider? formatProvider)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've added an IFormatProvider? overload and deprecated the whole CultureHelper class:

string -> CultureInfo conversions are not in the scope of UnitsNet

If you want to- we could also make this for v5 and have the string? overload directly removed in v6.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh I think I see what the deal is- the UnitParser doesn't really care about the IFormat part of the IFormatProvider, just the CultureName.. I wonder if there is a more standard way of dealing with things that depend solely on the language / region (and not that number-formatting stuff).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Given that we ultimately depend on the ResourceManager, and it takes a CultureInfo? as it's second parameter, I think the right way (another breaking change) would be to make everything that doesn't involve formatting the number (i.e. everything in the UnitParser, UnitAbbreviationsCache, as well as this new overload that I just added) use a CultureInfo? (with the rest of the code safe-casting the IFormatProvider?)..

@lipchev
Copy link
Collaborator Author

lipchev commented Jan 31, 2025

Oh, I just remembered that there are two more benchmarks that I forgot to post:

ParseUnitBenchmarks.cs

Before:

Method Job Runtime Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
ParseMassUnit .NET 8.0 .NET 8.0 6.746 ms 0.0381 ms 0.0338 ms 1.00 0.01 640.6250 - 10.32 MB 1.00
ParseVolumeUnit .NET 8.0 .NET 8.0 14.145 ms 0.0945 ms 0.0884 ms 2.10 0.02 1109.3750 - 17.8 MB 1.72
ParseDensityUnit .NET 8.0 .NET 8.0 15.158 ms 0.1904 ms 0.1781 ms 2.25 0.03 1140.6250 - 18.38 MB 1.78
ParsePressureUnit .NET 8.0 .NET 8.0 13.221 ms 0.0667 ms 0.0624 ms 1.96 0.01 1031.2500 - 16.62 MB 1.61
ParseVolumeFlowUnit .NET 8.0 .NET 8.0 22.251 ms 0.3512 ms 0.3285 ms 3.30 0.05 1906.2500 31.2500 30.44 MB 2.95
                       
ParseMassUnit .NET Framework 4.8 .NET Framework 4.8 21.608 ms 0.0759 ms 0.0634 ms 1.00 0.00 2531.2500 - 15.34 MB 1.00
ParseVolumeUnit .NET Framework 4.8 .NET Framework 4.8 44.665 ms 0.3540 ms 0.2956 ms 2.07 0.01 4727.2727 - 28.73 MB 1.87
ParseDensityUnit .NET Framework 4.8 .NET Framework 4.8 47.236 ms 0.6410 ms 0.5996 ms 2.19 0.03 5181.8182 - 31.25 MB 2.04
ParsePressureUnit .NET Framework 4.8 .NET Framework 4.8 41.511 ms 0.3631 ms 0.3219 ms 1.92 0.02 4583.3333 - 27.62 MB 1.80
ParseVolumeFlowUnit .NET Framework 4.8 .NET Framework 4.8 66.566 ms 1.2644 ms 1.1827 ms 3.08 0.05 7875.0000 125.0000 47.4 MB 3.09

After:

Method Job Runtime Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
ParseMassUnit .NET 8.0 .NET 8.0 2.729 ms 0.0427 ms 0.0356 ms 1.00 0.02 472.6563 3.9063 7.58 MB 1.00
ParseVolumeUnit .NET 8.0 .NET 8.0 5.144 ms 0.1026 ms 0.2861 ms 1.89 0.11 765.6250 7.8125 12.27 MB 1.62
ParseDensityUnit .NET 8.0 .NET 8.0 5.304 ms 0.0211 ms 0.0197 ms 1.94 0.03 796.8750 7.8125 12.82 MB 1.69
ParsePressureUnit .NET 8.0 .NET 8.0 4.820 ms 0.0156 ms 0.0122 ms 1.77 0.02 726.5625 7.8125 11.64 MB 1.54
ParseVolumeFlowUnit .NET 8.0 .NET 8.0 7.703 ms 0.1002 ms 0.0836 ms 2.82 0.05 1406.2500 31.2500 22.54 MB 2.97
                       
ParseMassUnit .NET Framework 4.8 .NET Framework 4.8 7.085 ms 0.0185 ms 0.0173 ms 1.00 0.00 1390.6250 7.8125 8.36 MB 1.00
ParseVolumeUnit .NET Framework 4.8 .NET Framework 4.8 13.392 ms 0.0400 ms 0.0354 ms 1.89 0.01 2359.3750 15.6250 14.2 MB 1.70
ParseDensityUnit .NET Framework 4.8 .NET Framework 4.8 13.938 ms 0.1321 ms 0.1171 ms 1.97 0.02 2406.2500 15.6250 14.52 MB 1.74
ParsePressureUnit .NET Framework 4.8 .NET Framework 4.8 12.253 ms 0.1216 ms 0.1137 ms 1.73 0.02 2187.5000 15.6250 13.18 MB 1.58
ParseVolumeFlowUnit .NET Framework 4.8 .NET Framework 4.8 19.720 ms 0.2704 ms 0.2530 ms 2.78 0.04 4187.5000 93.7500 25.2 MB 3.02

@lipchev
Copy link
Collaborator Author

lipchev commented Jan 31, 2025

TryParseInvalidUnitBenchmarks.cs

Before:

Method Job Runtime Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
TryParseMassUnit .NET 8.0 .NET 8.0 7.250 ms 0.0933 ms 0.0827 ms 1.00 0.02 656.2500 - 10.51 MB 1.00
TryParseVolumeUnit .NET 8.0 .NET 8.0 15.267 ms 0.2171 ms 0.2031 ms 2.11 0.04 1109.3750 15.6250 17.92 MB 1.70
TryParseDensityUnit .NET 8.0 .NET 8.0 15.942 ms 0.1558 ms 0.1301 ms 2.20 0.03 1156.2500 - 18.57 MB 1.77
TryParsePressureUnit .NET 8.0 .NET 8.0 13.440 ms 0.0523 ms 0.0489 ms 1.85 0.02 1046.8750 - 16.79 MB 1.60
TryParseVolumeFlowUnit .NET 8.0 .NET 8.0 22.725 ms 0.3670 ms 0.3433 ms 3.13 0.06 1906.2500 31.2500 30.59 MB 2.91
                       
TryParseMassUnit .NET Framework 4.8 .NET Framework 4.8 22.157 ms 0.0661 ms 0.0618 ms 1.00 0.00 2593.7500 - 15.58 MB 1.00
TryParseVolumeUnit .NET Framework 4.8 .NET Framework 4.8 45.670 ms 0.3925 ms 0.3480 ms 2.06 0.02 4750.0000 - 28.9 MB 1.85
TryParseDensityUnit .NET Framework 4.8 .NET Framework 4.8 48.609 ms 0.0723 ms 0.0604 ms 2.19 0.01 5181.8182 - 31.49 MB 2.02
TryParsePressureUnit .NET Framework 4.8 .NET Framework 4.8 42.079 ms 0.1119 ms 0.1047 ms 1.90 0.01 4583.3333 - 27.85 MB 1.79
TryParseVolumeFlowUnit .NET Framework 4.8 .NET Framework 4.8 68.444 ms 0.1200 ms 0.0937 ms 3.09 0.01 7875.0000 125.0000 47.61 MB 3.06

After:

Method Job Runtime NbAbbreviations Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
TryParseMassUnit .NET 8.0 .NET 8.0 1000 3.116 ms 0.0327 ms 0.0273 ms 1.00 0.01 484.3750 3.9063 7.77 MB 1.00
TryParseVolumeUnit .NET 8.0 .NET 8.0 1000 5.424 ms 0.0640 ms 0.0629 ms 1.74 0.02 773.4375 7.8125 12.4 MB 1.60
TryParseDensityUnit .NET 8.0 .NET 8.0 1000 5.808 ms 0.1109 ms 0.1139 ms 1.86 0.04 812.5000 7.8125 13.01 MB 1.68
TryParsePressureUnit .NET 8.0 .NET 8.0 1000 5.175 ms 0.0920 ms 0.0816 ms 1.66 0.03 734.3750 7.8125 11.81 MB 1.52
TryParseVolumeFlowUnit .NET 8.0 .NET 8.0 1000 8.070 ms 0.1596 ms 0.2437 ms 2.59 0.08 1421.8750 31.2500 22.69 MB 2.92
                         
TryParseMassUnit .NET Framework 4.8 .NET Framework 4.8 1000 7.812 ms 0.0085 ms 0.0071 ms 1.00 0.00 1429.6875 7.8125 8.6 MB 1.00
TryParseVolumeUnit .NET Framework 4.8 .NET Framework 4.8 1000 14.426 ms 0.1973 ms 0.1846 ms 1.85 0.02 2390.6250 15.6250 14.38 MB 1.67
TryParseDensityUnit .NET Framework 4.8 .NET Framework 4.8 1000 14.768 ms 0.0424 ms 0.0354 ms 1.89 0.00 2453.1250 15.6250 14.77 MB 1.72
TryParsePressureUnit .NET Framework 4.8 .NET Framework 4.8 1000 13.276 ms 0.0195 ms 0.0182 ms 1.70 0.00 2234.3750 15.6250 13.41 MB 1.56
TryParseVolumeFlowUnit .NET Framework 4.8 .NET Framework 4.8 1000 20.428 ms 0.0411 ms 0.0343 ms 2.62 0.00 4218.7500 93.7500 25.41 MB 2.95

Eh, yeah - I've made the number of iterations a constant 1000 but didn't bother to update the result (I used to think it would make sense to provide the number of iterations in the report).

…r with IReadOnlyCollection<QuantityInfo>

- UnitsNetSetup: replaced the ICollection<QuantityInfo> parameter with IReadOnlyCollection<QuantityInfo>
- UnitAbbreviationsCache: introduced a constructor with a list of quantities, and improve the comments of the other constructors
- added tests and benchmarks covering the UnitAbbreviations initializations
Copy link
Collaborator Author

@lipchev lipchev Jan 31, 2025

Choose a reason for hiding this comment

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

Before (without the new constructor):

Method Job Runtime Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Default .NET 8.0 .NET 8.0 107,378.0 ns 1,866.42 ns 1,745.85 ns 1.000 0.02 9.0332 2.1973 148.21 KB 1.00
EmptyWithCustomMapping .NET 8.0 .NET 8.0 623.6 ns 12.26 ns 15.94 ns 0.006 0.00 0.1793 0.0010 2.94 KB 0.02
DefaultWithoutLookup .NET 8.0 .NET 8.0 4,326.9 ns 75.43 ns 66.87 ns 0.040 0.00 0.4959 - 8.16 KB 0.06
EmptyWithoutLookup .NET 8.0 .NET 8.0 3,225.0 ns 61.52 ns 57.55 ns 0.030 0.00 0.3586 - 5.94 KB 0.04
                       
Default .NET Framework 4.8 .NET Framework 4.8 166,492.9 ns 2,520.02 ns 2,357.22 ns 1.00 0.02 25.3906 5.6152 156.56 KB 1.00
EmptyWithCustomMapping .NET Framework 4.8 .NET Framework 4.8 2,189.2 ns 27.21 ns 25.45 ns 0.01 0.00 0.5646 0.0038 3.48 KB 0.02
DefaultWithoutLookup .NET Framework 4.8 .NET Framework 4.8 12,468.0 ns 134.59 ns 125.90 ns 0.07 0.00 1.9379 0.0153 11.97 KB 0.08
EmptyWithoutLookup .NET Framework 4.8 .NET Framework 4.8 10,682.9 ns 92.34 ns 81.86 ns 0.06 0.00 1.4343 0.0153 8.89 KB 0.06

After:

Method Job Runtime Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Default .NET 8.0 .NET 8.0 48,822.3 ns 478.55 ns 424.22 ns 1.000 0.01 8.4229 1.8311 138.32 KB 1.00
EmptyWithCustomMapping .NET 8.0 .NET 8.0 466.2 ns 9.33 ns 16.10 ns 0.010 0.00 0.1945 0.0019 3.18 KB 0.02
WithSpecificQuantity .NET 8.0 .NET 8.0 3,787.3 ns 27.75 ns 25.96 ns 0.078 0.00 0.5188 - 8.61 KB 0.06
WithSpecificQuantityAndCustomMapping .NET 8.0 .NET 8.0 4,086.7 ns 80.18 ns 92.33 ns 0.084 0.00 0.5493 - 8.98 KB 0.06
DefaultWithoutLookup .NET 8.0 .NET 8.0 3,175.6 ns 61.39 ns 75.39 ns 0.065 0.00 0.3586 - 5.94 KB 0.04
EmptyWithoutLookup .NET 8.0 .NET 8.0 3,269.1 ns 62.13 ns 63.81 ns 0.067 0.00 0.3586 - 5.94 KB 0.04
WithSpecificQuantityWithoutLookup .NET 8.0 .NET 8.0 3,223.6 ns 63.93 ns 62.79 ns 0.066 0.00 0.3586 - 5.96 KB 0.04
                       
Default .NET Framework 4.8 .NET Framework 4.8 73,787.6 ns 1,266.97 ns 1,185.13 ns 1.00 0.02 22.9492 4.5166 141.48 KB 1.00
EmptyWithCustomMapping .NET Framework 4.8 .NET Framework 4.8 1,146.8 ns 11.71 ns 10.38 ns 0.02 0.00 0.5398 0.0038 3.32 KB 0.02
WithSpecificQuantity .NET Framework 4.8 .NET Framework 4.8 11,476.0 ns 118.92 ns 111.24 ns 0.16 0.00 1.8768 0.0305 11.57 KB 0.08
WithSpecificQuantityAndCustomMapping .NET Framework 4.8 .NET Framework 4.8 11,915.2 ns 136.68 ns 127.85 ns 0.16 0.00 1.9379 0.0305 11.95 KB 0.08
DefaultWithoutLookup .NET Framework 4.8 .NET Framework 4.8 10,557.8 ns 133.17 ns 124.57 ns 0.14 0.00 1.4343 0.0153 8.84 KB 0.06
EmptyWithoutLookup .NET Framework 4.8 .NET Framework 4.8 10,369.5 ns 52.83 ns 41.24 ns 0.14 0.00 1.4343 0.0153 8.84 KB 0.06
WithSpecificQuantityWithoutLookup .NET Framework 4.8 .NET Framework 4.8 10,373.9 ns 86.97 ns 81.35 ns 0.14 0.00 1.4343 0.0153 8.86 KB 0.06

…sing backing fields)

- added some benchmarks for the Enum/UnitKey
- updated some typos for the TryParseUnitBenchmarks and introduced an explict Culture for the Parse/TryParse unit benchmarks
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I did a before and after 6b4d05a

Before:

Method Job Runtime Mean Error StdDev Ratio
EqualsRecord .NET 8.0 .NET 8.0 186.6 ns 0.60 ns 0.53 ns 1.00
OperatorEqualsRecord .NET 8.0 .NET 8.0 186.1 ns 0.57 ns 0.54 ns 1.00
OperatorEqualsManual .NET 8.0 .NET 8.0 329.6 ns 1.68 ns 1.57 ns 1.77
             
EqualsRecord .NET Framework 4.8 .NET Framework 4.8 5,670.3 ns 11.10 ns 9.84 ns 1.00
OperatorEqualsRecord .NET Framework 4.8 .NET Framework 4.8 5,862.4 ns 26.46 ns 24.75 ns 1.03
OperatorEqualsManual .NET Framework 4.8 .NET Framework 4.8 1,321.3 ns 4.87 ns 4.32 ns 0.23

After:

Method Job Runtime Mean Error StdDev Ratio
EqualsRecord .NET 8.0 .NET 8.0 186.6 ns 0.27 ns 0.24 ns 1.00
OperatorEqualsRecord .NET 8.0 .NET 8.0 186.6 ns 0.24 ns 0.20 ns 1.00
OperatorEqualsManual .NET 8.0 .NET 8.0 328.6 ns 1.11 ns 1.04 ns 1.76
             
EqualsRecord .NET Framework 4.8 .NET Framework 4.8 1,325.5 ns 3.55 ns 3.32 ns 1.00
OperatorEqualsRecord .NET Framework 4.8 .NET Framework 4.8 1,328.1 ns 3.81 ns 3.38 ns 1.00
OperatorEqualsManual .NET Framework 4.8 .NET Framework 4.8 1,135.7 ns 1.84 ns 1.63 ns 0.86

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 6b4d05a :

Method Job Runtime Mean Error StdDev Ratio
GetHashCodeRecord .NET 8.0 .NET 8.0 1.367 μs 0.0025 μs 0.0023 μs 1.00
GetCustomHashCode .NET 8.0 .NET 8.0 2.369 μs 0.0033 μs 0.0028 μs 1.73
GetCustomHashCodeUnchecked .NET 8.0 .NET 8.0 1.314 μs 0.0027 μs 0.0024 μs 0.96
             
GetHashCodeRecord .NET Framework 4.8 .NET Framework 4.8 6.964 μs 0.0096 μs 0.0085 μs 1.00
GetCustomHashCode .NET Framework 4.8 .NET Framework 4.8 1.512 μs 0.0032 μs 0.0030 μs 0.22
GetCustomHashCodeUnchecked .NET Framework 4.8 .NET Framework 4.8 1.696 μs 0.0029 μs 0.0027 μs 0.24

After 6b4d05a :

Method Job Runtime Mean Error StdDev Ratio
GetHashCodeRecord .NET 8.0 .NET 8.0 1.312 μs 0.0019 μs 0.0017 μs 1.00
GetCustomHashCode .NET 8.0 .NET 8.0 2.371 μs 0.0059 μs 0.0052 μs 1.81
GetCustomHashCodeUnchecked .NET 8.0 .NET 8.0 1.321 μs 0.0044 μs 0.0041 μs 1.01
             
GetHashCodeRecord .NET Framework 4.8 .NET Framework 4.8 2.450 μs 0.0039 μs 0.0032 μs 1.00
GetCustomHashCode .NET Framework 4.8 .NET Framework 4.8 1.513 μs 0.0030 μs 0.0026 μs 0.62
GetCustomHashCodeUnchecked .NET Framework 4.8 .NET Framework 4.8 1.516 μs 0.0014 μs 0.0013 μs 0.62

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 6b4d05a :

Method Job Runtime Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ManualCast .NET 8.0 .NET 8.0 99.73 ns 0.876 ns 0.777 ns 1.00 0.01 - - NA
ExplicitCast .NET 8.0 .NET 8.0 17,091.84 ns 207.027 ns 193.653 ns 171.39 2.27 0.7019 12000 B NA
ExplicitCastBoxed .NET 8.0 .NET 8.0 14,396.40 ns 151.136 ns 141.373 ns 144.36 1.75 1.4343 24000 B NA
ToUnit .NET 8.0 .NET 8.0 100.33 ns 1.660 ns 1.553 ns 1.01 0.02 - - NA
                     
ManualCast .NET Framework 4.8 .NET Framework 4.8 97.67 ns 1.226 ns 1.147 ns 1.00 0.02 - - NA
ExplicitCast .NET Framework 4.8 .NET Framework 4.8 23,538.91 ns 190.287 ns 148.563 ns 241.03 3.11 1.8921 12035 B NA
ExplicitCastBoxed .NET Framework 4.8 .NET Framework 4.8 25,713.98 ns 227.592 ns 212.890 ns 263.30 3.66 3.8147 24071 B NA
ToUnit .NET Framework 4.8 .NET Framework 4.8 1,412.97 ns 6.864 ns 6.420 ns 14.47 0.18 - - NA

After 6b4d05a :

Method Job Runtime Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ManualCast .NET 8.0 .NET 8.0 99.12 ns 0.232 ns 0.206 ns 1.00 0.00 - - NA
ExplicitCast .NET 8.0 .NET 8.0 12,441.49 ns 30.149 ns 26.726 ns 125.51 0.36 0.7172 12000 B NA
ExplicitCastBoxed .NET 8.0 .NET 8.0 14,356.90 ns 128.752 ns 120.435 ns 144.84 1.21 1.4343 24000 B NA
ToUnit .NET 8.0 .NET 8.0 98.93 ns 0.209 ns 0.185 ns 1.00 0.00 - - NA
                     
ManualCast .NET Framework 4.8 .NET Framework 4.8 95.62 ns 0.076 ns 0.059 ns 1.00 0.00 - - NA
ExplicitCast .NET Framework 4.8 .NET Framework 4.8 22,978.83 ns 105.611 ns 98.789 ns 240.32 1.01 1.8921 12035 B NA
ExplicitCastBoxed .NET Framework 4.8 .NET Framework 4.8 24,999.50 ns 156.120 ns 146.035 ns 261.46 1.49 3.8147 24071 B NA
ToUnit .NET Framework 4.8 .NET Framework 4.8 1,037.09 ns 1.759 ns 1.560 ns 10.85 0.02 - - NA

…d the _quantitiesByName into a regular (lazy-loaded) dictionary

- UnitsNetSetup and UnitAbbreviationsCache constructors changed from IReadOnlyCollection<QuantityInfo> into IEnumerable<QuantityInfo>
- changed the Quantity.Infos from IReadOnlyCollection to IReadOnlyList
@lipchev lipchev requested a review from angularsen February 7, 2025 17:09
@lipchev
Copy link
Collaborator Author

lipchev commented Feb 7, 2025

@angularsen I think this is ready, I don't have anything more to add for the time being.

There is of course a Part II and Part III of this- my other project is still several times faster on these... 🚀

@angularsen
Copy link
Owner

Oh boy, I'll need some time to just read through this 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants