At the core of an interpreter is a loop that iterates over instructions and executes them in order. This requires dispatching: based on the current instruction it needs to select different code. A fast interpreter requires a fast instruction dispatcher, but so does everything else that needs to switch over a fixed set of different options.
This talk investigates various dispatching techniques, from simple switch statements to jump tables. We’ll look at performance analysis tools, benchmarks, and lots and lots of assembly code, in order to learn ways to trick the compiler into generating the assembly code that we actually want.
Even if you don’t need to actually write an interpreter or other dispatcher, you will learn a lot about optimization.
Source Code
Interpreter implementations available here: https://gist.github.com/foonathan/f034c74feb6f78e867e596a362ecdb3c
Link main.cpp
against one of the vm_*.cpp
. Code requires C++17 and clang.
References
- Virtual Machine Showdown: Stack versus Registers, Section 3.3: Dispatch Cost Reduction Techniques
- Mike Pall’s email - Why LuaJIT’s interpreter is written in assembly
See Also
- lauf bytecode interpreter: github.com/foonathan/lauf
- Parsing Protobuf at 2+GB/s: How I Learned To Love Tail Calls in C