Performance¶
Obviously, using unit-aware variables will slow down any computation compared to raw python values (int, flot, numpy.ndarray).
import matplotlib.pyplot as plt
import numpy as np
import physipy
from physipy import s, m, setup_matplotlib
from physipy import Dimension, units, quantify, Quantity
ms = units["ms"]
mm = units['mm']
km = units["km"]
cm = units["cm"]
mus = units["mus"]
ns = units["ns"]
a = 123456
b = 654321
aq = a*m
bq = b*m
Basic comparison on addition
Basic comparison on pow
benchmark timing¶
Here is a comparison of most operations :
import numpy as np
from physipy import m, s, Quantity
arr = np.linspace(0, 200)
sca = 5.14
pi = np.pi
arr_m = arr * m
ech_lmbda_mum = np.linspace(2, 15)
def bench_scalar_op_add(): m + m
def bench_scalar_op_sub(): m - m
def bench_scalar_op_mul(): m * m
def bench_scalar_op_div(): m / m
def bench_scalar_op_truediv(): m // m
def bench_scalar_op_pow(): m ** 1
def use_case():
x = arr * m
x2 = sca * s**2
y = x*x2/pi * np.sum(x**2) + 3*m**3*s**2
def bench_arr_scalar_op_add(): arr_m + m
def bench_arr_scalar_op_sub(): arr_m - m
def bench_arr_scalar_op_mul(): arr_m * m
def bench_arr_scalar_op_div(): arr_m / m
def bench_arr_scalar_op_truediv(): arr_m // m
def bench_arr_scalar_op_pow(): arr_m ** 1
def use_case2():
from physipy import units, constants, K
mum = units["mum"]
hp = constants["h"]
c = constants["c"]
kB = constants["k"]
def plancks_law(lmbda, Tbb):
return 2*hp*c**2/lmbda**5# * 1/(np.exp(hp*c/(lmbda*kB*Tbb))-1)
lmbdas = ech_lmbda_mum*mum
Tbb = 300*K
integral = np.trapz(plancks_law(lmbdas, Tbb), x=lmbdas)
import pint
import physipy
import forallpeople
import numpy as np
from astropy import units as astropy_units
ureg = pint.UnitRegistry()
a = 123
b = 654
# we don't want a big array since we are intersted in the differences
# between scalars and arrays, if any. Big array length could introduce
# time deltas from the numerical computation, not the unit overhead
arr = np.arange(1, 4)
import timeit
import operator
operations = {
"add":"__add__",
"sub":"__sub__",
"mul":"__mul__",
"truediv":"__truediv__",
}
N = 100000
physipy_qs = {
"name":"physipy",
"a":a*physipy.m,
"b":b*physipy.m,
'arrm':arr*physipy.m,
}
pint_qs = {
"name":"pint",
"a":a*ureg.m,
"b":b*ureg.m,
'arrm':arr*ureg.m,
}
fap_qs = {
"name":"forallpeople",
"a":a*forallpeople.m,
"b":b*forallpeople.m,
'arrm':arr*forallpeople.m,
}
asp_qs = {
"name":"astropy",
"a":a*astropy_units.m,
"b":b*astropy_units.m,
"arrm":arr*astropy_units.m,
}
results = []
for modules_dict in [physipy_qs, pint_qs, fap_qs, asp_qs]:
print(modules_dict["name"])
for operation, operation_method in operations.items():
aq = modules_dict["a"]
bq = modules_dict["b"]
#time = timeit.timeit('a.'+operation_method+"(b)", number=10000, globals=globals())
time_q = timeit.timeit('aq.'+operation_method+"(bq)", number=N, globals={"aq":aq, "bq":bq})#), globals=globals())
#print(f"{operation: >5} : {time_q/time: <5.1f}")
print(f"{operation :>5} : {time_q:.5f}")
results.append((modules_dict['name'], operation, time_q, aq, bq, "sca", "sca", str(getattr(operator, operation)(aq, bq))))
for operation, operation_method in operations.items():
aq = modules_dict["a"]
arrq = modules_dict["arrm"]
#time = timeit.timeit('a.'+operation_method+"(b)", number=10000, globals=globals())
try:
time_qarr = timeit.timeit('aq.'+operation_method+"(arr)", number=N, globals={"aq":aq, "arr":arrq})#), globals=globals())
print(f"{operation :>5} : {time_qarr:.5f}")
results.append((modules_dict['name'], operation, time_q, aq, arrq, "sca", "arr", str(getattr(operator, operation)(aq, arrq))))
except Exception as e:
print(f"{operation :>5} : {e}")
results.append((modules_dict['name'], operation, np.nan, aq, arrq, "sca", "arr", "Failed"))
import pandas as pd
import seaborn as sns
df = pd.DataFrame(results, columns=["package", "op", "time", "left", "right", "left_type", "right_type", "result"])
sns.catplot(
col="op", x="right_type", y="time", hue="package", data=df, kind="bar")
import pint
import physipy
import forallpeople
import numpy as np
ureg = pint.UnitRegistry()
a = 123456
b = 654321
arr = np.arange(100)
physipy_qs = {
"name":"physipy",
"a":a*physipy.m,
"b":b*physipy.m,
'arrm':arr*physipy.m,
}
pint_qs = {
"name":"pint",
"a":a*ureg.m,
"b":b*ureg.m,
'arrm':arr*ureg.m,
}
fap_qs = {
"name":"forallpeople",
"a":a*forallpeople.m,
"b":b*forallpeople.m,
'arrm':arr*forallpeople.m,
}
for modules_dict in [physipy_qs, pint_qs, fap_qs]:
print(modules_dict["name"])
for operation, operation_method in operations.items():
aq = modules_dict["a"]
bq = modules_dict["b"]
#time = timeit.timeit('a.'+operation_method+"(b)", number=10000, globals=globals())
time_q = timeit.timeit('aq.'+operation_method+"(bq)", number=10000, globals=globals())
#print(f"{operation: >5} : {time_q/time: <5.1f}")
print(f"{operation :>5} : {time_q:.5f}")
for operation, operation_method in operations.items():
aq = modules_dict["a"]
arr = modules_dict["arrm"]
#time = timeit.timeit('a.'+operation_method+"(b)", number=10000, globals=globals())
time_qarr = timeit.timeit('aq.'+operation_method+"(arr)", number=10000, globals=globals())
#print(f"{operation: >5} : {time_q/time: <5.1f}")
print(f"{operation :>5} : {time_qarr:.5f}")
Array creation¶
Compare lazy creation of arrays
Profiling base operations¶
Ideas for better performances : - less 'isinstance' - remove sympy - cleaner 'setattr'