3 ms·
I simulated yours vs the UPB arena fast path: https://godbolt.org/z/1oTrv1Y58 https://godbolt.org/z/1oTrv1Y58 Messing with it a bit, it seems like yours has a
by charleslmunger 1y ago
I simulated yours vs the UPB arena fast path:
https://godbolt.org/z/1oTrv1Y58 https://godbolt.org/z/1oTrv1Y58
Messing with it a bit, it seems like yours has a slightly shorter dependency chain due to loading the two members separately, where UPB loads them as a pair (as it needs both in order to determine how much size is available). Also seems to have less register pressure. I think that's because yours bumps down. UPB's supports in place forward extension, so it needs to bump up.
If you added branch hints to signal to the compiler that your slow path is not often hit, you might see some improvement (although if you have PGO it should already do this). These paths could also be good candidates for the `preserve_most` calling convention.
However, there is an unfortunate compiler behavior here for both implementations - it doesn't track whether the slow path (which is not inlined, and clobbers the pointers) was actually hit, so it reloads on the hot path, for both approaches. Unfortunately this means that a sequence of allocations will store and load the arena pointers repeatedly, when ideally they'd keep the current position in a register on the hot path and refill that register after clobbering in the cold path.
- kragen 1y agoThank you very much! I vaguely remember that it did that, and the failure to keep the pointer in registers might explain why PyPy's version is twice as fast (?).