by MatthiasPortzel
6 subcomments
- It's crazy that this post seems to have stumbled across an equivalent to the Copy-and-Patch technique[0] used to create a Lua interpreter faster than LuaJit[1]
[0]: https://sillycross.github.io/2023/05/12/2023-05-12/
[1]: https://sillycross.github.io/2022/11/22/2022-11-22/
The major difference is that LuaJIT Remake's Copy-and-Patch requires "manually" copying blocks of assembly code and patching values, while this post relies on the Go compiler's closures to create copies of the functions with runtime-known values.
I think there's fascinating processing being made in this area—I think in the future this technique (in some form) will be the go-to way to create new interpreted languages, and AST interpreters, switch-based bytecode VMs, and JIT compilation will be things of the past.
by cosmos0072
1 subcomments
- Compiling an expression to a tree of closures, and a list of statements to a slice of closures, is exactly how I optimized [gomacro](https://github.com/cosmos72/gomacro) my Go interpreter written in go.
There are more tricks available there, as for example unrolling the loop that calls the list of closures, and having a `nop` closure that is executed when there's nothing to run but execution is not yet at the end of the the unrolled loop.
- Compiling queries is one of those things that is both Make it Right and Make it Fast.
Because bind variables and Prepared Statements go hand in hand, you want to do everything you can to coax your users into doing the right thing. If each unique query has to be compiled before first use, that’s an extra inducement to using prepared statements properly.
by brianolson
0 subcomment
- I built a 'slice of function pointers' bytecode interpreter in Go in 2019 for the Algorand VM (Blockchain smart contract stuff) and before that the same pattern in C for a toy JVM around 2005.
It's a good pattern!
The Algorand VM was focused on low overhead running thousands of tiny programs per second. Version 1 had no loops and a 1000 instruction limit.
The JVM was focused on low memory, towards possible embedded microcontroller use.
So, 'array of function pointers' is nothing new, but it is a good pattern.
- The resulting VM implementation reminds me of this post from cloudflare where they similarly use closures to build their interpreter.
https://blog.cloudflare.com/building-fast-interpreters-in-ru...
by giovannibonetti
0 subcomment
- The article mentions the struggle to speed up expression computation on MySQL due to its dynamic typing. I wonder if Postgres' more static type system works better in that regard.
by returningfory2
0 subcomment
- Really interesting technique. I guess one of the (theoretical?) drawbacks is the memory usage is not ideal? In a traditional bytecode compiler the result of compilation is a very-cache-friendly binary array, whereas here it looks like a vector of pointers to heap allocated closures.
by politician
5 subcomments
- It would be ideal if Go would add support for computed goto, so that we could build direct threaded interpreters.
- This is very interesting article! In https://expr-lang.org we did our own research and a paper about comparison on different vm implementation in go:
- https://expr-lang.org/blog/exploring-virtual-machines-for-em...
by djwatson24
0 subcomment
- Marc feeley wrote “using closures for code generation” way back in 1987. Everything old is new again!
It’s quite nice and only a small bit more code than an ast interpreter - but not as fast as a luajit- style tail calling bytecode vm, nor a baseline jit like copy and patch. It works wonders in languages that support closures though.
https://www-labs.iro.umontreal.ca/~feeley/papers/FeeleyLapal...
- This looks very similar to how "basic" emulators work.
by rajandatta
0 subcomment
- Nice article. Instructive to see the latest trends and direction for high performance interpreters. Thanks for sharing.
- [flagged]
- vitess is not the answer