Just-in-time (JIT) binding
REBOL 3.0 will introduce a new type of binding: just-in-time
(JIT) binding. This is a long article, but I thought you would
be interested in the details, and I know you will not be shy in
providing your comments.
Currently, REBOL supports two types of binding:
| Definitional Context Binding (DCB) | Binding that occurs when you
define a block of data as the body of a specific datatype or
context. This is the normal REBOL binding as implemented by the
bind function. For example, the variables of a function are bound
to the function body when the make function! operation is
performed (or mezz function like func, function, etc.)
|
| Late Path Binding (LPB) | Binding that occurs when you use a path
that contains symbolic fields. This very late binding occurs during
the evaluation of the path itself. This happens mainly when you
access the fields of an object, and it is done so late to allow the
fields to be variables themselves.
|
These binding methods work fine for normal REBOL code; however, when
processing dialects of REBOL, they provide no benefit because the words
of the dialect never undergo definitional processing. Dialects are
just blocks, not specialized datatypes such as functions or objects.
During the implementation of the Rebcode project (which created a unique
model for a high performance REBOL virtual machine), it became obvious
that a very fast lookup method was needed for the opcodes of the VM.
When you specify an add or sub you don't want the interpreter
wasting any time trying to figure out what those opcode actually
mean.
Two solutions became obvious: use DCB or come up with a new type of
late binding, which for lack of a better term I will call
just-in-time binding (JIT). Both methods were considered; however,
because Rebcode was embodied in a function wrapper, it had the
opportunity for definitional processing. In addition, modification
of Rebcode blocks is not allowed after definition. So DCB was
perfect and improved the speed of Rebcode by three or four times.
So, what about other REBOL dialects? Can the performance of dialects
like draw and parse be improved? Yes, they can. Take the draw
dialect as an example. The keywords (commands) of the dialect are
evaluated quickly, but for large blocks (such as a large SVG image),
performance could be better. A significant amount of time is
required resolving the keywords each time the draw block is
evaluated.
Rebcode was implemented using the technique where a dialect
context defined the words of the dialect, and DCB was used to
bind the dialect block to that context. There is nothing fancy here.
However, this DCB method does not work well for dialect blocks like draw,
which are allowed to change dynamically. For instance, during an animation the
programmer may insert or remove commands in/from the draw block.
Such changes would invalidate the work done by a DCB process, but
the alternative would be to burden the programmer with maintaining the
bindings manually during each insertion, and we would prefer to avoid that.
Here enters JIT binding. In JIT, words are bound to the context as
they are encountered during the evaluation stream. Unlike LPB, the
words of the block are modified to hold their new bindings. They
are sticky. The next time the block is evaluated only a quick
check of the word's context is required, and no other action is
needed. If a new word has been inserted, the check fails, and the
JIT bind occurs.
The result is that dialects like draw and parse can evaluate much
faster, but without the need to prebind the words of the dialect
block. And, if the block remains substantially un-modified, the
evaluation speed of the dialect is very close to that of pure REBOL
code (requiring only the extra context check for each word).
Is there a downside to this method? Yes. Good design is all about
managing tradeoffs. It takes a small amount of time to setup the JIT
bind method prior to dialect evaluation. For very short blocks that
contain less than three or four keywords, the overhead of the JIT
setup could slow those blocks down. This is the kind of thing we
will need to test and evaluate. There may also be a slight penalty
for exceptions (errors, breaks, returns, throws) that occur during
evaluation of JIT dialects; however, such events should be rare for
most dialected code.
In conclusion: JIT binding is really just a evaluation-time style
of definitional context binding. It is believed that this type of
binding could provide substantial additional performance for the
dialects used in draw, parse, and others. It is also possible
that we could use JIT in place of LPB to improve performance for
object field access. That must be studied.
Note: this concept is still in the early design stage, and we should
know more soon. If you have any comments on this idea, I know that
you will happily post them here. Thanks.
21 Comments
|