Speed tricks
============

There are a few tricks that can significantly speed up mpmath code at low to medium precision (up a hundred digits or so):

* Repeated type conversions from floats, strings and integers are expensive (exceptions: ``n/x``, ``n*x`` and ``x**n`` are fast when ``n`` is an ``int`` and ``x`` is an ``mpf``). Numerical constants that are used repeatedly, such as in the body of a function passed to ``quadts``, should be pre-converted to ``mpf`` instances.

* The JIT compiler `psyco <http://psyco.sourceforge.net/>`_ fairly consistently speeds up mpmath about 2x.

* An additional 2x gain is possible by using the low-level functions in ``mpmath.lib`` instead of ``mpf`` instances.

* Changing the rounding mode to *floor* can give a slight speedup.

Here follows a simple example demonstrating some of these optimizations.

Original algorithm (0.028 seconds)::

    >>> from mpmath import *
    >>> mp.dps = 15
    >>> x = mpf(1)
    >>> for i in range(1000):
    ...     x += 0.1

Preconverting the float constant (0.0080 seconds)::

    >>> x = mpf(1)
    >>> one_tenth = mpf(0.1)
    >>> for i in range(1000):
    ...     x += one_tenth

With psyco (0.0036 seconds)::

    >>> import psyco; psyco.full()
    >>> x = mpf(1)
    >>> one_tenth = mpf(0.1)
    >>> for i in range(1000):
    ...     x += one_tenth

With psyco and low-level functions (0.0017 seconds)::

    >>> import psyco; psyco.full()
    >>> from mpmath.lib import from_int, from_float, fadd, round_nearest
    >>> x = from_int(1)
    >>> one_tenth = from_float(0.1)
    >>> for i in range(1000):
    ...     x = fadd(x, one_tenth, 53, round_nearest)

The last version is 16.5 times faster than the first (however, this example is extreme; the gain will usually be smaller in realistic calculations).

Many calculations can be done with ordinary floating-point arithmetic, and only in special cases require multiprecision arithmetic (for example to avoid overflows in corner cases). In these situations, it may be possible to write code that uses fast regular floats by default, and automatically (or manually) falls backs to mpmath only when needed. Python's dynamic namespaces and ability to compile code on the fly are helpful. Here is a simple (probably not failsafe) example::

    >>> import math
    >>> import mpmath
    >>>
    >>> def evalmath(expr):
    ...     try:
    ...         r = eval(expr, math.__dict__)
    ...     except OverflowError:
    ...         r = eval(expr, mpmath.__dict__)
    ...         try:
    ...             r = float(r)
    ...         except OverflowError:
    ...             pass
    ...     return r
    ...
    >>> evalmath('sin(3)')
    0.14112000805986721
    >>>
    >>> evalmath('exp(10000)')
    mpf('8.8068182256629216e+4342')
    >>>
    >>> evalmath('exp(10000) / exp(10000)')
    1.0
