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.
| Axis | Value |
|---|---|
| Drivers | qail-zig versus pg.zig |
| qail-zig authoring surface | Native QailCmd AST workloads compiled once to prepared SQL templates, then executed through qail-zig's prepared protocol path |
| pg.zig execution surface | The same prepared SQL templates executed through queryOpts(..., .{ .cache_name = ... }) so pg.zig stays on its normal cached prepared path |
| Workloads | point, wide_rows, large_rows, many_params, aggregate |
| Modes | Shared single and pool10 only; pipeline is left off this page so the comparison stays on the clearest shared modes |
| Rounds | 5 interleaved rounds with median reporting |
| Harness | src/qail_pgzig_bench.zig in the qail-zig workspace |
| Seed table | zig_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.
| Workload | Single | Pool10 |
|---|---|---|
| Point lookup | pg.zig 21,597 qail-zig 49,070 2.27x | pg.zig 79,283 qail-zig 161,884 2.04x |
| Wide rows | pg.zig 4,694 qail-zig 5,479 1.17x | pg.zig 16,638 qail-zig 19,154 1.15x |
| Large rows | pg.zig 93.721 qail-zig 100.129 1.07x | pg.zig 349.533 qail-zig 387.407 1.11x |
| Many params | pg.zig 21,224 qail-zig 42,682 2.01x | pg.zig 78,086 qail-zig 157,960 2.02x |
| Aggregate | pg.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.
| Workload | pg.zig | qail-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.
| Workload | pg.zig | qail-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.
| Workload | Single | Pool10 |
|---|---|---|
| Point lookup | 49,070 q/s 20.38 us/query 20,379 ns/query | 161,884 q/s 6.18 us/query 6,177 ns/query |
| Wide rows | 5,479 q/s 182.52 us/query 182,515 ns/query | 19,154 q/s 52.21 us/query 52,208 ns/query |
| Large rows | 100.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 params | 42,682 q/s 23.43 us/query 23,428 ns/query | 157,960 q/s 6.33 us/query 6,330 ns/query |
| Aggregate | 253.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
pointis the tiny point lookup path: narrow row, one parameter, minimal server work.wide_rowspulls back medium result sets with wide mixed rows to expose receive/decode and materialization overhead.large_rowsincreases row counts into the 10k to 40k range to push the large-result receive path harder.many_paramsstresses bind handling with sixteen integer parameters in the same prepared statement.aggregateis 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
pool10mode 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-benchis 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