3 ms·
The notion that the overhead of interpreter dispatch dominates everything else (and thus the most focus should be placed on optimizing dispatch) was formed with
by cwzwarich 11y ago
The notion that the overhead of interpreter dispatch dominates everything else (and thus the most focus should be placed on optimizing dispatch) was formed with experiments against microarchitectures that are fairly primitive by current standards.
Earlier indirect branch predictors didn't take much execution history into account, mostly associating a predicted branch target with the address of a branch instruction. With a predictor of this kind, using a distinct branch instruction for distinct execution paths dramatically improves the amount of context available to the branch predictor. The same remains true if you add a small amount of context to the predictor.
Over time, conditional branch predictors incorporated more branch history, e.g. the various designs like OGEHL / TAGE based on associating a branch instruction with multiple branch histories whose lengths form a geometric series. The same trick was quite successful when applied to indirect branches, although it is more difficult to implement because the result of a prediction is an address rather than a single bit indicating the branch direction.
You can see evaluations of various benchmarks against both actual Intel processors and simulations of the leading academic branch predictor designs here:
https://hal.inria.fr/hal-01100647/document https://hal.inria.fr/hal-01100647/document
On Haswell and later (and with their model of ITTAGE) the mispredictions encountered by a switch-based interpreter loop are negligible.
That being said, I would still write an interpreter in assembly myself, because the code generators found in traditional compilers do a poor job of generating code for interpreters, and you can do a better job of deciding what work goes on the inline fast path and what goes on a slow path. When you're writing an interpreter by hand, you might as well use separate indirect branches for dispatch because there's no impedance mismatch like there is in C/C++.
- bogomipz 11y ago"because the code generators found in traditional compilers do a poor job of generating code for interpreters," Are you referring to the assembler in the GNU toolchaine or? Could you elaborate on this sentiment? Thanks.
- cwzwarich 11y agoI mean "code generator" as in the backend of a compiler that comes after most optimizations have been done. In particular, most common register allocators and schedulers are designed to optimize code that looks much different than an interpreter loop, and they generally do a poor job on interpreters.