Near the beginning of most MIT-Scheme files you'll
see (declare (usual-integrations))
. This instructs the
SF program, which translates Scheme to S-code, to replace the
“usual” global variables with their (compile time)
values. You lose the ability to change the values of these
variables, but who redefines CAR
or CDR
?
(And if you do, just remove the declare
form or
add CAR
and CDR
to the exception
list.)
Now forms such as (null? x)
, which used to be syntaxed
into this S-code:
[funcall [global ’null?] [argument 0]]
are now syntaxed into this S-code:
[funcall [quote [primitive null?]] [argument 0]]
Recall that S-code is a tree representation of Lisp code that is
walked by the interpreter. The leaves of the tree are all
either quote
, global
, argument
,
or lexical
, and each of these only take one or two
machine instructions to eval. The “overhead” of
interpretation involves getting to the leaves.
The internal nodes of an S-code tree are the compound forms:
function calls and special forms. IF
and PROGN
(in Scheme BEGIN
) are the two
most frequently evaluated special forms. When we construct the
S-code for a compound form, we pattern match against the subforms
and specialize the S-code. Specialized S-code for a compound form
is called a “superoperator”. Superoperators eliminate
the eval dispatch for the subforms. The machine code for
interpreting a superoperator is close to the equivalent compiled
code. Let us examine some of the most important superoperators.
Funcall Argument List Elimination
In general, a function call accumulates a list of arguments and
jumps to APPLY
to dispatch the function. We can
eliminate the overhead of doing this. First, we can eliminate the
call to APPLY
by having applicable objects implement
the applicable
interface. We use virtual function
dispatch to invoke the code that implements the applicable object.
Applicable objects can be applied to any number of arguments passed
as a list. We can eliminate the overhead of manipulating short
argument lists by spreading the arguments and
adding call0
, call1
, call2
,
and call3
methods to the applicable
interface. The number of arguments at any particular call site is
fixed, and it is usually a small number. We eliminate
the overhead of accumulating the argument list for a function call
by specializing the funcall
S-code object on the number
of arguments.
An S-code funcall
is replaced by
a funcall0
, funcall1
, funcall2
,
funcall3
, or funcalln
depending on the
number of arguments. The argument accumulation loop is unrolled in
the numbered cases placing the arguments in local C# variables.
The funcallx
superoperators directly call the
appropriate calln
method in the function’s
applicable
interface.
Primitive Function Call
Function calls to primitive procedures are the foundation of Lisp and Scheme programs. We can look at a Lisp program as if it were a “black box” that makes a series of primitive function calls. If unoptimized, a Lisp program ought to make the same series of primitive function calls with the same values whether it is interpreted or compiled. The primitive function will run at the same speed no matter how it is called, so if compiled code is faster than interpreted, it is issuing the primitive function calls faster, taking less time between them. The time between issuing primitive function calls is the interpretation overhead, and we want to reduce that.
When we construct an S-code funcall
(and funcall0
, funcall1
, etc.), we
check if the thing being called is a literal quoted primitive
function, and if it is, we replace the S-code with
a primitive-funcall
(or primitive-funcall0
, primitive-funcall1
, etc.).
A primitive-funcall
evaluates its arguments, but it can
skip evaluating the function. It can skip the apply dispatch, too,
and just jump right to the primitive entry point.
We construct (null? arg)
as
[primitive-funcall1 [quote [primitive null?]] [argument 0]]
The MIT-Scheme interpreter and the MIT-Scheme hardware all special case function calls with small numbers of arguments and those which call primitive procedures.
In the next post, we develop this idea beyond what MIT-Scheme already does.
No comments:
Post a Comment