Skip to content

Proposal: Reducing size of library (WIP) #372

Closed
@angularsen

Description

@angularsen

NOTE: This is work in progress and early thoughts, but input is most welcome.

Motivation

UnitsNet nuget is nearing the 1MB mark and this is probably a lot bigger than most consumers would expect, since most users only work with a small set of quantities and units.

Proposal 1 - Core nuget + quantity nugets + meta nugets

This tries to achieve a kind of extension model, where new quantities are added via nugets - potentially 3rd party quantities.

  • Move all shared code into UnitsNet.Core (UnitSystem, UnitConverter, QuantityParser, UnitFormatter, QuantityValue etc..)
  • Repurpose our current UnitsNet nuget to a meta package that brings in the most common units (think 80/20, this is what we want most people to use)
  • Add nugets for all quantities UnitsNet.Quantities.Length, UnitsNet.Quantities.Mass etc. (around 80 of these now), which has the Length struct type (Length.g.cs and Length.extra.cs files) and any related extension methods (NumberToLengthExtensions), with a dependency on UnitsNet.Core
  • Add metapackage nugets to bring in a set of quantities for an engineering/technical domain, such as UnitsNet.Meta.ElectricalEngineering, UnitsNet.Meta.AudioEngineering, UnitsNet.Meta.PetroleumEngineering, UnitsNet.Meta.ChemicalEngineering

Concerns:

  • Discoverability is hurt by moving code out into separate packages, so you need to browse packages to see what is possible unless you add a metapackage that brings in all or the most common quantities
  • A lot of nugets (80 for quantities plus a few meta packages to bring in groups of quantities)
  • Extension model with static types is tricky. How can we provide operator overloads such as Force = Pressure * Area, when the user may not have added all three nuget packages? Should there be a nuget package for the Force+Pressure+Area combination? Can we move operator overloads as well as interfaces and enums for all units to the core nuget, while keeping the quantity implementations and the bulk of the quantity code in separate nugets? Need to think of something clever here.

Proposal 2 - Use extension methods and builder pattern to reduce member count per quantity

Instead of this:

Length l = Length.FromMeters(1);
Length l = Length.FromKilometers(1);
Length l = Length.FromMillimeters(1);

We could do something like this:

Length l = Length.From.Meters(1);
Length l = Length.From.Kilo.Meters(1);
Length l = Length.From.Milli.Meters(1);

The idea is to move all the prefix code (milli, kilo, mega etc) to a base type #371 for reuse across quantities. We can achieve this by the builder pattern described above using inheritance or extension methods.

A similar approach could be made with conversion properties:

double mm = myLength.Milli.Meters; // Milli effectively multiplies the Meters result by 1e3

Concerns:

  • The syntax is awkward and not very readable
  • Code size reduction is only significant for quantities with a lot of prefixes
  • Prefixes is not always as simple as multiplying by 1e3 etc. We have 3 logarithmic quantities AmplitudeRatio, PowerRatio and Level where the arithmetic is entirely different.

Proposal 3 - Move number extension methods to separate nuget

I just realized we have a ton of number extension methods, and this part of the library is something I think many simply don't use and could make sense to make opt-in.

For every unit, we have an extension method like Length l = 1.cm(); and Mass m = 85.kg();. To support the four number types (int, long, double, decimal) we currently care about, we have 4 overloads of this method. And to support nullable like int? nullable = 10; Length? l = nullable.cm(); , we double that count. This means, for our 600+ units today there is now 8 x 600 extension methods. I would be very interested to see the diff in size when removing this, but I think it can be pretty significant since it seems it is mainly our high method/property count that causes the big size and not so much the amount of code inside each of them.

Update 2018-01-20:
I just tried a Release build locally before and after removing all the number extensions code. The difference is a STAGGERING 939 vs 475 kB ! I think we have found our low hanging fruit to reduce code size.

Besides adding this as a separate nuget, we can also consider using the QuantityValue type to implicitly cast from multiple number types per extension method, to avoid the method overload explosion.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions