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

Efficiency of local fns and closures



    Date: Fri, 20 Oct 89 05:31:49 EDT
    From: barmar@Think.COM
    
Barry, this was a good answer, but I need to comment on a couple of
things you said.

       Date: Thu, 19 Oct 89 16:10 EST
       From: GODDEN%gmr.com@RELAY.CS.NET
    
       Consider:
    
       (defun foo (x y)
	 (labels ((bar (z)
		   (some-function y z)))		;free reference to y
	   (bar (some-other-function x))))
    
       I have a two-part question regarding efficiency:
    
    Your questions can easily be answered by looking at some disassemblies and
    a few simple timings with the TIME macro.
    
For things this fast you can usually get more accurate results by using
METERING:DEFINE-METERING-FUNCTION.  It runs the test case as many times
as necessary to get a statistically valid result, and filters out
various sources of inaccuracy.  Try clicking middle on the average it
presents.  We probably should make TIME filter out the EVAL overhead,
but I don't think Common Lisp will let us make TIME run the test case
multiple times and do statistical processing, so unfortunately TIME
can never be as accurate as METERING:DEFINE-METERING-FUNCTION.

    A quick
    test I just tried seemed to indicate that it takes about 4 microseconds
    longer to access a lexical variable than a parameter variable; 5
    microseconds rather than 1 microsecond per access.

Those numbers seem quite a lot higher than I expected.  Actual
measurement (admittedly in a prototype of Genera 8.0, but for this
particular operation 7.2 should be the same speed) on a 3640 shows
accessing a parent's lexical variable is 1.3 microseconds slower than
accessing a local lexical variable.  The time to access a local
lexical variable on a 3640 is about 200 ns, which is small enough
to make it difficult to measure accurately.  All the numbers are
faster on Ivory-based machines.
    
    For compiling to core, I can't think of a hard technical problem, and I
    think the ANSI CL draft requires this to work.  

Do you mean, does (LET ((X 1)) (COMPILE NIL #'(LAMBDA () X))) have a
defined meaning in ANSI Common Lisp?  The answer is that it does not.
This was considered and rejected.  See COMPILE-ARGUMENT-PROBLEMS:CLARIFY.

How I measured it:

(metering:define-metering-function m1 (lexp) ()
   (let ((a 1)(b 2))
     (flet ((foo (lexp loc)
              (if lexp a loc)))
        (foo lexp b))))

(metering:define-metering-function m2 (locp) ()
   (let ((a 1)(b 2))
     (flet ((foo (locp loc)
              (if locp loc a)))
        (foo locp b))))

then compare the speed of (m1 t) to (m2 t).  Note how I have
been careful to make sure that nothing else changes between
the two cases; the pattern of branches is the same for both.
Comparing (m1 t) to (m1 nil) would not meet this criterion.
The reason for using a lexp/locp argument is to prevent the
compiler from figuring out whether I am going to use the
lexical variable or not and optimizing on that basis.