Invert binary tree
65k-node balanced binary tree: allocator pressure, recursive descent, and pointer mutation in one inner loop. The only workload in the suite that stresses all three at once.
Runtime · median per inner-loop window
Full statistics
| Runner | N | Compile | Runtime | P95 | Stddev | RSS | vs pipit | Status |
|---|---|---|---|---|---|---|---|---|
| Native Gocompiled | 7 | 108 ms | 5.96 ms | 6.03 ms | 299 µs | 1.62 GiB | 0.10× | OK |
| Pipitbytecode VM | 7 | 1.17 ms | 61.2 ms | 78.7 ms | 6.05 ms | 592 MiB | 1.00× | OK |
| CPython 3.14bytecode VM | 7 | 354 µs | 80.4 ms | 81.9 ms | 977 µs | n/a | 1.31× | OK |
| PyPy 7.3tracing JIT | 7 | 285 µs | 42.9 ms | 45.3 ms | 1.82 ms | n/a | 0.70× | OK |
| tengobytecode VM | 7 | 230 µs | 180 ms | 205 ms | 14.6 ms | 1.69 GiB | 2.94× | OK |
| scriggobytecode VM | 0 | n/a | n/a | n/a | n/a | n/a | n/a | no data |
| mvmbytecode VM | 7 | 358 µs | 165 ms | 167 ms | 2.06 ms | 1.69 GiB | 2.70× | OK |
| yaegiAST walker | 7 | 359 µs | 574 ms | 606 ms | 14.0 ms | 1.69 GiB | 9.37× | OK |
Workload & symmetry rules
Workload
- Recursively construct a balanced binary tree of depth 15 (2¹⁶ − 1 = 65535 nodes). Each node's value is derived deterministically from its parent (
value*2for left,value*2+1for right, 32-bit masked). - Count nodes via recursive traversal (sanity check).
- Compute an in-order hash fold:
state = state*31 + valuemod 2³². - Invert the tree: at every node, swap
leftandrightpointers recursively. - Re-count and re-hash. Emit the four observables
pre_count,pre_fold,post_count,post_fold.
Symmetry rules
- Real recursive
*Nodestruct (Go) /Nodeclass with__slots__(Python). - No conversion to arrays for the inversion step, just a pointer swap at every node.
What makes this workload unique
Every other benchmark in the suite isolates one performance dimension. Bench 14 stresses pure float arithmetic in a tight loop. Bench 15 stresses interpreter throughput with no allocation. Bench 13 stresses interface dispatch with no mutation. This one combines three pressures into a single inner loop: 65k heap allocations during construction, four full recursive walks across the tree, and in-place pointer-field mutation during the inversion. The runner that wins this benchmark is the runner whose allocator, call-frame management, and pointer-write path are all simultaneously fast. The runner that loses it has a bottleneck in at least one of the three.
Source code
pipit / Go
pipit_source.gonative Go
native_main.goCPython / PyPy
cpython.pytengo
script.tengo