Skip to content

Performance ​

This document summarizes the current local aq vs jq benchmark results for the harvested upstream jq direct-success test corpus.

The raw machine-generated artifacts live in the repository at:

  • benchmarks/jq-upstream-benchmark.md
  • benchmarks/jq-upstream-benchmark.json

Setup ​

  • Sources: jq upstream tests/base64.test, tests/jq.test, tests/man.test, tests/manonig.test, tests/onig.test, tests/optional.test, and tests/uri.test
  • Harvested direct-success cases: 836
  • Comparable cases in this workspace: 833
  • aq binary: target/release/aq
  • jq binary: local jq master build at tmp/jq-master/build/install-pure/bin/jq
  • jq version: jq-master-69785bf-dirty
  • Warmup runs per case: 1
  • Measured runs per case: 3
  • Per-run timeout: 10.0s

The benchmark command is:

bash
python3 scripts/jq_upstream_benchmark.py \
  --jq-binary "$PWD/tmp/jq-master/build/install-pure/bin/jq"

Use an absolute jq path here. The harness changes working directories while it runs module and fixture cases.

Headline ​

On the latest local jq-master run, aq is broadly at parity with jq and slightly ahead on aggregate.

Aggregate results from the saved report:

  • Compared cases: 833
  • aq faster cases: 411
  • jq faster cases: 185
  • Roughly equal cases: 237
  • Uncomparable cases: 3
  • Sum of jq medians: 1.343s
  • Sum of aq medians: 1.261s
  • Median aq/jq ratio: 0.95x
  • Geometric mean aq/jq ratio: 0.96x

Interpretation:

  • Ratios below 1.00x mean aq is faster.
  • Ratios above 1.00x mean aq is slower.

The current release build is in the same performance tier as jq master on this corpus, with a small overall aggregate edge to aq.

Startup ​

Focused local one-shot measurements against the same jq-master build came out to:

  • aq -n null: about 1.30ms
  • jq -n null: about 1.50ms
  • aq . --compact on {"a":1}: about 1.32ms
  • jq . -c on {"a":1}: about 1.35ms

So startup is also essentially at parity, with aq slightly ahead in these small local checks.

Notable Wins ​

The biggest speedups in the current run were:

  • jq case #391, datetime roundtrip pipeline: jq 27.34ms, aq 2.21ms, 0.08x
  • jq case #512, deep tojson / fromjson / flatten case: jq 11.36ms, aq 2.71ms, 0.24x
  • jq case #513, deep tojson and fromjson depth-limit case: jq 5.21ms, aq 2.86ms, 0.55x

These are real wins, but they are no longer representative of the whole story. Against jq master, most of the suite is clustered much closer to parity.

Notable Slowdowns ​

The largest remaining slowdowns in the current run were:

  • jq case #104, destructuring swap: jq 1.46ms, aq 2.23ms, 1.52x
  • manonig.test case #8, regex global match: jq 1.38ms, aq 1.93ms, 1.40x
  • jq case #468, try input catch .: jq 1.57ms, aq 2.16ms, 1.38x

These are still small absolute differences, but they are the main places where plain jq master is currently tighter.

Heaviest Cases ​

The heaviest cases called out in the saved report were:

  1. jq case #512reduce range(9999) as $_ ([];[.]) | tojson | fromjson | flatten jq 11.36ms, aq 2.71ms, 0.24x

  2. jq case #513reduce range(10000) as $_ ([];[.]) | tojson | try (fromjson) catch . | (contains("<skipped: too deep>") | not) and contains("Exceeds depth limit for parsing") jq 5.21ms, aq 2.86ms, 0.55x

  3. jq case #391last(range(365 * 67)|("1970-03-01T01:02:03Z"|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime) + (86400 * .)|strftime("%Y-%m-%dT%H:%M:%SZ")|strptime("%Y-%m-%dT%H:%M:%SZ")) jq 27.34ms, aq 2.21ms, 0.08x

The deep JSON cases are still where most of the absolute time is, even though two of the three are already wins.

Caveats ​

This benchmark compares aq against a local jq-master build, not against the older local jq 1.8.1 baseline used earlier in the project.

There are 3 uncomparable cases in this workspace. In those cases, the local jq-master build failed while aq produced the expected upstream result:

  • modulemeta
  • modulemeta | .deps | length
  • modulemeta | .defs | length

The saved benchmark therefore represents:

  • a strict local aq vs local jq-master performance comparison
  • on the harvested upstream direct-success corpus
  • with three jq-master local-build exceptions in the module metadata slice

Current Read ​

Current assessment:

  • aq is competitive with a current jq-master build on the harvested upstream corpus
  • overall performance is near parity, with a modest aggregate edge to aq
  • remaining performance work is targeted rather than structural

The best remaining optimization targets are:

  • deep tojson and fromjson workloads, especially jq case #512
  • low-millisecond overhead in simple map and arithmetic pipelines
  • module metadata behavior, if we want cleaner apples-to-apples comparison on the last uncomparable cases

Structured querying, rewriting, and Starlark scripting across common data formats.