Polymorphic AST eval
Interface / virtual-method dispatch through nine AST node types under heavy traversal load.
Runtime · median per inner-loop window
Full statistics
| Runner | N | Compile | Runtime | P95 | Stddev | RSS | vs pipit | Status |
|---|---|---|---|---|---|---|---|---|
| Native Gocompiled | 7 | 108 ms | 2.06 ms | 2.07 ms | 7.40 µs | 1.62 GiB | 0.06× | OK |
| Pipitbytecode VM | 7 | 1.92 ms | 31.9 ms | 33.8 ms | 741 µs | 591 MiB | 1.00× | OK |
| CPython 3.14bytecode VM | 7 | 742 µs | 51.9 ms | 52.7 ms | 553 µs | n/a | 1.63× | OK |
| PyPy 7.3tracing JIT | 7 | 555 µs | 86.2 ms | 100 ms | 6.82 ms | n/a | 2.70× | OK |
| tengobytecode VM | 7 | 344 µs | 119 ms | 125 ms | 3.01 ms | 1.69 GiB | 3.73× | OK |
| scriggobytecode VM | 0 | n/a | n/a | n/a | n/a | n/a | n/a | unsupported |
| mvmbytecode VM | 7 | 620 µs | 240 ms | 256 ms | 6.40 ms | 1.69 GiB | 7.52× | OK |
| yaegiAST walker | 7 | 654 µs | 1.10 s | 1.13 s | 21.8 ms | 1.69 GiB | 34.5× | OK |
Workload & symmetry rules
Workload
Build an expression tree from a deterministic LCG that mixes nine node kinds (Add, Sub, Mul, Mod, Const, Var, IfPos, Min, Max), each as a distinct struct/class satisfying a single-method contract: Node.Eval(env) uint32. Walk the tree repeatedly and accumulate the fold.
Symmetry rules
- Real interface dispatch in Go (
Eval(env)method on aNodeinterface) and class-based duck typing in Python. - No tagged-union switch shortcut; every Eval call must go through dynamic dispatch.
Why this benchmark exists
A direct measurement of the interpreter's interface / virtual-method dispatch path. Bench 05 is similar but uses a tagged-int switch; this one forces real virtual dispatch.
Source code
pipit / Go
pipit_source.gonative Go
native_main.goCPython / PyPy
cpython.pytengo
script.tengo