On Sun, Aug 23, 2009 at 6:37 PM, Nils Bruin<nbr...@sfu.ca> wrote:
>
> On Aug 23, 1:58 pm, William Stein <wst...@gmail.com> wrote:
>> To whet our appetite though, might you do some benchmarks that compare
>> the speed of adding 2+2 via the library and via pexpect?
>
> OK, here's a little experiment. I thought adding 2 and 2 would be too
> hard for now, so I stuck with 1+2. Good news is that all routines
> arrived at the correct answer. I did count the conversions back and
> forth every time as well, so times are not so very low. The
> conversions are completely string based, so I think performance can be
> quite a bit better still. Anyway, results:
>
> ecl/library (+ 2 2): 27.6 µs per loop
> maxima/library(+ 2 2): 134 µs per loop
> maxima/pexpect(+ 2 2): 4.97 ms per loop
> python/strings(2+2): 14 µs per loop
>
> So yes, library access does work a little better than pexpect.
>
> code used:
>
> sage: from sage.libs.ecl import *
> sage: init_ecl()
> sage: ecl_eval("(require 'asdf)")
> ;;; Loading #P"/home/wstein/build/sage-4.1.1/local/lib/ecl-9.4.1/
> asdf.fas"
> ;;; Loading #P"/home/wstein/build/sage-4.1.1/local/lib/ecl-9.4.1/
> cmp.fas"
> ;;; Loading #P"/home/wstein/build/sage-4.1.1/local/lib/ecl-9.4.1/
> sysfun.lsp"
> <ECL: ("ASDF" "CMP") >
> sage: ecl_eval('(load "%s")'%(SAGE_ROOT+"/local/lib/maxima/
> maxima.fasb"))
> ;;; Loading "/scratch/nbruin/sage-4.1.1-ecl-x86_64-Linux/local/lib/
> maxima/maxima.fasb"
> <ECL: #P"/scratch/nbruin/sage-4.1.1-ecl-x86_64-Linux/local/lib/maxima/
> maxima.fasb" >
> sage: ecl_eval('(in-package :maxima)')
> <ECL: #<"MAXIMA" package> >
> sage:
> sage: def ecladd(a,b):
> ....:       return eval(str(ecl_eval('(+ %s %s)'%(a,b))));
> ....:
> sage: def libadd(a,b):
> ....:       return eval(str(ecl_eval('(MEVAL #$ %s + %s$)'%(a,b))))
> ....:
> sage: def maxadd(a,b):
> ....:       return (maxima(a)+maxima(b)).sage()
> ....:
> sage: def pythadd(a,b):
> ....:       return eval("%s + %s"%(a,b))
> ....:
> sage: timeit("ecladd(1,2)")
> 625 loops, best of 3: 27.6 µs per loop
> sage: timeit("libadd(1,2)")
> 625 loops, best of 3: 134 µs per loop

First, shouldn't you compare with:

sage: timeit("str(maxima.eval('1+2'))")
5 loops, best of 3: 584 µs per loop

rather than what you put above.  The above is much more comparable.
If the speedup by switching to the C library is really going to only
be 584/134.0 = 4.358..., then I'm worried it isn't worth it.   Is the
Maxima parser just really really slow?

In fact, I am mildly surprised that the above two timings between ecl
and maxima are so different.  I wonder why it is an order of magnitude
faster to compute "(+ 1 2)"   than to compute "(MEVAL #$ 1 + 2$)".
Perhaps in the second case the Maxima parser has to parse "1+2"?

Here are some other relevant timings (the below is on sage.math.washington.edu):

sage: timeit("str(pari('1+2'))")
625 loops, best of 3: 5.12 µs per loop
sage: timeit("pari('1+2')")
625 loops, best of 3: 3.33 µs per loop

sage: timeit("str(gap.eval('1+2'))")
625 loops, best of 3: 223 µs per loop
sage: timeit("str(gp.eval('1+2'))")
625 loops, best of 3: 95.7 µs per loop

I just noticed that maxima.eval gets dramatically slower every time it
is called even on this simple benchmark:

sage: timeit("str(maxima.eval('1+2'))")
5 loops, best of 3: 758 µs per loop
sage: timeit("str(maxima.eval('1+2'))")
625 loops, best of 3: 1.22 ms per loop
sage: timeit("str(maxima.eval('1+2'))")
125 loops, best of 3: 2.98 ms per loop
sage: timeit("str(maxima.eval('1+2'))")
125 loops, best of 3: 3.97 ms per loop

I can think of absolutely no logical reason for this.  (Yes, I know
similar things in more complicated situations have been pointed out
before.)  This is not a generic pexpect interface issue, since it does
not happen with other interfaces, e.g., the GP/pari interface:

sage: timeit("str(gp.eval('1+2'))")
625 loops, best of 3: 169 µs per loop
sage: timeit("str(gp.eval('1+2'))")
625 loops, best of 3: 170 µs per loop
sage: timeit("str(gp.eval('1+2'))")
625 loops, best of 3: 170 µs per loop
sage: timeit("str(gp.eval('1+2'))")
625 loops, best of 3: 169 µs per loop

or the GAP interface:

sage: timeit("str(gap.eval('1+2'))")
625 loops, best of 3: 220 µs per loop
sage: timeit("str(gap.eval('1+2'))")
625 loops, best of 3: 217 µs per loop
sage: timeit("str(gap.eval('1+2'))")
625 loops, best of 3: 215 µs per loop
sage: timeit("str(gap.eval('1+2'))")
625 loops, best of 3: 218 µs per loop

So there is something *seriously* foo-bared about Sage's
communications with Maxima that doesn't appear with the other pexpect
interfaces.  It's also possible that Maxima+ecl parser just degrades
over time.   We had the same problem in Sage-3.4 before switching to
ECL, by the way, except then the time started out a lot slower than
with ECL, but got badly just as quickly:

wst...@sage:/disk/scratch/mabshoff-sage-releases/sage-3.4$ ./sage
----------------------------------------------------------------------
| Sage Version 3.4, Release Date: 2009-03-11                         |
| Type notebook() for the GUI, and license() for information.        |
----------------------------------------------------------------------
sage: timeit("str(maxima.eval('1+2'))")
5 loops, best of 3: 2.06 ms per loop
sage: timeit("str(maxima.eval('1+2'))")
125 loops, best of 3: 2.32 ms per loop
sage: timeit("str(maxima.eval('1+2'))")
125 loops, best of 3: 2.64 ms per loop
sage: timeit("str(maxima.eval('1+2'))")
125 loops, best of 3: 2.88 ms per loop
sage: timeit("str(maxima.eval('1+2'))")
125 loops, best of 3: 3.09 ms per loop
sage: timeit("str(maxima.eval('1+2'))")
125 loops, best of 3: 3.23 ms per loop
sage: timeit("str(maxima.eval('1+2'))")
125 loops, best of 3: 3.43 ms per loop

Robert Dodier -- do you have any crazy ideas why Maxima might get
slower to do the same thing over time?  Is it caching all output
somewhere?  What other caching might be going on?

To see what's happening I logged all the IO between Maxima and Sage
while it gets slower as follows:

wst...@sage:~$ sage
----------------------------------------------------------------------
| Sage Version 4.1.1, Release Date: 2009-08-14                       |
| Type notebook() for the GUI, and license() for information.        |
----------------------------------------------------------------------
sage: m = Maxima(logfile="/home/wstein/tmp/maxima.log")
sage: timeit("m.eval('1+2')")
5 loops, best of 3: 681 µs per loop
sage: timeit("m.eval('1+2')")
625 loops, best of 3: 1.22 ms per loop
sage: timeit("m.eval('1+2')")
125 loops, best of 3: 3.01 ms per loop

The logfile is here:

http://wstein.org/home/wstein/tmp/maxima.log

Since the synchronization may be overwhelming (or responsible), I
built another version of Sage with it disabled.   The results:

sage: m = Maxima(logfile="/home/wstein/tmp/maxima-nosync.log")
sage: timeit("m.eval('1+2')")
5 loops, best of 3: 253 µs per loop
sage: timeit("m.eval('1+2')")
625 loops, best of 3: 312 µs per loop
sage: timeit("m.eval('1+2')")
625 loops, best of 3: 475 µs per loop
sage: timeit("m.eval('1+2')")
625 loops, best of 3: 658 µs per loop
sage: timeit("m.eval('1+2')")
625 loops, best of 3: 1.03 ms per loop

and the output file:

http://wstein.org/home/wstein/tmp/maxima-nosync.log

which is just lots of:

"(%i566) 1+2;
<sage-display>
(%o566) 3"

It's really disturbing that Maxima systematically slows down like
this.  Again, none of the other pexpect interfaces have this problem,
at least not Mathematica, Magma, GAP, GP/PARI, MATLAB, etc.   The
implementations of the different interfaces are nearly identical, so I
don't know why Maxima is messed up like this.

Nils -- can you test via the C library and see if you notice any
similar slowdown????  That could tell us something.

By the way, note the initial time of "253 µs per loop".  With work,
e.g., some slight change to Maxima itself to embed some control codes
in output, one could get rid of the synchronization function.  Then
Sage <--> Maxima communication would be that fast over pexpect.  Is it
really the case that using the C library only speeds stuff up by a
factor of 2 for basic operations?    Wow.

> sage: timeit("maxadd(1,2)")
> 5 loops, best of 3: 4.97 ms per loop

4970/134.0 = 37...

> sage: timeit("pythadd(1,2)")
> 625 loops, best of 3: 14 µs per loop


Here's something very surprising to me that has essentially nothing to
do with this discussion.

On sage.math (Linux) I get the same timing as you:

def pythadd(a,b):
sage: def pythadd(a,b):
....:     return eval("%s + %s"%(a,b))
....:
sage: timeit("pythadd(1,2)")
625 loops, best of 3: 14.5 µs per loop

In shocking contrast, on OS X (on my laptop or desktop), I get vastly
worse timings:

sage:  timeit("pythadd(1,2)")
625 loops, best of 3: 65.7 µs per loop
sage:  timeit("eval('2+2')")
625 loops, best of 3: 57.9 µs per loop

So calling Python's eval on OS X is about 4 times slower than in Linux
on similar hardware.   I didn't know that.

 -- William

-- 
William Stein
Associate Professor of Mathematics
University of Washington
http://wstein.org

--~--~---------~--~----~------------~-------~--~----~
To post to this group, send an email to sage-devel@googlegroups.com
To unsubscribe from this group, send an email to 
sage-devel-unsubscr...@googlegroups.com
For more options, visit this group at http://groups.google.com/group/sage-devel
URLs: http://www.sagemath.org
-~----------~----~----~----~------~----~------~--~---

Reply via email to