On Tue, 14 Sept 2021 at 23:12, sandona...@gmail.com <
sandona.dav...@gmail.com> wrote:

> Hello,
> let's say I'd like to numerically evaluate a single sympy function over an
> array using sympy as the module. Curiously, passing in regular Python's
> float numbers makes the evaluation much faster then passing in Sympy's
> Float instances. I tried several sympy functions, they tend to follow this
> trend.
>
> Why does this happen? Considering that the following lambda function
> always return Float instances,  I thought that the input was sympified
> inside a sympy function, thus making the actual python's float evaluation
> slower due to casting...
>
> from sympy import *
> import numpy as np
> from mpmath import mpf
>
> func = sin(x)
> f = np.frompyfunc(lambdify(x, func, "sympy"), 1, 1)
> domain = np.linspace(0, 100, 1000)
> domain_mpf = [mpf(str(t)) for t in domain]
> domain_sympy = [Float(t) for t in domain]
>
> %timeit f(domain)
> %timeit f(domain_mpf)
> %timeit f(domain_sympy)
>

Running that here I get:

   ...: %timeit f(domain)
   ...: %timeit f(domain_mpf)
   ...: %timeit f(domain_sympy)
756 µs ± 58.1 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
4.67 ms ± 130 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
4.46 ms ± 64.6 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

However if I switch from timeit to time and run each in a separate process
(close and reopen isympy in between each %time) I get:

   ...: %time ok = f(domain)
CPU times: user 920 ms, sys: 4 ms, total: 924 ms
Wall time: 921 ms

   ...: %time ok = f(domain_mpf)
CPU times: user 824 ms, sys: 8 ms, total: 832 ms
Wall time: 834 ms

   ...: %time ok = f(domain_sympy)
CPU times: user 856 ms, sys: 16 ms, total: 872 ms
Wall time: 877 ms

So now f(domain) is slowest where %timeit suggested that it was fastest.
Also the time measured by %time is 2-3 orders of magnitude greater than the
result from %timeit.

The reason that %time shows a much slower time than %timeit is because
common operations in SymPy are cached. Since %timeit times things in a loop
and records only the fastest runs it only ever really measures the
warm-cache time. Conversely it never really tells you the time to compute
something *once* which is in fact all that really matters in most
situations.

The 3 millisecond timing difference that you are asking about here is
dwarfed by the actual 1 second time that it really takes to compute this
result the first time. Most likely the time differences you see are just to
do with exactly how efficient the cache lookups are for different types.

It is possible to run timings with the cache disabled (env var:
SYMPY_USE_CACHE=no). However then SymPy is often much slower and so those
timings are also not necessarily representative of the timings seen with
the cache enabled which is how SymPy is normally used.

By all means investigate timeit results when micro-optimising but when you
actually want to evaluate the actual real time that it takes to do
something then you need to use a fresh process and time the result of
running the operation precisely once. Yes, that will give noisy results
compared to timeit but the results from timeit eliminate the "noise" to
give a consistent but meaningless measure. Alternatively you can use
%timeit but with SYMPY_USE_CACHE=no bearing in mind the caveat that in
practice sympy is not normally used with that setting so it may also be
unrepresentative for different reasons.

--
Oscar

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sympy+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sympy/CAHVvXxThamFOzU5%2BpeUyVOnBKKyuAqvQ%2BndkTj3w%3Dj935yj0yQ%40mail.gmail.com.

Reply via email to