1. Every instruction results in functional call overhead, which is something that tail calls (with protect_none) avoid.
2. Because you are using closures the next instruction has to be heap allocated. I don’t know what closures look like in Go, but there will involve some indirection.
3. Every iteration of the loop requires a bounds check on ip.
Now, none of these may matter for SQL because as the blog post said, the instructions are quite large. And it already shows huge improvements over the AST interpreter. I am just curious how the switch would benchmark.
On an unrelated note. Using closures to write an interpreter brought to mind this talk https://m.youtube.com/watch?v=V8dnIw3amLA&list=PLFTr8ChfQg9t...