Home Expressions
Docs
Drivers Gateway SDKs Benchmarks
Changelog
GitHub
Blog Status Roadmap
← Back to benchmark index
Current Zig report April 22, 2026

QAIL Zig Benchmark Report

March 31, 2026 5-round median PostgreSQL matrix for qail-zig and pg.zig across the shared prepared-statement surface.

This is the canonical public benchmark page for qail-zig. It replaces the older narrow three-way prepared-point snapshot with the current matched Zig driver comparison: qail-zig versus pg.zig on single-connection and pool10 prepared execution. Use /zig for the product page, /zig/docs for setup and driver reference, and /benchmarks for the cross-runtime benchmark index.
9 / 10

shared throughput slices won by qail-zig in this published matrix (single aggregate favors pg.zig)

9 / 10
Slices won
single and pool10 across five workloads
2.27x
Strongest gain
point lookup in single mode: 49,070 q/s on qail-zig versus 21,597 q/s on pg.zig
161,884 q/s
Peak published throughput
point lookup in pool10 on qail-zig

Benchmark Surface

This page reports the clean shared public surface between qail-zig and pg.zig. The goal is an apples-to-apples driver comparison on the modes both projects present clearly.

AxisValue
Driversqail-zig versus pg.zig
qail-zig authoring surfaceNative QailCmd AST workloads compiled once to prepared SQL templates, then executed through qail-zig's prepared protocol path
pg.zig execution surfaceThe same prepared SQL templates executed through queryOpts(..., .{ .cache_name = ... }) so pg.zig stays on its normal cached prepared path
Workloadspoint, wide_rows, large_rows, many_params, aggregate
ModesShared single and pool10 only; pipeline is left off this page so the comparison stays on the clearest shared modes
Rounds5 interleaved rounds with median reporting
Harnesssrc/qail_pgzig_bench.zig in the qail-zig workspace
Seed tablezig_pg_driver_bench_payload with 50k rows and mixed text, numeric, boolean, and nullable columns

This report supersedes the older Zig page that mixed qail-zig with qail.rs and pgx on a narrow prepared-point story. The current page is the clean current qail-zig and pg.zig comparison.

5-Round Median Throughput

Higher is better. Cells show pg.zig, qail-zig, then the qail-zig/pg.zig ratio.

WorkloadSinglePool10
Point lookuppg.zig 21,597
qail-zig 49,070
2.27x
pg.zig 79,283
qail-zig 161,884
2.04x
Wide rowspg.zig 4,694
qail-zig 5,479
1.17x
pg.zig 16,638
qail-zig 19,154
1.15x
Large rowspg.zig 93.721
qail-zig 100.129
1.07x
pg.zig 349.533
qail-zig 387.407
1.11x
Many paramspg.zig 21,224
qail-zig 42,682
2.01x
pg.zig 78,086
qail-zig 157,960
2.02x
Aggregatepg.zig 291.296
qail-zig 253.936
0.87x
pg.zig 1,992.861
qail-zig 2,041.760
1.02x

qail-zig leads 9 of 10 published shared throughput cells in this matrix. The single aggregate slice currently favors pg.zig; pipeline remains intentionally off-page so the comparison stays on the shared public surface.

Throughput — Single Connection

5-round median queries per second on a single connection. Higher is better.

pg.zig qail-zig ↑ q/s
Point lookup Wide rows Many params Aggregate pg.zig: 21,597 q/s pg.zig: 4,694 q/s pg.zig: 21,224 q/s pg.zig: 291 q/s qail-zig: 49,070 q/s (2.27×) qail-zig: 5,479 q/s (1.17×) qail-zig: 42,682 q/s (2.01×) qail-zig: 254 q/s (0.87×)
Workload pg.zigqail-zig Ratio
Point lookup 22K 49K 2.27×
Wide rows 4.7K 5.5K 1.17×
Many params 21K 43K 2.01×
Aggregate 291 254 0.87×

Large rows excluded from this chart because the q/s scale differs by orders of magnitude (sub-100 q/s). See the table above for full data.

Throughput — Pool10

5-round median queries per second with 10 pooled connections. Higher is better.

pg.zig qail-zig ↑ q/s
Point lookup Wide rows Many params Aggregate pg.zig: 79,283 q/s pg.zig: 16,638 q/s pg.zig: 78,086 q/s pg.zig: 1,993 q/s qail-zig: 161,884 q/s (2.04×) qail-zig: 19,154 q/s (1.15×) qail-zig: 157,960 q/s (2.02×) qail-zig: 2,042 q/s (1.02×)
Workload pg.zigqail-zig Ratio
Point lookup 79K 162K 2.04×
Wide rows 17K 19K 1.15×
Many params 78K 158K 2.02×
Aggregate 2.0K 2.0K 1.02×

Rust-Style Units (qail-zig)

Same qail-zig medians translated to the rust-style format: q/s, us/query, ns/query.

WorkloadSinglePool10
Point lookup49,070 q/s
20.38 us/query
20,379 ns/query
161,884 q/s
6.18 us/query
6,177 ns/query
Wide rows5,479 q/s
182.52 us/query
182,515 ns/query
19,154 q/s
52.21 us/query
52,208 ns/query
Large rows100.129 q/s
9,987.12 us/query
9,987,116 ns/query
387.407 q/s
2,581.26 us/query
2,581,256 ns/query
Many params42,682 q/s
23.43 us/query
23,428 ns/query
157,960 q/s
6.33 us/query
6,330 ns/query
Aggregate253.936 q/s
3,937.99 us/query
3,937,990 ns/query
2,041.760 q/s
489.77 us/query
489,774 ns/query

Formula: us/query = 1,000,000 / q/s, ns/query = 1,000,000,000 / q/s.

Workload Definitions

  • point is the tiny point lookup path: narrow row, one parameter, minimal server work.
  • wide_rows pulls back medium result sets with wide mixed rows to expose receive/decode and materialization overhead.
  • large_rows increases row counts into the 10k to 40k range to push the large-result receive path harder.
  • many_params stresses bind handling with sixteen integer parameters in the same prepared statement.
  • aggregate is the more server-heavy slice; it compresses client gaps because PostgreSQL itself dominates more of the total work.

Interpretation

The current matched Zig matrix stays strongly positive for qail-zig: it leads 9 / 10 shared throughput slices, with the largest gains on point and many_params. Those are the slices that expose dispatch and bind-path overhead most directly, and qail-zig remains clearly ahead there in both single and pool10.

The important exception is single/aggregate, where pg.zig currently leads by about 12.8%. That is not a contradiction; it is useful signal. Aggregate-heavy single-connection work shifts more weight to server-side execution and result handling details, and pg.zig remains a serious baseline on that slice. In pool10/aggregate, qail-zig returns to a small lead.

The methodology remains strict and readable: qail-zig workloads are authored as native QailCmd ASTs, compiled once to prepared SQL templates for statement preparation, then executed through qail-zig's prepared protocol path. pg.zig executes those same SQL templates through its cached prepared-query path. PostgreSQL statement boundaries stay aligned, so differences are easier to interpret.

Methodology Notes

  • qail-zig uses a warm prepared named-statement path after one warmup pass per slice.
  • pg.zig uses queryOpts(..., .{ .cache_name = ... }) so its cached prepared path is also warm before timing starts.
  • Both sides operate against the same seeded table and the same SQL template after qail-zig's AST compilation step.
  • The pool10 mode uses ten connections on both sides with equal work division per worker.
  • The page publishes medians from a local March 31, 2026 run. As of the v0.8.0 Zig 0.16 branch, pgzig-bench is temporarily disabled until the pg.zig dependency build script is updated, so this remains the latest published matrix.

Reproduce

These are the commands behind the current qail-zig versus pg.zig report.

git clone https://github.com/qail-io/qail-zig.git
cd qail-zig

# one slice
./scripts/zigw pgzig-bench qail single --workload point
./scripts/zigw pgzig-bench pgzig single --workload point

# full 5-round matrix
out=/tmp/qail_pgzig_matrix_round5.csv
printf 'runner,mode,workload,round,qps\n' > "$out"
for mode in single pool10; do
  for workload in point wide_rows large_rows many_params aggregate; do
    for runner in qail pgzig; do
      for round in 1 2 3 4 5; do
        qps=$(./scripts/zigw pgzig-bench "$runner" "$mode" --workload "$workload" --plain)
        printf '%s,%s,%s,%s,%s\n' "$runner" "$mode" "$workload" "$round" "$qps" >> "$out"
      done
    done
  done
done