purejq 0.2.0 — static binding, fast paths, real benchmarks
A second performance round on top of 0.1.1, plus a benchmark suite that measures purejq against both the jq PyPI package (C bindings) and the jq binary.
Headline numbers
Embedded in Python (100k objects, in-process) — purejq is 6–40x faster than the jq C-binding package, which pays a full JSON serialization round-trip per call:
| workload | purejq | jq PyPI (C bindings) |
|---|---|---|
| filter + count | 56 ms | 485 ms |
| group_by | 114 ms | 765 ms |
| transform + sort | 141 ms | 943 ms |
End-to-end CLI (93 MB file, 1M objects) — purejq beats the C jq binary on parse-dominated runs:
| workload | purejq | jq 1.8 |
|---|---|---|
| single lookup | 0.5 s | 1.6 s |
| filter + count | 1.1 s | 2.0 s |
| group_by | 2.3 s | 4.0 s |
PyPy: another 2–9x on top (map+aggregate: 2 ms).
Engine changes
- Static call binding: names never redefined in a program are resolved to their prelude/Python builtin at compile time
- Single-output fast paths: constants, variables, field chains and arithmetic over them compile to plain callables; binops, object literals, pipes, and builtin arguments (
select/map/sort_bykeys) skip generator machinery reduce/foreachreuse one environment per fold
Conformance unchanged: 751/781 (96.2%) of jq's official test suite. Run the benchmarks yourself: python3 tools/bench.py 1000000.