-
Notifications
You must be signed in to change notification settings - Fork 391
QuantityInfo
: internalizing the UnitInfo
construction
#1555
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
base: master
Are you sure you want to change the base?
Conversation
- QuantityInfo: introducing a delegate for constructing the quantity (required only for net standard) - QuantityInfo: introducing an optional ResourceDictionary - UnitInfo: introducing a back-reference to the QuantityInfo (making the QuantityName [Obsolete]) - IQuantity: added the QuantityInfo<TQuantity, TUnit>, the From(double, TUnit) method and default implementations for the non-generic properties - UnitAbbreviationsCache: ReadAbbreviationsFromResourceFile implemented using the provided ResourceDictionary (if available) - updating the QuantityInfo definitions for all quantities (introducing a concrete class, such as MassInfo) with helpers for creating a derried configuration - HowMuch upgraded to IQuantity<HowMuch, HowMuchUnit> (the original QuantityInfo is now abstract) - UnitsNet.csproj / UnitsNet.Tests.csproj: added some (specific) implicit usings
…e methods for dynamically adding a UnitInfo
QuantityInfo
: internalizing the UnitInfo
construction (WIP)QuantityInfo
: internalizing the UnitInfo
construction
@angularsen The most breaking thing here should be the change from Providing a delegate and resource dictionary in the PS The PS2 The new size is:
which is up from
but that would go down again, when I remove all the
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's how caching the ResourceManager
instance affects the cold start of the UnitAbbreviationsCache
(note that this affects the performance for every call to ReadAbbreviationsFromResourceFile
, which happens for every unit and every culture):
Before:
Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|---|---|---|---|---|
Default | .NET 9.0 | .NET 9.0 | 3.233 μs | 0.0755 μs | 0.2130 μs | 1.00 | 0.09 | 0.2594 | - | 4.28 KB | 1.00 |
EmptyWithCustomMapping | .NET 9.0 | .NET 9.0 | 3.050 μs | 0.0250 μs | 0.0234 μs | 0.95 | 0.06 | 0.2747 | - | 4.53 KB | 1.06 |
WithSpecificQuantity | .NET 9.0 | .NET 9.0 | 3.743 μs | 0.0410 μs | 0.0383 μs | 1.16 | 0.08 | 0.4501 | - | 7.44 KB | 1.74 |
WithSpecificQuantityAndCustomMapping | .NET 9.0 | .NET 9.0 | 3.767 μs | 0.0521 μs | 0.0487 μs | 1.17 | 0.08 | 0.4578 | - | 7.69 KB | 1.80 |
Default | .NET Framework 4.8 | .NET Framework 4.8 | 10.512 μs | 0.0382 μs | 0.0319 μs | 1.00 | 0.00 | 1.3580 | 0.0153 | 8.36 KB | 1.00 |
EmptyWithCustomMapping | .NET Framework 4.8 | .NET Framework 4.8 | 10.837 μs | 0.0324 μs | 0.0271 μs | 1.03 | 0.00 | 1.3885 | 0.0153 | 8.56 KB | 1.02 |
WithSpecificQuantity | .NET Framework 4.8 | .NET Framework 4.8 | 11.704 μs | 0.1035 μs | 0.0968 μs | 1.11 | 0.01 | 1.8768 | 0.0305 | 11.54 KB | 1.38 |
WithSpecificQuantityAndCustomMapping | .NET Framework 4.8 | .NET Framework 4.8 | 11.719 μs | 0.0276 μs | 0.0258 μs | 1.11 | 0.00 | 1.9073 | 0.0305 | 11.74 KB | 1.40 |
After:
Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|---|---|---|---|---|
Default | .NET 9.0 | .NET 9.0 | 194.2 ns | 2.92 ns | 2.73 ns | 1.00 | 0.02 | 0.0904 | 0.0005 | 1.48 KB | 1.00 |
EmptyWithCustomMapping | .NET 9.0 | .NET 9.0 | 248.0 ns | 2.78 ns | 2.32 ns | 1.28 | 0.02 | 0.1054 | 0.0005 | 1.73 KB | 1.17 |
WithSpecificQuantity | .NET 9.0 | .NET 9.0 | 1,495.2 ns | 25.54 ns | 23.89 ns | 7.70 | 0.16 | 0.4501 | 0.0076 | 7.36 KB | 4.98 |
WithSpecificQuantityAndCustomMapping | .NET 9.0 | .NET 9.0 | 1,627.0 ns | 19.65 ns | 18.38 ns | 8.38 | 0.15 | 0.4654 | 0.0076 | 7.61 KB | 5.15 |
Default | .NET Framework 4.8 | .NET Framework 4.8 | 385.5 ns | 1.09 ns | 0.97 ns | 1.00 | 0.00 | 0.2728 | 0.0014 | 1.68 KB | 1.00 |
EmptyWithCustomMapping | .NET Framework 4.8 | .NET Framework 4.8 | 500.3 ns | 0.84 ns | 0.79 ns | 1.30 | 0.00 | 0.3042 | 0.0010 | 1.87 KB | 1.12 |
WithSpecificQuantity | .NET Framework 4.8 | .NET Framework 4.8 | 1,342.3 ns | 5.49 ns | 5.13 ns | 3.48 | 0.02 | 0.8106 | 0.0095 | 4.99 KB | 2.98 |
WithSpecificQuantityAndCustomMapping | .NET Framework 4.8 | .NET Framework 4.8 | 1,432.3 ns | 10.34 ns | 9.67 ns | 3.72 | 0.03 | 0.8430 | 0.0114 | 5.19 KB | 3.09 |
There is of course some added cost to the constructor of the QuantityInfo
(I've got a benchmark which I think I forgot to add), but that's just a one time cost (per selected quantity) which is generally incurred on start instead of on the first request for a given unit and culture.
My initial thought was to use some sort of a struct representing the Tuple<Assembly, string> (that is required for the ReadAbbreviationsFromResourceFile
to work with external units), but ultimately decided against it. Here's what this looks like in the sample project:
public static readonly QuantityInfo<HowMuch, HowMuchUnit> Info = new(
HowMuchUnit.Some,
new UnitDefinition<HowMuchUnit>[]
{
new(HowMuchUnit.Some, "Some", BaseUnits.Undefined),
new(HowMuchUnit.ATon, "Tons", new BaseUnits(mass: MassUnit.Tonne), QuantityValue.FromTerms(1, 10)),
new(HowMuchUnit.AShitTon, "ShitTons", BaseUnits.Undefined, QuantityValue.FromTerms(1, 100))
},
new BaseDimensions(0, 1, 0, 0, 0, 0, 0),
// providing a resource manager for the unit abbreviations (optional)
Properties.CustomQuantities_HowMuch.ResourceManager);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the before (still using Enum.GetValues(typeof(MassUnit)).Cast<MassUnit>().ToArray()
on both targets):
Method | Job | Runtime | NbIterations | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|---|---|---|---|---|---|
CreateDefaultMass | .NET 9.0 | .NET 9.0 | 1000 | 804.5 μs | 16.03 μs | 27.22 μs | 1.00 | 0.05 | 351.5625 | 6.8359 | 5.62 MB | 1.00 |
CreateDefaultVolume | .NET 9.0 | .NET 9.0 | 1000 | 1,384.4 μs | 21.80 μs | 18.21 μs | 1.72 | 0.06 | 494.1406 | 13.6719 | 7.9 MB | 1.41 |
CreateDefaultVolumeFlow | .NET 9.0 | .NET 9.0 | 1000 | 1,938.2 μs | 36.43 μs | 35.78 μs | 2.41 | 0.09 | 634.7656 | 25.3906 | 10.15 MB | 1.81 |
CreateDefaultMass | .NET Framework 4.8 | .NET Framework 4.8 | 1000 | 7,751.9 μs | 59.94 μs | 56.07 μs | 1.00 | 0.01 | 1250.0000 | 15.6250 | 7.56 MB | 1.00 |
CreateDefaultVolume | .NET Framework 4.8 | .NET Framework 4.8 | 1000 | 14,789.7 μs | 85.69 μs | 80.16 μs | 1.91 | 0.02 | 1937.5000 | 46.8750 | 11.71 MB | 1.55 |
CreateDefaultVolumeFlow | .NET Framework 4.8 | .NET Framework 4.8 | 1000 | 20,617.8 μs | 94.10 μs | 88.02 μs | 2.66 | 0.02 | 2562.5000 | 93.7500 | 15.42 MB | 2.04 |
After:
Method | Job | Runtime | NbIterations | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|---|---|---|---|---|---|
CreateDefaultMass | .NET 9.0 | .NET 9.0 | 1000 | 2.351 ms | 0.0185 ms | 0.0173 ms | 1.00 | 0.01 | 429.6875 | 7.8125 | 6.86 MB | 1.00 |
CreateDefaultVolume | .NET 9.0 | .NET 9.0 | 1000 | 3.175 ms | 0.0333 ms | 0.0311 ms | 1.35 | 0.02 | 609.3750 | - | 9.86 MB | 1.44 |
CreateDefaultVolumeFlow | .NET 9.0 | .NET 9.0 | 1000 | 3.750 ms | 0.0368 ms | 0.0326 ms | 1.60 | 0.02 | 789.0625 | 11.7188 | 12.63 MB | 1.84 |
CreateDefaultMass | .NET Framework 4.8 | .NET Framework 4.8 | 1000 | 2.578 ms | 0.0153 ms | 0.0143 ms | 1.00 | 0.01 | 1265.6250 | 27.3438 | 7.6 MB | 1.00 |
CreateDefaultVolume | .NET Framework 4.8 | .NET Framework 4.8 | 1000 | 3.652 ms | 0.0270 ms | 0.0253 ms | 1.42 | 0.01 | 2035.1563 | 62.5000 | 12.24 MB | 1.61 |
CreateDefaultVolumeFlow | .NET Framework 4.8 | .NET Framework 4.8 | 1000 | 4.486 ms | 0.0216 ms | 0.0202 ms | 1.74 | 0.01 | 2367.1875 | 78.1250 | 14.21 MB | 1.87 |
This is quite remarkable really, just for the fact that for the first time we've got a benchmark in which .NET Framework 4.8
is neck-a-neck with .NET 9.0
🤣
/// <summary> | ||
/// Creates a new instance of the <see cref="MassInfo"/> class with the default settings for the Mass quantity and a callback for customizing the default unit mappings. | ||
/// </summary> | ||
/// <param name="customizeUnits"> | ||
/// A callback function for customizing the default unit mappings. | ||
/// </param> | ||
/// <returns> | ||
/// A new instance of the <see cref="MassInfo"/> class with the default settings. | ||
/// </returns> | ||
public static MassInfo CreateDefault(Func<IEnumerable<UnitDefinition<MassUnit>>, IEnumerable<IUnitDefinition<MassUnit>>> customizeUnits) | ||
{ | ||
return new MassInfo(nameof(Mass), DefaultBaseUnit, customizeUnits(GetDefaultMappings()), new Mass(0, DefaultBaseUnit), DefaultBaseDimensions); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you might be wondering about this thing here (currently uncovered by tests). It allows us to include, exclude or re-configure certain units (not very useful In the current version, where the conversion expressions are still not available).
Here's how this is covered in #1544 : MassTestsBase.g.cs.
The problem is that this it uses these extensions, (tested here) which, I just realized, are using two generic type parameters, making them susceptible to the bug with ReSharper.
If you think we can Ignore and Continue (and you agree with the rest of the changes so far), then I can add them here.
PS Note that in #1544 I went to even further and (with the use of some dazzling re-directions) was able to make the default Mass.Info
configurable from the UnitsNetSetup.DefaultConfigurationBuilder
, but that's still ahead of us (should you decide to take the red pill 🤣 ).
@lipchev just a heads up that this week is crazy for me and I'm traveling this weekend, not sure I'll get around to much |
QuantityInfo
: internalizing theUnitInfo
constructionQuantityInfo
: introducing a delegate for constructing the quantity (required only for net standard)QuantityInfo
: introducing an optionalResourceDictionary
QuantityInfo
: replacing theTUnit[]
with anIReadOnlyCollection<TUnit>
UnitInfo
: introducing a back-reference to theQuantityInfo
(making theQuantityName
[Obsolete]
)IQuantity
: added theQuantityInfo<TQuantity, TUnit>
, theFrom(double, TUnit)
method and default implementations for the non-generic propertiesQuantityInfoLookup
: added another collection for the quantity by type mapping (replacing the generated code inQuantity.g.s
).UnitAbbreviationsCache
:ReadAbbreviationsFromResourceFile
implemented using the providedResourceManager
(if available)QuantityInfo
definitions for all quantities (introducing a concrete class, such asMassInfo
) with helpers for creating a derived configurationHowMuch
upgraded toIQuantity<HowMuch, HowMuchUnit>
(the originalQuantityInfo
is now abstract)Quantity
refactored theParse
/From*
methods using the defaultQuantityParser
/QuantityInfoLookup
Quantity
replaced theByName
dictionary with anIReadOnlyDictionary
UnitsNet.csproj
/UnitsNet.Tests.csproj
: added some (specific) implicit usings