4 ms·
My colleagues and I have developed a technique that addresses a lot of the issues Mike discusses in that link. In this blog article explaining the technique, I
by haberman 5y ago
My colleagues and I have developed a technique that addresses a lot of the issues Mike discusses in that link. In this blog article explaining the technique, I included an example that nearly matches Mike's hand-coded assembly from your link: https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreters.html https://blog.reverberate.org/2021/04/21/musttail-efficient-i...
In my experience, the weakest point of modern optimizing compilers is the register allocator. As Mike mentioned in his link, in functions with loops that contain complex control flow, the register allocation just falls apart. Too often you see the register allocator forcing a spill on the fast path that was caused by code in a fallback path. You can sprinkle __builtin_expect(cond, 0) on all your guards until the cows come home but the register allocator still sees a big complicated connected graph and optimizes the whole thing together.
The technique described in my link above works around this by basically forcing a specific register allocation at each "operation" boundary, putting fallback paths in separate functions entirely (as much as possible), so that they will be considered totally distinct to the optimizer. This isn't a perfect solution, but it helps a lot.
My overall view is that you can rarely beat the compiler in straight-line code. It's mainly in loops with complex control flow where the compiler's code quality suffers.
- pjscott 5y agoThat technique is beautiful! And the results are as good as I would expect, once you’ve given the compiler the information it needs to do a good job. I’ll keep this one in mind next time I need to clearly delineate the fast path from the fallback path and I’m using something with access to the musttail attribute.