[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: PROGV mystery explained



>Kim, your mention that EVAL does COMPILE by default prompts me to
>ask about the performance implications.  How much, relatively &
>intuitively, am I paying for this?   Would I be better off to
>arrange it so that compile is not the default on EVAL?  If so,
>what is the switch for it? Is this the same control as for the
>toplevel READ-EVAL loop?

*COMPILE-DEFINITIONS*, which defaults to T, is the switch.

EVAL does not compile everything. It knows that many forms are "easy"
to evaluate, and handles them. These include symbols, other atoms (which
are self-evaluating), and lists with a car of QUOTE, PROGN, SETQ,
EVAL-WHEN, IF, LOCALLY, SYMBOL-MACROLET, MACROLET, UNWIND-PROTECT,
or any symbol which is not a special form (e.g. macros and regular
functions). The compiler or the "real" evaluator is called whenever the user
form specifies any kind of binding or explicitly requests the creation of
a function object (This list is for 2.0 final. 2.0b1's EVAL invoked the compiler
a little more often).

As to whether you're better off timewise running with *COMPILE-DEFINITIONS*
set to NIL, that depends on what you're evaluating.

Here's an example where it doesn't matter, since the evaluated form is
"simple" enough to grok without considering *COMPILE-DEFINITIONS*:

? (let ((*compile-definitions* t))
    (without-interrupts
     (time (dotimes (i 1000) 
             (eval '(+ 2 3))))))
(DOTIMES (I 1000) (EVAL '(+ 2 3))) took 238 milliseconds (0.238 seconds) to run.
 16000 bytes of memory allocated.
NIL
? (let ((*compile-definitions* nil))
    (without-interrupts
     (time (dotimes (i 1000) 
             (eval '(+ 2 3))))))
(DOTIMES (I 1000) (EVAL '(+ 2 3))) took 238 milliseconds (0.238 seconds) to run.
 16000 bytes of memory allocated.
NIL
? 

Here's an example where it is slightly faster to run with
*COMPILE-DEFINITIONS* bound to NIL:

? (let ((*compile-definitions* t))
    (without-interrupts
     (time
      (eval '(let ((x 1)) x)))))
(EVAL '(LET ((X 1)) X)) took 13 milliseconds (0.013 seconds) to run.
Of that, 1 milliseconds (0.001 seconds) were spent in The Cooperative Multitasking Experience.
 600 bytes of memory allocated.
1
? (let ((*compile-definitions* nil))
    (without-interrupts
     (time
      (eval '(let ((x 1)) x)))))
(EVAL '(LET ((X 1)) X)) took 6 milliseconds (0.006 seconds) to run.
 800 bytes of memory allocated.
1
? 

If you do more computation, the evaluator starts to show its sluggishness:

? (let ((*compile-definitions* t))
    (without-interrupts
     (time
      (eval '(let ((x 1))
               (dotimes (i 1000) (incf x))
               x)))))
(EVAL '(LET ((X 1)) (DOTIMES (I 1000) (INCF X)) X)) took 91 milliseconds (0.091 seconds) to run.
Of that, 1 milliseconds (0.001 seconds) were spent in The Cooperative Multitasking Experience.
 1984 bytes of memory allocated.
1001
? (let ((*compile-definitions* nil))
    (without-interrupts
     (time
      (eval '(let ((x 1))
               (dotimes (i 1000) (incf x))
               x)))))
(EVAL '(LET ((X 1)) (DOTIMES (I 1000) (INCF X)) X)) took 552 milliseconds (0.552 seconds) to run.
 2296 bytes of memory allocated.
1001
? 

Note that the evaluator also conses a bit more in these two examples (I
don't know if this is a general rule).