Skip to content

Commit 1abfdea

Browse files
authored
⚡ Prefer built-in conversions over extensibility for perf (#1049)
Co-authored-by: Andreas Gullberg Larsen <[email protected]> Fixes #1069 Per @lipchev's comments in #1023, I decided to add the local conversion method back (as private ```TryToUnit```). This will allow better performance for auto-generated conversions before falling back to the extensibility points.
1 parent ee058dc commit 1abfdea

File tree

118 files changed

+10738
-5779
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+10738
-5779
lines changed

CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs

Lines changed: 95 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -364,36 +364,37 @@ private void GenerateStaticMethods()
364364
/// <param name=""unitConverter"">The <see cref=""UnitConverter""/> to register the default conversion functions in.</param>
365365
internal static void RegisterDefaultConversions(UnitConverter unitConverter)
366366
{{
367-
// Register in unit converter: BaseUnit -> {_quantity.Name}Unit");
367+
// Register in unit converter: {_quantity.Name}Unit -> BaseUnit");
368368

369-
foreach(var unit in _quantity.Units)
370-
{
371-
if (unit.SingularName == _quantity.BaseUnit)
372-
continue;
369+
foreach(var unit in _quantity.Units)
370+
{
371+
if (unit.SingularName == _quantity.BaseUnit)
372+
continue;
373373

374-
var func = unit.FromBaseToUnitFunc.Replace("{x}", "quantity.Value");
375-
Writer.WL($@"
376-
unitConverter.SetConversionFunction<{_quantity.Name}>({_unitEnumName}.{_quantity.BaseUnit}, {_quantity.Name}Unit.{unit.SingularName}, quantity => new {_quantity.Name}({func}, {_quantity.Name}Unit.{unit.SingularName}));");
377-
}
378-
Writer.WL();
379-
Writer.WL($@"
374+
var func = unit.FromUnitToBaseFunc.Replace("{x}", "quantity.Value");
375+
Writer.WL($@"
376+
unitConverter.SetConversionFunction<{_quantity.Name}>({_quantity.Name}Unit.{unit.SingularName}, {_unitEnumName}.{_quantity.BaseUnit}, quantity => quantity.ToUnit({_unitEnumName}.{_quantity.BaseUnit}));");
377+
}
378+
379+
Writer.WL();
380+
Writer.WL($@"
380381
381382
// Register in unit converter: BaseUnit <-> BaseUnit
382383
unitConverter.SetConversionFunction<{_quantity.Name}>({_unitEnumName}.{_quantity.BaseUnit}, {_unitEnumName}.{_quantity.BaseUnit}, quantity => quantity);
383384
384-
// Register in unit converter: {_quantity.Name}Unit -> BaseUnit");
385+
// Register in unit converter: BaseUnit -> {_quantity.Name}Unit");
385386

386-
foreach(var unit in _quantity.Units)
387-
{
388-
if (unit.SingularName == _quantity.BaseUnit)
389-
continue;
387+
foreach(var unit in _quantity.Units)
388+
{
389+
if (unit.SingularName == _quantity.BaseUnit)
390+
continue;
390391

391-
var func = unit.FromUnitToBaseFunc.Replace("{x}", "quantity.Value");
392-
Writer.WL($@"
393-
unitConverter.SetConversionFunction<{_quantity.Name}>({_quantity.Name}Unit.{unit.SingularName}, {_unitEnumName}.{_quantity.BaseUnit}, quantity => new {_quantity.Name}({func}, {_unitEnumName}.{_quantity.BaseUnit}));");
394-
}
392+
var func = unit.FromBaseToUnitFunc.Replace("{x}", "quantity.Value");
393+
Writer.WL($@"
394+
unitConverter.SetConversionFunction<{_quantity.Name}>({_unitEnumName}.{_quantity.BaseUnit}, {_quantity.Name}Unit.{unit.SingularName}, quantity => quantity.ToUnit({_quantity.Name}Unit.{unit.SingularName}));");
395+
}
395396

396-
Writer.WL($@"
397+
Writer.WL($@"
397398
}}
398399
399400
internal static void MapGeneratedLocalizations(UnitAbbreviationsCache unitAbbreviationsCache)
@@ -655,13 +656,13 @@ private void GenerateArithmeticOperators()
655656
/// <summary>Get <see cref=""{_quantity.Name}""/> from adding two <see cref=""{_quantity.Name}""/>.</summary>
656657
public static {_quantity.Name} operator +({_quantity.Name} left, {_quantity.Name} right)
657658
{{
658-
return new {_quantity.Name}(left.Value + right.GetValueAs(left.Unit), left.Unit);
659+
return new {_quantity.Name}(left.Value + right.ToUnit(left.Unit).Value, left.Unit);
659660
}}
660661
661662
/// <summary>Get <see cref=""{_quantity.Name}""/> from subtracting two <see cref=""{_quantity.Name}""/>.</summary>
662663
public static {_quantity.Name} operator -({_quantity.Name} left, {_quantity.Name} right)
663664
{{
664-
return new {_quantity.Name}(left.Value - right.GetValueAs(left.Unit), left.Unit);
665+
return new {_quantity.Name}(left.Value - right.ToUnit(left.Unit).Value, left.Unit);
665666
}}
666667
667668
/// <summary>Get <see cref=""{_quantity.Name}""/> from multiplying value and <see cref=""{_quantity.Name}""/>.</summary>
@@ -711,15 +712,15 @@ private void GenerateLogarithmicArithmeticOperators()
711712
{{
712713
// Logarithmic addition
713714
// Formula: {x} * log10(10^(x/{x}) + 10^(y/{x}))
714-
return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, left.Value/{x}) + Math.Pow(10, right.GetValueAs(left.Unit)/{x})), left.Unit);
715+
return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, left.Value / {x}) + Math.Pow(10, right.ToUnit(left.Unit).Value / {x})), left.Unit);
715716
}}
716717
717718
/// <summary>Get <see cref=""{_quantity.Name}""/> from logarithmic subtraction of two <see cref=""{_quantity.Name}""/>.</summary>
718719
public static {_quantity.Name} operator -({_quantity.Name} left, {_quantity.Name} right)
719720
{{
720721
// Logarithmic subtraction
721722
// Formula: {x} * log10(10^(x/{x}) - 10^(y/{x}))
722-
return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, left.Value/{x}) - Math.Pow(10, right.GetValueAs(left.Unit)/{x})), left.Unit);
723+
return new {_quantity.Name}({x} * Math.Log10(Math.Pow(10, left.Value / {x}) - Math.Pow(10, right.ToUnit(left.Unit).Value / {x})), left.Unit);
723724
}}
724725
725726
/// <summary>Get <see cref=""{_quantity.Name}""/> from logarithmic multiplication of value and <see cref=""{_quantity.Name}""/>.</summary>
@@ -747,11 +748,11 @@ private void GenerateLogarithmicArithmeticOperators()
747748
public static double operator /({_quantity.Name} left, {_quantity.Name} right)
748749
{{
749750
// Logarithmic division = subtraction
750-
return Convert.ToDouble(left.Value - right.GetValueAs(left.Unit));
751+
return Convert.ToDouble(left.Value - right.ToUnit(left.Unit).Value);
751752
}}
752753
753754
#endregion
754-
");
755+
" );
755756
}
756757

757758
private void GenerateEqualityAndComparison()
@@ -762,25 +763,25 @@ private void GenerateEqualityAndComparison()
762763
/// <summary>Returns true if less or equal to.</summary>
763764
public static bool operator <=({_quantity.Name} left, {_quantity.Name} right)
764765
{{
765-
return left.Value <= right.GetValueAs(left.Unit);
766+
return left.Value <= right.ToUnit(left.Unit).Value;
766767
}}
767768
768769
/// <summary>Returns true if greater than or equal to.</summary>
769770
public static bool operator >=({_quantity.Name} left, {_quantity.Name} right)
770771
{{
771-
return left.Value >= right.GetValueAs(left.Unit);
772+
return left.Value >= right.ToUnit(left.Unit).Value;
772773
}}
773774
774775
/// <summary>Returns true if less than.</summary>
775776
public static bool operator <({_quantity.Name} left, {_quantity.Name} right)
776777
{{
777-
return left.Value < right.GetValueAs(left.Unit);
778+
return left.Value < right.ToUnit(left.Unit).Value;
778779
}}
779780
780781
/// <summary>Returns true if greater than.</summary>
781782
public static bool operator >({_quantity.Name} left, {_quantity.Name} right)
782783
{{
783-
return left.Value > right.GetValueAs(left.Unit);
784+
return left.Value > right.ToUnit(left.Unit).Value;
784785
}}
785786
786787
/// <summary>Returns true if exactly equal.</summary>
@@ -809,7 +810,7 @@ public int CompareTo(object obj)
809810
/// <inheritdoc />
810811
public int CompareTo({_quantity.Name} other)
811812
{{
812-
return _value.CompareTo(other.GetValueAs(this.Unit));
813+
return _value.CompareTo(other.ToUnit(this.Unit).Value);
813814
}}
814815
815816
/// <inheritdoc />
@@ -826,7 +827,7 @@ public override bool Equals(object obj)
826827
/// <remarks>Consider using <see cref=""Equals({_quantity.Name}, double, ComparisonType)""/> for safely comparing floating point values.</remarks>
827828
public bool Equals({_quantity.Name} other)
828829
{{
829-
return _value.Equals(other.GetValueAs(this.Unit));
830+
return _value.Equals(other.ToUnit(this.Unit).Value);
830831
}}
831832
832833
/// <summary>
@@ -905,10 +906,10 @@ private void GenerateConversionMethods()
905906
public double As({_unitEnumName} unit)
906907
{{
907908
if (Unit == unit)
908-
return Convert.ToDouble(Value);
909+
return (double)Value;
909910
910-
var converted = GetValueAs(unit);
911-
return Convert.ToDouble(converted);
911+
var converted = ToUnit(unit);
912+
return (double)converted.Value;
912913
}}
913914
914915
/// <inheritdoc cref=""IQuantity.As(UnitSystem)""/>
@@ -946,36 +947,86 @@ double IQuantity.As(Enum unit)
946947
}}
947948
948949
/// <summary>
949-
/// Converts this {_quantity.Name} to another {_quantity.Name} using the given <paramref name=""unitConverter""/> with the unit representation <paramref name=""unit"" />.
950+
/// Converts this <see cref=""{_quantity.Name}""/> to another <see cref=""{_quantity.Name}""/> using the given <paramref name=""unitConverter""/> with the unit representation <paramref name=""unit"" />.
950951
/// </summary>
951952
/// <param name=""unit"">The unit to convert to.</param>
952953
/// <param name=""unitConverter"">The <see cref=""UnitConverter""/> to use for the conversion.</param>
953954
/// <returns>A {_quantity.Name} with the specified unit.</returns>
954955
public {_quantity.Name} ToUnit({_unitEnumName} unit, UnitConverter unitConverter)
955956
{{
956-
if (Unit == unit)
957+
if (TryToUnit(unit, out var converted))
957958
{{
958-
// Already in requested units.
959-
return this;
959+
// Try to convert using the auto-generated conversion methods.
960+
return converted!.Value;
960961
}}
961962
else if (unitConverter.TryGetConversionFunction((typeof({_quantity.Name}), Unit, typeof({_quantity.Name}), unit), out var conversionFunction))
962963
{{
963-
// Direct conversion to requested unit found. Return the converted quantity.
964-
var converted = conversionFunction(this);
965-
return ({_quantity.Name})converted;
964+
// See if the unit converter has an extensibility conversion registered.
965+
return ({_quantity.Name})conversionFunction(this);
966966
}}
967967
else if (Unit != BaseUnit)
968968
{{
969-
// Direct conversion to requested unit NOT found. Convert to BaseUnit, and then from BaseUnit to requested unit.
969+
// Conversion to requested unit NOT found. Try to convert to BaseUnit, and then from BaseUnit to requested unit.
970970
var inBaseUnits = ToUnit(BaseUnit);
971971
return inBaseUnits.ToUnit(unit);
972972
}}
973973
else
974974
{{
975+
// No possible conversion
975976
throw new NotImplementedException($""Can not convert {{Unit}} to {{unit}}."");
976977
}}
977978
}}
978979
980+
/// <summary>
981+
/// Attempts to convert this <see cref=""{_quantity.Name}""/> to another <see cref=""{_quantity.Name}""/> with the unit representation <paramref name=""unit"" />.
982+
/// </summary>
983+
/// <param name=""unit"">The unit to convert to.</param>
984+
/// <param name=""converted"">The converted <see cref=""{_quantity.Name}""/> in <paramref name=""unit""/>, if successful.</param>
985+
/// <returns>True if successful, otherwise false.</returns>
986+
private bool TryToUnit({_quantity.Name}Unit unit, out {_quantity.Name}? converted)
987+
{{
988+
if (_unit == unit)
989+
{{
990+
converted = this;
991+
return true;
992+
}}
993+
994+
converted = (_unit, unit) switch
995+
{{
996+
// {_quantity.Name}Unit -> BaseUnit");
997+
998+
foreach(var unit in _quantity.Units)
999+
{
1000+
if (unit.SingularName == _quantity.BaseUnit)
1001+
continue;
1002+
1003+
var func = unit.FromUnitToBaseFunc.Replace("{x}", "_value");
1004+
Writer.WL($@"
1005+
({_quantity.Name}Unit.{unit.SingularName}, {_unitEnumName}.{_quantity.BaseUnit}) => new {_quantity.Name}({func}, {_unitEnumName}.{_quantity.BaseUnit}),");
1006+
}
1007+
1008+
Writer.WL();
1009+
Writer.WL($@"
1010+
1011+
// BaseUnit -> {_quantity.Name}Unit");
1012+
foreach(var unit in _quantity.Units)
1013+
{
1014+
if (unit.SingularName == _quantity.BaseUnit)
1015+
continue;
1016+
1017+
var func = unit.FromBaseToUnitFunc.Replace("{x}", "_value");
1018+
Writer.WL($@"
1019+
({_unitEnumName}.{_quantity.BaseUnit}, {_quantity.Name}Unit.{unit.SingularName}) => new {_quantity.Name}({func}, {_quantity.Name}Unit.{unit.SingularName}),");
1020+
}
1021+
1022+
Writer.WL();
1023+
Writer.WL($@"
1024+
_ => null!
1025+
}};
1026+
1027+
return converted != null;
1028+
}}
1029+
9791030
/// <inheritdoc />
9801031
IQuantity IQuantity.ToUnit(Enum unit)
9811032
{{
@@ -1009,12 +1060,6 @@ IQuantity IQuantity.ToUnit(Enum unit)
10091060
/// <inheritdoc />
10101061
IQuantity<{_unitEnumName}> IQuantity<{_unitEnumName}>.ToUnit(UnitSystem unitSystem) => ToUnit(unitSystem);
10111062
1012-
private {_valueType} GetValueAs({_unitEnumName} unit)
1013-
{{
1014-
var converted = ToUnit(unit);
1015-
return ({_valueType})converted.Value;
1016-
}}
1017-
10181063
#endregion
10191064
");
10201065
}

UnitsNet/CustomCode/Quantities/AmountOfSubstance.extra.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public partial struct AmountOfSubstance
3030
/// <returns>The number of particles (atoms or molecules) in this amount of substance.</returns>
3131
public double NumberOfParticles()
3232
{
33-
var moles = GetValueAs(AmountOfSubstanceUnit.Mole);
34-
return AvogadroConstant * moles;
33+
var moles = ToUnit(AmountOfSubstanceUnit.Mole);
34+
return AvogadroConstant * moles.Value;
3535
}
3636

3737

0 commit comments

Comments
 (0)