.. _performance:
Optimizing Performance
======================
Pint can impose a significant performance overhead on computationally-intensive problems. The following are some suggestions for getting the best performance.
.. note:: Examples below are based on the IPython shell (which provides the handy %timeit extension), so they will not work in a standard Python interpreter.
Use magnitudes when possible
----------------------------
It's significantly faster to perform mathematical operations on magnitudes (even though your'e still using pint to retrieve them from a quantity object).
.. doctest::
In [1]: from pint import UnitRegistry
In [2]: ureg = UnitRegistry()
In [3]: q1 =ureg('1m')
In [5]: q2=ureg('2m')
In [6]: %timeit (q1-q2)
100000 loops, best of 3: 7.9 µs per loop
In [7]: %timeit (q1.magnitude-q2.magnitude)
1000000 loops, best of 3: 356 ns per loop
This is especially important when using pint Quantities in conjunction with an iterative solver, such as the `brentq method`_ from scipy:
.. doctest::
In [1]: from scipy.optimize import brentq
In [2]: def foobar_with_quantity(x):
# find the value of x that equals q2
# assign x the same units as q2
qx = ureg(str(x)+str(q2.units))
# compare the two quantities, then take their magnitude because
# brentq requires a dimensionless return type
return (qx - q2).magnitude
In [3]: def foobar_with_magnitude(x):
# find the value of x that equals q2
# don't bother converting x to a quantity, just compare it with q2's magnitude
return x - q2.magnitude
In [4]: %timeit brentq(foobar_with_quantity,0,q2.magnitude)
1000 loops, best of 3: 310 µs per loop
In [5]: %timeit brentq(foobar_with_magnitude,0,q2.magnitude)
1000000 loops, best of 3: 1.63 µs per loop
Bear in mind that altering computations like this **loses the benefits of automatic unit conversion**, so use with care.
A safer method: wrapping
------------------------
A better way to use magnitudes is to use pint's wraps decorator (See :ref:`wrapping`). By decorating a function with wraps, you pass only the magnitude of an argument to the function body according to units you specify. As such this method is safer in that you are sure the magnitude is supplied in the correct units.
.. doctest::
In [1]: import pint
In [2]: ureg = pint.UnitRegistry()
In [3]: import numpy as np
In [4]: def f(x, y):
return (x - y) / (x + y) * np.log(x/y)
In [5]: @ureg.wraps(None, ('meter', 'meter'))
def g(x, y):
return (x - y) / (x + y) * np.log(x/y)
In [6]: a = 1 * ureg.meter
In [7]: b = 1 * ureg.centimeter
In [8]: %timeit f(a, b)
1000 loops, best of 3: 312 µs per loop
In [9]: %timeit g(a, b)
10000 loops, best of 3: 65.4 µs per loop
.. _`brentq method`: http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brentq.html