mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 11:50:28 +02:00
Update "Why a VM?"
* doc/ref/vm.texi (Why a VM?): Update.
This commit is contained in:
parent
f1b745eec5
commit
2018609a69
1 changed files with 40 additions and 35 deletions
|
@ -50,26 +50,28 @@ programs to Guile's VM.
|
||||||
@subsection Why a VM?
|
@subsection Why a VM?
|
||||||
|
|
||||||
@cindex interpreter
|
@cindex interpreter
|
||||||
For a long time, Guile only had an interpreter. Guile's interpreter
|
For a long time, Guile only had a Scheme interpreter, implemented in C.
|
||||||
operated directly on the S-expression representation of Scheme source
|
Guile's interpreter operated directly on the S-expression representation
|
||||||
code.
|
of Scheme source code.
|
||||||
|
|
||||||
But while the interpreter was highly optimized and hand-tuned, it still
|
But while the interpreter was highly optimized and hand-tuned, it still
|
||||||
performed many needless computations during the course of evaluating an
|
performed many needless computations during the course of evaluating a
|
||||||
expression. For example, application of a function to arguments
|
Scheme expression. For example, application of a function to arguments
|
||||||
needlessly consed up the arguments in a list. Evaluation of an
|
needlessly consed up the arguments in a list. Evaluation of an
|
||||||
expression always had to figure out what the car of the expression is --
|
expression like @code{(f x y)} always had to figure out whether @var{f}
|
||||||
a procedure, a memoized form, or something else. All values have to be
|
was a procedure, or a special form like @code{if}, or something else.
|
||||||
allocated on the heap. Et cetera.
|
The interpreter represented the lexical environment as a heap data
|
||||||
|
structure, so every evaluation caused allocation, which was of course
|
||||||
|
slow. Et cetera.
|
||||||
|
|
||||||
The solution to this problem was to compile the higher-level language,
|
The solution to the slow-interpreter problem was to compile the
|
||||||
Scheme, into a lower-level language for which all of the checks and
|
higher-level language, Scheme, into a lower-level language for which all
|
||||||
dispatching have already been done---the code is instead stripped to
|
of the checks and dispatching have already been done---the code is
|
||||||
the bare minimum needed to ``do the job''.
|
instead stripped to the bare minimum needed to ``do the job''.
|
||||||
|
|
||||||
The question becomes then, what low-level language to choose? There
|
The question becomes then, what low-level language to choose? There are
|
||||||
are many options. We could compile to native code directly, but that
|
many options. We could compile to native code directly, but that poses
|
||||||
poses portability problems for Guile, as it is a highly cross-platform
|
portability problems for Guile, as it is a highly cross-platform
|
||||||
project.
|
project.
|
||||||
|
|
||||||
So we want the performance gains that compilation provides, but we
|
So we want the performance gains that compilation provides, but we
|
||||||
|
@ -82,33 +84,36 @@ implement the virtual machine within Guile itself. Guile contains a
|
||||||
bytecode interpreter (written in C) and a Scheme to bytecode compiler
|
bytecode interpreter (written in C) and a Scheme to bytecode compiler
|
||||||
(written in Scheme). This way the virtual machine provides what Scheme
|
(written in Scheme). This way the virtual machine provides what Scheme
|
||||||
needs (tail calls, multiple values, @code{call/cc}) and can provide
|
needs (tail calls, multiple values, @code{call/cc}) and can provide
|
||||||
optimized inline instructions for Guile (@code{cons}, @code{struct-ref},
|
optimized inline instructions for Guile as well (GC-managed allocations,
|
||||||
etc.).
|
type checks, etc.).
|
||||||
|
|
||||||
So this is what Guile does. The rest of this section describes that VM
|
Guie also includes a just-in-time (JIT) compiler to translate bytecode
|
||||||
that Guile implements, and the compiled procedures that run on it.
|
to native code. Because Guile uses the portable GNU Lightning library
|
||||||
|
to emit that code, we keep the benefits of portability while also
|
||||||
|
benefitting from fast native code. To avoid too much time spent in the
|
||||||
|
JIT compiler itself, Guile is tuned to only emit machine code for
|
||||||
|
bytecode that is called often.
|
||||||
|
|
||||||
|
The rest of this section describes that VM that Guile implements, and
|
||||||
|
the compiled procedures that run on it.
|
||||||
|
|
||||||
Before moving on, though, we should note that though we spoke of the
|
Before moving on, though, we should note that though we spoke of the
|
||||||
interpreter in the past tense, Guile still has an interpreter. The
|
interpreter in the past tense, Guile still has an interpreter. The
|
||||||
difference is that before, it was Guile's main evaluator, and so was
|
difference is that before, it was Guile's main Scheme implementation,
|
||||||
implemented in highly optimized C; now, it is actually implemented in
|
and so was implemented in highly optimized C; now, it is actually
|
||||||
Scheme, and compiled down to VM bytecode, just like any other program.
|
implemented in Scheme, and compiled down to VM bytecode, just like any
|
||||||
(There is still a C interpreter around, used to bootstrap the compiler,
|
other program. (There is still a C interpreter around, used to
|
||||||
but it is not normally used at runtime.)
|
bootstrap the compiler, but it is not normally used at runtime.)
|
||||||
|
|
||||||
The upside of implementing the interpreter in Scheme is that we preserve
|
The upside of implementing the interpreter in Scheme is that we preserve
|
||||||
tail calls and multiple-value handling between interpreted and compiled
|
tail calls and multiple-value handling between interpreted and compiled
|
||||||
code. The downside is that the interpreter in Guile 2.2 is still about
|
code, and with advent of the JIT compiler in Guile 3.0 we reach the
|
||||||
twice as slow as the interpreter in 1.8. Since Scheme users are mostly
|
speed of the old hand-tuned C implementation; it's the best of both
|
||||||
running compiled code, the compiler's speed more than makes up for the
|
worlds.
|
||||||
loss. In any case, once we have native compilation for Scheme code, we
|
|
||||||
expect the self-hosted interpreter to handily beat the old hand-tuned C
|
|
||||||
implementation.
|
|
||||||
|
|
||||||
Also note that this decision to implement a bytecode compiler does not
|
Also note that this decision to implement a bytecode compiler does not
|
||||||
preclude native compilation. We can compile from bytecode to native
|
preclude ahead-of-time native compilation. More possibilities are
|
||||||
code at runtime, or even do ahead of time compilation. More
|
discussed in @ref{Extending the Compiler}.
|
||||||
possibilities are discussed in @ref{Extending the Compiler}.
|
|
||||||
|
|
||||||
@node VM Concepts
|
@node VM Concepts
|
||||||
@subsection VM Concepts
|
@subsection VM Concepts
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue