If you're curious, you can find Inko's interpreter loop here: https://gitlab.com/inko-lang/inko/blob/master/vm/src/vm/mach...
This is the case with closure-based approach too, and indeed was one of the main motivations to switch away from AST interpretation.
Did you profile your bytecode implementation to see where the time is going?
[0]: https://en.wikipedia.org/wiki/Threaded_code#Subroutine_threa...
I would expect that the advantage of the closure version of your AST interpreter is coming from somewhere else (at the end of the day it is still an AST interpreter). One possibility is that you are passing around the operands to your filters as Rust lexically-closed values instead of using a custom stack for your interpreter, which makes things a bit more "statically typed".
In a pretty remote sense, I guess.
> One possibility is that you are passing around the operands to your filters as Rust lexically-closed values instead of using a custom stack for your interpreter, which makes things a bit more "statically typed".
Yes, that and using native dynamic dispatch instead of walking a tree structure with branching are making this technique much closer to template JITs than AST interpretation, with corresponding performance wins.
It's CPU dependent but in practice the cache benefits of packed instructions will probably dominate any misalignment penalty. It's not like x86 instructions are aligned after all.
What's the best way to implement a variable-width bytecode interpreter in Rust?
As for alignment - you can control it in Rust enums too, if you wish, by putting custom aligned structures as variant payloads.
In general, I'm sure it would be possible to achieve same (or, who knows, maybe even slightly better) performance than AST interpretation with manual tweaking, but the closure-based approach proved to give significant wins out of the box, while being more elegant to maintain and extend over time.
Has the Rust team considered that choosing cute names like "crate" might turn away some of the target audience?
The same applies to other ecosystems like homebrew, but in the case of Rust some of the target audience certainly includes C/C++ programmers.
Beyond that, “crate” specifically has utility. The closest existing name is “compilation unit,” but with things like incremental compilation, that’s a bit of a misnomer. We need some kind of name.
The "threaded" approach is for each bytecode instruction to complete by decoding and jumping to the handler for the next instruction.
Basically instead of "break" you have `goto handlers[nextIp->opcode].`
The advantages of threading are fewer jumps and better branch prediction (since branch prediction is tied to IP). The disadvantages are slightly larger code and compilers struggle to optimize it, since the control flow is not structured.
Here's a production version from OpenJ9's JVM ByteCode Interpreter. [2]
[1] https://kseo.github.io/posts/2017-01-09-continuation-passing...
[2] https://github.com/eclipse/openj9/blob/01be53f659a8190959c16...