[net.lang.lisp] macros vs. functions

michaelm@bcsaic.UUCP (michael maxwell) (06/23/86)

In article <3827@utah-cs.UUCP> shebs@utah-cs.UUCP (Stanley Shebs) writes:
>In article <1311@well.UUCP> jjacobs@well.UUCP (Jeffrey Jacobs) writes:
>[...]  In fact, many expert Lisp hackers replace
>functions with equivalent macros when possible, to save function call overhead
>(I don't recommend this practice, most compilers have hooks to opencode user
>functions when requested).

My question doesn't really have anything to do with the original posting(s),
rather I'm interested in this comment on macros vs. functions.  Many of the
functions I write are only called from one place in the program (not
recursively!),  and are never called by the user; they're just conveniences 
for top-down programming.  I've often wondered whether turning them into macros 
wouldn't speed things up.  Anyone care to comment on Stan's contention that
turning such functions into macros doesn't speed things up?
comment on the 
-- 
Mike Maxwell
Boeing Artificial Intelligence Center
	...uw-beaver!uw-june!bcsaic!michaelm

shebs@utah-cs.UUCP (Stanley Shebs) (06/30/86)

In article <574@bcsaic.UUCP> michaelm@bcsaic.UUCP (michael maxwell) writes:
>In article <3827@utah-cs.UUCP> shebs@utah-cs.UUCP (Stanley Shebs) writes:
>>In article <1311@well.UUCP> jjacobs@well.UUCP (Jeffrey Jacobs) writes:
>>[...]  In fact, many expert Lisp hackers replace
>>functions with equivalent macros when possible, to save function call overhead
>>(I don't recommend this practice, most compilers have hooks to opencode user
>>functions when requested).
>
>My question doesn't really have anything to do with the original posting(s),
>rather I'm interested in this comment on macros vs. functions.  Many of the
>functions I write are only called from one place in the program (not
>recursively!),  and are never called by the user; they're just conveniences 
>for top-down programming.  I've often wondered whether turning them into macros 
>wouldn't speed things up.  Anyone care to comment on Stan's contention that
>turning such functions into macros doesn't speed things up?

Well mostly yes and a little no.

Consider the situation which I believe is an example of what you're talking
about:

(defun bar (x y)
  (foo x y 0))

(defun foo (a b c)
  (print c))

When you compile these, you get bar that consists of code to call foo,
and foo that consists of code to call print.

We can change foo to be defined as a macro:

(defmacro foo (a b c)
  `(print ,c))

The first step of the compiler is macro-expansion.  Macroexpansion replaces
one piece of source code with another,  so the compiler's work reduces to
compiling

(defun bar (x y)
  (print 0))

And when the compiler compiles this, bar will just be code to call print.
We've saved a lot! One-half the number of function calls and some argument
shuffling.  The whole macro expansion business has gone away completely,
and you don't even need to keep the definition of foo around at runtime
(those among us who are still awake will see a problem).

Now, before you run out to turn all your function calls into macros,
there are a bunch of caveats.

1) This technique cannot be used if an argument to the macro is
referenced twice and has a side effect.  If foo is defined:

(defun foo (a b c)
  (print c)
  (print c))

Then the obvious macro definition is

(defmacro foo (a b c)
  `(progn (print ,c) (print ,c)))

But if bar looks like

(defun bar (x y)
  (foo x y (incf y)))

then expansions yields

(defun bar (x y)
  (progn (print (incf y)) (print (incf y))))

which will increment y twice instead of once!  The solution where you
lambda-bind the item that shows up twice, as in

(defmacro foo (a b c)
  `(let ((tmp ,c))
     (print tmp) (print tmp)))

fixes the bug, but the let expands into a lambda application, which is (you
guessed it) a function call!  So you haven't won efficiency-wise.

2) As the first caveat showed by example, getting a macro semantically
right can be tricky.  Using macros to gain efficiency is asking for
trouble, especially if the functions that you want to rewrite are
complicated.

3) Use of macros can expand code size, since you are replacing a
function call with the whole body.  If this is done often, the increase
may be noticeable.  As a real fine point, note that increased
code size might bolix the cache and virtual memory, slowing
things down.

4) Macros in most Lisps (including CL) cannot be APPLYed.

5) The macro expansion process throws away the original body.
In the above example, if I redefine foo, bar will be unaffected.
If foo is a function call, bar will get the new function.

6) In the above example, the compiler is quite likely to compile
these calls as jumps, since they are in tail-recursive position.
It may or may not be the case that you get any advantage from the
macro, or it may be a 1% improvement even if the foo function is
called a lot.  The only way to know for sure is to look at your
compiled code and see what's being produced.  Even then you have
to have done all the other optimization things, like keeping track
of conses and avoiding linear searches down long lists (member fn
does it for instance).  These sorts of things can totally swamp
the cost of function calls.

7) Function protocols vary in expense.  PSL's functions are cheap, Franz's
are more expensive, Spice Lisp's are very expensive.  There is little
point to optimizing cheap function calls, unless you're doing realtime
in Lisp or some such.

8) Some compiler (PCLS for instance) allow you to declare that
functions will be coded "inline", which is almost identical to
macroexpansion.  The compiler can then do the tricky parts of
figuring out the equivalent macro of a function and substitute,
while you can still use it as a function etc.

Common Lisp provides a standardized syntax for declaring this inlining
operation, so it is becoming more prevalent as an option for
Lispers.  That's why I don't recommend using macros - it has
to be a Lisp where you can't tell the compiler what to do, and
you have to *know* that you will get a measurable speedup.

>Mike Maxwell

						stan shebs