Closures pipeline
Map/Filter/Map/Filter/Reduce over 100k integers, every stage a closure built by a factory function.
Runtime · median per inner-loop window
Full statistics
| Runner | N | Compile | Runtime | P95 | Stddev | RSS | vs piko | Status |
|---|---|---|---|---|---|---|---|---|
| Native Gocompiled | 10 | 180 ms | 2.36 ms | 2.46 ms | 99.6 µs | 68 MiB | 0.02× | OK |
| Piko interpbytecode VM | 10 | 1.31 ms | 150 ms | 150 ms | 485 µs | 173 MiB | 1.00× | OK |
| CPython 3.13bytecode VM | 10 | 387 µs | 115 ms | 120 ms | 2.67 ms | n/a | 0.77× | OK |
| PyPy 7.3tracing JIT | 10 | 308 µs | 22.0 ms | 22.7 ms | 399 µs | n/a | 0.15× | OK |
| tengobytecode VM | 10 | 271 µs | 242 ms | 254 ms | 17.7 ms | 2.69 GiB | 1.62× | OK |
| scriggobytecode VM | 10 | 395 µs | 148 ms | 152 ms | 9.41 ms | 279 MiB | 0.99× | OK |
| mvmbytecode VM | 10 | 482 µs | 179 ms | 205 ms | 12.1 ms | 66 MiB | 1.20× | OK |
| yaegiAST walker | 10 | 549 µs | 810 ms | 837 ms | 22.7 ms | 74 MiB | 5.40× | OK |
Workload & symmetry rules
Workload
Generate 100,000 uint32 values from an LCG. Pipe them through five stages, each a closure built by a factory function that captures one constant:
applyMap(makeMultiply(3)), capturing factor 3applyFilter(makeDivisorFilter(7)), capturing divisor 7applyMap(makeAdd(1234)), capturing offset 1234applyFilter(makeThresholdFilter(1000)), capturing threshold 1000applyReduce(makeFold(5)), capturing shift 5
Symmetry rules
- Every stage is a real closure with captured state (not a top-level function).
- Pipeline composition through ordinary function calls, no tail-call tricks.
Why this benchmark exists
Measures first-class-function dispatch under load. Every element walks through five closure invocations. Closure allocation and upvalue access dominate the inner loop.
Source code
piko / Go
piko_source.gonative Go
native_main.goCPython / PyPy
cpython.pytengo
script.tengo