mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
496 lines
18 KiB
Plaintext
496 lines
18 KiB
Plaintext
@c Generated by the sb-manual contrib. Do not edit.
|
|
|
|
@node efficiency
|
|
@cindex efficicency
|
|
@chapter Efficiency
|
|
|
|
@menu
|
|
* Slot Access: slot access.
|
|
* Stack Allocation: stack allocation.
|
|
* Modular Arithmetic: modular arithmetic.
|
|
* Recognized Idioms: recognized idioms.
|
|
* Global and Always-bound Variables: global and always bound variables.
|
|
* Miscellaneous Efficiency Issues: miscellaneous efficiency issues.
|
|
@end menu
|
|
|
|
@node slot access
|
|
@cindex slot access
|
|
@section Slot Access
|
|
|
|
@menu
|
|
* Structure Object Slot Access: structure object slot access.
|
|
* Standard Object Slot Access: standard object slot access.
|
|
@end menu
|
|
|
|
@node structure object slot access
|
|
@subsection Structure Object Slot Access
|
|
|
|
Structure slot accessors are efficient only if the compiler is
|
|
able to open code them: compiling a call to a structure slot
|
|
accessor before the structure is defined, declaring one @code{notinline},
|
|
or passing it as a functional argument to another function causes
|
|
severe performance degradation.
|
|
|
|
@node standard object slot access
|
|
@subsection Standard Object Slot Access
|
|
|
|
The most efficient way to access a slot of a @code{standard-object} is
|
|
by using @code{slot-value} with a constant slot name argument inside a
|
|
@code{defmethod} body, where the variable holding the instance is a
|
|
specializer parameter of the method and is never assigned to. The
|
|
cost is roughly 1.6 times that of an open coded structure slot
|
|
accessor.
|
|
|
|
Second most efficient way is to use a CLOS slot accessor, or
|
|
@code{slot-value} with a constant slot name argument, but in circumstances
|
|
other than specified above. This may be up to 3 times as slow as the
|
|
method described above.
|
|
|
|
Example:
|
|
|
|
@example
|
|
(defclass foo () ((bar)))
|
|
|
|
;; Fast: specializer and never assigned to
|
|
(defmethod quux ((foo foo) new)
|
|
(let ((old (slot-value foo 'bar)))
|
|
(setf (slot-value foo 'bar) new)
|
|
old))
|
|
|
|
;; Slow: not a specializer
|
|
(defmethod quux ((foo foo) new)
|
|
(let* ((temp foo)
|
|
(old (slot-value temp 'bar)))
|
|
(setf (slot-value temp 'bar) new)
|
|
old))
|
|
|
|
;; Slow: assignment to FOO
|
|
(defmethod quux ((foo foo) new)
|
|
(let ((old (slot-value foo 'bar)))
|
|
(setf (slot-value foo 'bar) new)
|
|
(setf foo new)
|
|
old))
|
|
@end example
|
|
|
|
Note that when profiling code such as this, the first few calls to the
|
|
generic function are not representative, as the dispatch mechanism is
|
|
lazily set up during those calls.
|
|
|
|
@node stack allocation
|
|
@section Stack Allocation
|
|
|
|
SBCL has fairly extensive support for performing allocations on the
|
|
stack when a variable or function is declared @code{dynamic-extent}. The
|
|
@code{dynamic-extent} declarations are not verified but are simply trusted
|
|
as long as @code{sb-ext:*stack-allocate-dynamic-extent*} is true.
|
|
|
|
@anchor{Variable sb-ext *stack-allocate-dynamic-extent*}
|
|
@vvindex @sortas{stack-allocate-dynamic-extent* sb-ext} *stack-allocate-dynamic-extent* [sb-ext]
|
|
@deffn{Variable} sb-ext:*stack-allocate-dynamic-extent*
|
|
If true (the default), the compiler believes @code{dynamic-extent} declarations
|
|
and stack allocates otherwise inaccessible parts of the object whenever
|
|
possible.
|
|
@end deffn
|
|
SBCL recognizes any value which a variable declared @code{dynamic-extent}
|
|
can take on as having dynamic extent. This means that, in addition
|
|
to the value a variable is bound to initially, a value assigned to a
|
|
variable by @code{setq} is also recognized as having dynamic extent when
|
|
the variable is declared @code{dynamic-extent}. Users can thus build
|
|
complex structures on the stack using iteration and @code{setq}.
|
|
|
|
At present, SBCL implements stack allocation for the following kinds
|
|
of values when they are recognized as having dynamic extent:
|
|
|
|
@itemize
|
|
@item @code{&rest} lists;
|
|
|
|
@item the results of @code{cons}, @code{list}, @code{list*}, and @code{vector};
|
|
|
|
@item the result of simple forms of @code{make-array}: stack allocation is
|
|
possible only if the resulting array is known to be both simple
|
|
and one-dimensional, and has a constant @code{:element-type};
|
|
|
|
@quotation
|
|
@cindex safety, optimization quality
|
|
@cindex optimization quality safety
|
|
@strong{Warning}: Stack space is limited, so allocation of a large
|
|
vector may cause stack overflow. Stack overflow checks are
|
|
done except in 0 safety policies.
|
|
@end quotation
|
|
|
|
@item closures defined with @code{flet} or @code{labels} with a bound @code{dynamic-extent}
|
|
declaration;
|
|
|
|
@item anonymous closures defined with @code{lambda};
|
|
|
|
@item user-defined structures when the structure constructor defined using
|
|
@code{defstruct} has been declared @code{inline};
|
|
|
|
@quotation
|
|
@emph{Note}: Structures with @emph{raw} slots can currently be
|
|
stack-allocated only on x86 and x86-64. A raw slot is one
|
|
whose declared type is a subtype of exactly one of:
|
|
@code{double-float}, @code{single-float}, @code{(complex
|
|
double-float)}, @code{(complex single-float)}, or @code{sb-ext:word}; but
|
|
as an exception to the preceding, any subtype of @code{fixnum} is not
|
|
stored as raw despite also being a subtype of @code{sb-ext:word}.
|
|
@end quotation
|
|
|
|
@item otherwise-inaccessible parts of objects recognized to be dynamic
|
|
extent. The support for detecting when this applies is very
|
|
sophisticated. The compiler can do this detection when any value
|
|
form for a variable contains conditional allocations, function
|
|
calls, inlined functions, anonymous closures, or even other
|
|
variables. This allows stack allocation of complex structures.
|
|
@end itemize
|
|
|
|
Examples:
|
|
|
|
@example
|
|
;;; Declaiming a structure constructor inline before definition makes
|
|
;;; stack allocation possible.
|
|
(declaim (inline make-thing))
|
|
(defstruct thing obj next)
|
|
|
|
;;; Stack allocation of various objects bound to DYNAMIC-EXTENT
|
|
;;; variables.
|
|
(let* ((list (list 1 2 3))
|
|
(nested (cons (list 1 2) (list* 3 4 (list 5))))
|
|
(vector (make-array 3 :element-type 'single-float))
|
|
(thing (make-thing :obj list
|
|
:next (make-thing :obj (make-array 3))))
|
|
(closure (let ((y ...)) (lambda () y))))
|
|
(declare (dynamic-extent list nested vector thing closure))
|
|
...)
|
|
|
|
;;; Stack allocation of objects assigned to DYNAMIC-EXTENT variables.
|
|
(let ((x nil))
|
|
(declare (dynamic-extent x))
|
|
(setq x (list 1 2 3))
|
|
(dotimes (i 10)
|
|
(setq x (cons i x)))
|
|
...)
|
|
|
|
;;; Stack allocation of arguments to a local function is equivalent
|
|
;;; to stack allocation of local variable values.
|
|
(flet ((f (x)
|
|
(declare (dynamic-extent x))
|
|
...))
|
|
...
|
|
(f (list 1 2 3))
|
|
(f (cons (cons 1 2) (cons 3 4)))
|
|
...)
|
|
|
|
;;; Stack allocation of &REST lists
|
|
(defun foo (&rest args)
|
|
(declare (dynamic-extent args))
|
|
...)
|
|
@end example
|
|
|
|
As a notable exception to recognizing otherwise inaccessible parts
|
|
of other recognized dynamic extent values, SBCL does not as of
|
|
1.0.48.21 propagate dynamic-extentness through @code{&rest} arguments --
|
|
but another conforming implementation might, so portable code should
|
|
not rely on this.
|
|
|
|
@example
|
|
(declaim (inline foo))
|
|
(defun foo (fun &rest arguments)
|
|
(declare (dynamic-extent arguments))
|
|
(apply fun arguments))
|
|
|
|
(defun bar (a)
|
|
;; SBCL will heap allocate the result of (LIST A), and stack
|
|
;; allocate only the spine of the &rest list -- so this is
|
|
;; safe but unportable.
|
|
;;
|
|
;; Another implementation, including earlier versions of SBCL
|
|
;; might consider (LIST A) to be otherwise inaccessible and
|
|
;; stack-allocate it as well!
|
|
(foo #'car (list a)))
|
|
@end example
|
|
|
|
If dynamic extent constraints specified in the Common Lisp standard
|
|
are violated, the best that can happen is for the program to have
|
|
garbage in variables and return values; more commonly, the system
|
|
will crash.
|
|
|
|
In particular, it is important to realize that this can interact in
|
|
suprising ways with the otherwise inaccessible parts criterion:
|
|
|
|
@example
|
|
(let* ((a (list 1 2 3))
|
|
(b (cons a a)))
|
|
(declare (dynamic-extent b))
|
|
;; Unless A is accessed elsewhere as well, SBCL will consider
|
|
;; it to be otherwise inaccessible -- it can only be accessed
|
|
;; through B, after all -- and stack allocate it as well.
|
|
;;
|
|
;; Hence returning (CAR B) here is unsafe.
|
|
...)
|
|
@end example
|
|
|
|
SBCL also performs sophisticated escape analysis to enable automatic
|
|
stack allocation of local functions without any bound dynamic extent
|
|
declarations in many situations where the compiler can prove that no
|
|
uses escape (traditional Lisp terminology names this situation "all
|
|
uses are downward funargs"). For example, in the following
|
|
function, the local function @code{#'predicatep} is stack allocated,
|
|
because the compiler understands that the built-in function
|
|
@code{position-if} only uses its first argument as a downward funarg:
|
|
|
|
@example
|
|
(let ((acc 0))
|
|
(flet ((predicatep (num) (plusp (+ num off))))
|
|
(dotimes (i 10)
|
|
(incf acc (position-if #'predicatep array)))
|
|
(if (plusp off)
|
|
(incf acc (if (positivep acc) 10 3))
|
|
(incf acc (position-if #'predicatep array))))
|
|
acc)
|
|
@end example
|
|
|
|
Users can also declare that their own functions take downward
|
|
funargs by adding bound dynamic extent declarations on the function
|
|
arguments.
|
|
|
|
@example
|
|
(defun trivial-hof (fun arg)
|
|
(declare (dynamic-extent fun))
|
|
(funcall fun 3 arg))
|
|
@end example
|
|
|
|
Currently, such dynamic extent declarations only cause stack
|
|
allocation of downward funargs at call sites on sufficiently unsafe
|
|
policy. This is partly because the compiler is currently not able to
|
|
detect incorrect usage of dynamic extent declarations.
|
|
|
|
@example
|
|
(defun autodxclosure1 (&optional (x 4))
|
|
;; Calling a higher-order function will only implicitly
|
|
;; stack-allocate a funarg if the callee is trusted (a CL:
|
|
;; function) or the caller is unsafe.
|
|
(declare (optimize speed (safety 0) (debug 0)))
|
|
(trivial-hof (lambda (a b) (+ a b x)) 92))
|
|
@end example
|
|
|
|
@node modular arithmetic
|
|
@cindex modular arithmetic
|
|
@cindex arithmetic, modular
|
|
@cindex arithmetic, hardware
|
|
@section Modular Arithmetic
|
|
|
|
@menu
|
|
* Signed Modular Arithmetic: signed modular arithmetic.
|
|
@end menu
|
|
|
|
Some numeric functions have a property: n lower bits of the
|
|
result depend only on n lower bits of (all or some) arguments. If
|
|
the compiler sees an expression of form @code{(logand <expr> <mask>)},
|
|
where @code{<expr>} is a tree of such @emph{good} functions and @code{<mask>} is
|
|
known to be of type @code{(unsigned-byte <w>)}, where @code{<w>} is a @emph{good}
|
|
width, all intermediate results will be cut to @code{<w>} bits (but it is
|
|
not done for variables and constants!). This often results in an
|
|
ability to use simple machine instructions for the functions.
|
|
|
|
Consider this example:
|
|
|
|
@example
|
|
(defun i (x y)
|
|
(declare (type (unsigned-byte 32) x y))
|
|
(ldb (byte 32 0) (logxor x (lognot y))))
|
|
@end example
|
|
|
|
The result of @code{(lognot y)} will be negative and of type
|
|
@code{(signed-byte 33)}, so a naive implementation on a 32-bit platform
|
|
is unable to use 32-bit arithmetic here. But modular arithmetic
|
|
optimizer is able to do it: because the result is cut down to 32
|
|
bits, the compiler will replace @code{logxor} and @code{lognot} with versions
|
|
cutting results to 32 bits, and because terminals (here, expressions
|
|
@code{x} and @code{y}) are also of type @code{(unsigned-byte 32)}, 32-bit machine
|
|
arithmetic can be used.
|
|
|
|
As of SBCL 0.8.5 good functions are @code{+}, @code{-}, @code{logand}, @code{logior},
|
|
@code{logxor}, @code{lognot} and their combinations; and @code{ash} with the positive
|
|
second argument. Good widths are 32 on 32-bit CPUs and 64 on 64-bit
|
|
CPUs. While it is possible to support smaller widths as well,
|
|
currently this is not implemented.
|
|
|
|
@node signed modular arithmetic
|
|
@subsection Signed Modular Arithmetic
|
|
|
|
Sign-extending the result in the following way will be
|
|
translated into signed modular arithmetic:
|
|
|
|
@example
|
|
(defun add (a b)
|
|
(declare (type (signed-byte 64) a b))
|
|
(let ((u (ldb (byte 64 0) (+ a b))))
|
|
(logior u (- (mask-field (byte 1 63) u)))))
|
|
@end example
|
|
|
|
@node recognized idioms
|
|
@cindex modular arithmetic
|
|
@cindex arithmetic, modular
|
|
@cindex arithmetic, hardware
|
|
@section Recognized Idioms
|
|
|
|
@menu
|
|
* Count Trailing Zeros: count trailing zeros.
|
|
@end menu
|
|
|
|
Common Lisp doesn't directly expose all features present in
|
|
modern hardware. Some code patterns are recognized and turned into
|
|
more efficient hardware instructions without requiring the use of
|
|
internal features.
|
|
|
|
@node count trailing zeros
|
|
@subsection Count Trailing Zeros
|
|
|
|
@example
|
|
(defun ctz (n)
|
|
(declare (type (unsigned-byte 64) n))
|
|
(integer-length (ldb (byte 64 0) (lognor n (- n)))))
|
|
@end example
|
|
|
|
is turned into hardware instructions on arm64 and x86-64. It returns
|
|
64 when @code{n} is 0. @code{n} can also be @code{(signed-byte 64)} or @code{fixnum}.
|
|
|
|
@node global and always bound variables
|
|
@section Global and Always-bound Variables
|
|
|
|
@anchor{Macro sb-ext defglobal}
|
|
@ffindex @sortas{defglobal sb-ext} defglobal [sb-ext]
|
|
@deffn{Macro} sb-ext:defglobal name value &optional doc
|
|
Defines @code{name} as a global variable that is always bound. @code{value} is evaluated
|
|
and assigned to @code{name} both at compile- and load-time, but only if @code{name} is not
|
|
already bound.
|
|
|
|
Global variables share their values between all threads, and cannot be
|
|
locally bound, declared special, defined as constants, and neither bound
|
|
nor defined as symbol macros.
|
|
|
|
See also the declarations @code{sb-ext:global} and @code{sb-ext:always-bound}.
|
|
@end deffn
|
|
@anchor{Declaration sb-ext global}
|
|
@ddindex @sortas{global sb-ext} global [sb-ext]
|
|
@deffn{Declaration} sb-ext:global
|
|
Syntax: @code{(sb-ext:global &rest symbols)}
|
|
|
|
Only valid as a global proclamation.
|
|
|
|
Specifies that the named symbols cannot be proclaimed or locally
|
|
declared @code{special}. Proclaiming an already special or constant variable
|
|
name as @code{sb-ext:global} signal an error. Allows more efficient value
|
|
lookup in threaded environments in addition to expressing programmer
|
|
intention.
|
|
@end deffn
|
|
@anchor{Declaration sb-ext always-bound}
|
|
@ddindex @sortas{always-bound sb-ext} always-bound [sb-ext]
|
|
@deffn{Declaration} sb-ext:always-bound
|
|
Syntax: @code{(sb-ext:always-bound &rest symbols)}
|
|
|
|
Only valid as a global proclamation.
|
|
|
|
Specifies that the named symbols are always bound. Inhibits @code{makunbound}
|
|
of the named symbols. Proclaiming an unbound symbol as
|
|
@code{sb-ext:always-bound} signals an error. Allows the compiler to elide
|
|
boundness checks from value lookups.
|
|
@end deffn
|
|
@node miscellaneous efficiency issues
|
|
@section Miscellaneous Efficiency Issues
|
|
|
|
FIXME: The material in the CMUCL manual about getting good
|
|
performance from the compiler should be reviewed, reformatted in
|
|
Texinfo, lightly edited for SBCL, and substituted into this
|
|
manual. In the meantime, the original CMUCL manual is still 95+%
|
|
correct for the SBCL version of the Python compiler. See the
|
|
sections
|
|
|
|
@itemize
|
|
@item Advanced Compiler Use and Efficiency Hints
|
|
@item Advanced Compiler Introduction
|
|
@item More About Types in Python
|
|
@item Type Inference
|
|
@item Source Optimization
|
|
@item Tail Recursion
|
|
@item Local Call
|
|
@item Block Compilation
|
|
@item Inline Expansion
|
|
@item Object Representation
|
|
@item Numbers
|
|
@item General Efficiency Hints
|
|
@item Efficiency Notes
|
|
@end itemize
|
|
|
|
Besides this information from the CMUCL manual, there are a few other
|
|
points to keep in mind.
|
|
|
|
@itemize
|
|
@item The CMUCL manual doesn't seem to state it explicitly, but Python
|
|
has a mental block about type inference when assignment is
|
|
involved. Python is very aggressive and clever about inferring the
|
|
types of values bound with @code{let}, @code{let*}, inline function call, and so
|
|
forth. However, it's much more passive and dumb about inferring
|
|
the types of values assigned with @code{setq}, @code{setf}, and friends. It
|
|
would be nice to fix this, but in the meantime don't expect that
|
|
just because it's very smart about types in most respects it will
|
|
be smart about types involved in assignments. (This doesn't affect
|
|
its ability to benefit from explicit type declarations involving
|
|
the assigned variables, only its ability to get by without
|
|
explicit type declarations.)
|
|
@end itemize
|
|
|
|
@cindex garbage collector, generational
|
|
@cindex generational garbage collector
|
|
@itemize
|
|
@item Since the time the CMUCL manual was written, CMUCL (and thus SBCL)
|
|
has gotten a generational GC. This means that there are some
|
|
efficiency implications of various patterns of memory usage which
|
|
aren't discussed in the CMUCL manual. (Some new material should be
|
|
written about this.)
|
|
|
|
@item SBCL has some important known efficiency problems. Perhaps the
|
|
most important are
|
|
|
|
@itemize
|
|
@item The garbage collector is not particularly efficient, at least
|
|
on platforms without the generational collector (as of SBCL
|
|
0.8.9, all except x86).
|
|
|
|
@item Various aspects of the PCL implementation of CLOS are more
|
|
inefficient than necessary.
|
|
@end itemize
|
|
@end itemize
|
|
|
|
Finally, note that Common Lisp defines many constructs which, in the
|
|
infamous phrase, "could be compiled efficiently by a sufficiently
|
|
smart compiler". The phrase is infamous because making a compiler
|
|
which actually is sufficiently smart to find all these optimizations
|
|
systematically is well beyond the state of the art of current
|
|
compiler technology. Instead, they're optimized on a case-by-case
|
|
basis by hand-written code, or not optimized at all if the
|
|
appropriate case hasn't been hand-coded. Some cases where no such
|
|
hand-coding has been done as of SBCL version 0.6.3 include
|
|
|
|
@itemize
|
|
@item @code{(reduce #'f x)} where the type of @code{x} is known at compile time,
|
|
|
|
@item various bit vector operations, e.g. @code{(position 0 some-bit-vector)},
|
|
|
|
@item specialized sequence idioms, e.g. @code{(remove item list :count 1)},
|
|
|
|
@item cases where local compilation policy does not require excessive
|
|
type checking, e.g. @code{(locally (declare (safety 1)) (assoc item list))}
|
|
(which currently performs safe @code{endp} checking internal to @code{assoc}).
|
|
@end itemize
|
|
|
|
If your system's performance is suffering because of some construct
|
|
which could in principle be compiled efficiently, but which the SBCL
|
|
compiler can't in practice compile efficiently, consider writing a
|
|
patch to the compiler and submitting it for inclusion in the main
|
|
sources. Such code is often reasonably straightforward to write;
|
|
search the sources for the string @code{deftransform} to find many
|
|
examples (some straightforward, some less so).
|
|
|