Next: Constant Folding, Up: Source Optimization [Contents][Index]
The primary optimization of let variables is to delete them when they are unnecessary. Whenever the value of a let variable is a constant, a constant variable or a constant (local or non-notinline) function, the variable is deleted, and references to the variable are replaced with references to the constant expression. This is useful primarily in the expansion of macros or inline functions, where argument values are often constant in any given call, but are in general non-constant expressions that must be bound to preserve order of evaluation. Let variable optimization eliminates the need for macros to carefully avoid spurious bindings, and also makes inline functions just as efficient as macros.
A particularly interesting class of constant is a local function. Substituting for lexical variables that are bound to a function can substantially improve the efficiency of functional programming styles, for example:
(let ((a #'(lambda (x) (zow x)))) (funcall a 3))
effectively transforms to:
(zow 3)
This transformation is done even when the function is a closure, as in:
(let ((a (let ((y (zug))) #'(lambda (x) (zow x y))))) (funcall a 3))
becoming:
(zow 3 (zug))
A constant variable is a lexical variable that is never assigned to, always keeping its initial value. Whenever possible, avoid setting lexical variables—instead bind a new variable to the new value. Except for loop variables, it is almost always possible to avoid setting lexical variables. This form:
(let ((x (f x))) ...)
is more efficient than this form:
(setq x (f x)) ...
Setting variables makes the program more difficult to understand, both to the compiler and to the programmer. Python compiles assignments at least as efficiently as any other Common Lisp compiler, but most let optimizations are only done on constant variables.
Constant variables with only a single use are also optimized away,
even when the initial value is not constant.10 For example, this expansion of
incf
:
(let ((#:g3 (+ x 1))) (setq x #:G3))
becomes:
(setq x (+ x 1))
The type semantics of this transformation are more important than the
elimination of the variable itself. Consider what happens when
x
is declared to be a fixnum
; after the transformation,
the compiler can compile the addition knowing that the result is a
fixnum
, whereas before the transformation the addition would
have to allow for fixnum overflow.
Another variable optimization deletes any variable that is never read. This causes the initial value and any assigned values to be unused, allowing those expressions to be deleted if they have no side-effects.
Note that a let is actually a degenerate case of local call (see let-calls), and that let optimization can be done on calls that weren’t created by a let. Also, local call allows an applicative style of iteration that is totally assignment free.
The source transformation in this example doesn’t represent the preservation of evaluation order implicit in the compiler’s internal representation. Where necessary, the back end will reintroduce temporaries to preserve the semantics.
Next: Constant Folding, Up: Source Optimization [Contents][Index]