Webel: MMA 13.0 Quantity performance issue (notebook version 2022-04-28 14:30 AEST)

IMPORTANT: This test suite now only uses For loops in timing tests, not Table,
(so any recommendation of QuantityArray for efficiency is not relevant).
IMPORTANT: This test deliberately does not use UnitConvert anywhere!
Case: Physics calculation without any use of Quantity
In the following the ‘SI’ everywhere is a hint to users to use values scaled to SI units:
In[]:=
distanceNoUnitsSI[vSI_Real,aSI_Real,tSI_Real]:=vSItSI+1/2aSItSI^2;
In[]:=
distanceNoUnitsSI[1.,2.,3.]
Out[]=
12.
The timing test deliberately using a For (not Table) so it is more like a heat exchanger calculation iteration:
In[]:=
test$distanceNoUnitsSI[n_]:=For[i=1,i<=n,i++,distanceNoUnitsSI[1.i,2.i,3.i]]
In[]:=
test$distanceNoUnitsSI[10000];//Timing
Out[]=
{0.028378,Null}
Case: Physics calculation with internal use of Quantity
Note how this relies on the units-aware system to figure out the correct units (length in m) of the return Quantity,
​only the input arguments need to ever invoke the Quantity[val,”unit”] method.
In[]:=
distanceWithUnits[v_,a_,t_]:=vt+1/2at^2;
In[]:=
distanceWithUnits[​​Quantity[1,"Meters"/"Seconds"],​​Quantity[2,"Meters"/"Seconds"^2],​​Quantity[3,"Seconds"]​​]
Out[]=
12
m
The timing test uses For so that it’s more like an actual heat exchanger calculation iteration.
Note that this test is inefficiently using the Quantity function as a constructor each time.
In[]:=
test$distanceWithUnits[n_]:=For[i=1,i<=n,i++,​​distanceWithUnits[​​Quantity[1.i,"Meters"/"Seconds"],​​Quantity[2.i,"Meters"/"Seconds"^2],​​Quantity[3.i,"Seconds"]​​]​​];
In[]:=
test$distanceWithUnits[10000];//Timing
Out[]=
{15.0911,Null}
Estimated performance hit factor using Quantity:
In[]:=
14.618174`/0.027599`
Out[]=
529.663
Case: Physics calculation with internal use of Quantity and with
pre-defined arguments and pre-multiplication of each Quantity
This test invokes with predefined Quantity items (typically, in the real application,
these would be the return values from other units-aware functions). Note that
this test version accepts pre-defined Quantity args then multiplies each of them by
i in the For loop so that distanceWithUnits is invoked with unique args each time.
test$distanceWithUnits2[n_,v_,a_,t_]:=For[i=1,i<=n,i++,​​distanceWithUnits[iv,ia,it]​​];
In[]:=
v1=Quantity[1.,"Meters"/"Seconds"]
Out[]=
1.
m/s
In[]:=
a1=Quantity[2.,"Meters"/"Seconds"^2]
Out[]=
2.
m/
2
s
In[]:=
t1=Quantity[3.,"Seconds"]
Out[]=
3.
s
In[]:=
test$distanceWithUnits2[10000,v1,a1,t1];//Timing
Out[]=
{16.456,Null}
Estimated performance hit factor using Quantity:
In[]:=
16.456011`/0.028378`
Out[]=
579.886
CASE: Use the units-aware physics function in another function
In the real application, the Quantity result of each units-aware function is used by many other units-aware functions
This is contrived, but the distance computed above is then used in another units-aware computation, and then another.
The final fuel consumed will be a volume.
In[]:=
fuelConsumed[v_,a_,t_,nTrips_,fuelPerDistance_]:=distanceWithUnits[v,a,t]fuelPerDistancenTrips;
In[]:=
fuelConsumed[v1,a1,t1,10,Quantity[1.234,"Liters"/"Meters"]]
Out[]=
148.08
L
It is safely units-aware. Except for communication in the user interface, the actual volume units used in the return value are irrelevant:
In[]:=
fuelConsumed[v1,a1,t1,10,Quantity[1234,"Milliliters"/"Meters"]]
Out[]=
148080.
mL
In this real application, this is done across of network of hundreds of units-aware functions, with each Quantity return of each function being used in many different contexts, by many other “downstream” units-aware functions.