Skip to content

feat(bench): Group B workloads — W7..W9 (sub-phase 9.3)#104

Merged
joaoh82 merged 1 commit into
mainfrom
bench-9.3-group-b
May 7, 2026
Merged

feat(bench): Group B workloads — W7..W9 (sub-phase 9.3)#104
joaoh82 merged 1 commit into
mainfrom
bench-9.3-group-b

Conversation

@joaoh82

@joaoh82 joaoh82 commented May 7, 2026

Copy link
Copy Markdown
Owner

Summary

Sub-phase 9.3 of docs/benchmarks-plan.md. Builds on #102 (9.1 harness + W1) and #103 (9.2 Group A) to land the SQL-feature-scaling workloads:

ID Shape Status
W7 SELECT SUM(v) FROM big over 1M rows
W8 SELECT k, COUNT(*) FROM big GROUP BY k at 10 / 1k / 100k cardinalities ✅ (SQLRite × 100k skipped — see below)
W9 INNER JOIN customers ⨝ orders, per-PK probe ✅ (scaled-down dataset — see below)

Smoke-run numbers (M1 Pro, criterion --warm-up-time 1 --measurement-time 1 --sample-size 10)

Workload SQLRite SQLite Ratio
W7 sum (1M rows) ~111 ms ~31 ms ~3.5×
W8 GROUP BY card-10 ~204 ms ~460 ms SQLRite wins (small samples)
W8 GROUP BY card-1k ~1.50 s ~242 ms ~6.2×
W8 GROUP BY card-100k skipped ~241 ms
W9 INNER JOIN (10k×10k) ~32 s ~2.3 µs ~14,000,000× ⚠️

W7's ~3.5× is the closest SQLRite has come to SQLite on any workload — the executor's per-row "touch" cost is tighter than the per-iter parse cost that dominates W1 / W6. Encouraging signal.

Two plan deviations, both documented + tracked

W8 × 100k-cardinality on SQLRite — skipped by default

A first measurement clocked ~245 s per iteration (~41 min for 10 samples) on SQLRite. Set SQLRITE_BENCH_W8_CARD_100K_SQLRITE=1 to re-enable once fixed. SQLite runs the same bucket fine.

The 245 s ≈ 1M × 100k = 1e11 ops timing strongly suggests an O(n × cardinality) GROUP BY path — likely a Vec-backed group store doing linear scans per row instead of an O(1) hash lookup. SQLR-19 tracks the investigation.

W9 dataset scaled down: 100k rows → 10k rows

Plan target was two 100k-row tables. A first smoke ran 88 minutes on SQLRite without producing a single sample before being killed. SQLRite's join executor (src/sql/executor.rs::execute_select_rows_joined) is a left-folded nested-loop driver with no inner-side index probe — so per-PK probe walks every row in orders. SQLR-20 tracks the planner / index-pushdown fix.

At the reduced 10k scale, the gap is still ~14 million×:

  • SQLRite: ~32 s / probe (10k inner-row scan, with ~3 ms per row of per-pair overhead)
  • SQLite: ~2.3 µs / probe (indexed nested-loop)

That ~3 ms/row is 3,000× slower than W2's full-scan rate (1 µs/row). Two unlocks: (a) push the ON predicate into an index probe, (b) trim the per-pair scope-construction overhead.

Bumping W9 back to 100k follows the SQLR-20 fix + a W9.v2 tag. Plan deviation captured in benchmarks/src/data.rs (JOIN_ROW_COUNT const + comment) and benchmarks/src/workloads/join.rs (top-of-file "Plan deviation" section).

Test plan

  • cargo fmt --all -- --check clean
  • cargo clippy -p sqlrite-benchmarks --all-targets clean
  • Full CI-equivalent: cargo build / test / doc --workspace --exclude {desktop,python,nodejs,benchmarks} green
  • cargo test -p sqlrite-benchmarks — 5 tests pass (4 inliner + 1 ymd round-trip)
  • Smoke cargo bench -p sqlrite-benchmarks --bench suite -- --warm-up-time 1 --measurement-time 1 '^W[789]' runs all three workloads end-to-end on both engines (with documented skips)
  • Aggregator post-processes the criterion output into a 9-row JSON envelope (W7×2, W8×4 with SQLRite × 100k absent, W9×2)
  • W7 / W8 / W9 correctness gates pass on both drivers

Follow-ups

  • SQLR-19 — fix W8 GROUP BY high-cardinality blowup. Likely a VecHashMap group store swap.
  • SQLR-20 — push JOIN ON predicate into inner-side index probe. The single-table executor already has the <col> = <literal> fast-path; the join executor needs to reuse it.
  • SQLR-18 (carried from 9.2) — investigate W4 single-row INSERT gap.
  • 9.4 — Group C differentiators (W10 vector top-10, W11 BM25 top-10, W12 hybrid retrieval). Phase 7d / 8 surface validation.

References

🤖 Generated with Claude Code

Lands the SQL-feature-scaling workloads on top of the 9.2 Group A
baseline:

  W7 aggregate    SELECT SUM(v) FROM big over 1M rows.
                  Single-statement full-scan + accumulator.
  W8 group-by     SELECT k, COUNT(*) FROM big GROUP BY k at three
                  cardinalities (10 / 1k / 100k distinct groups).
                  Each cardinality is its own criterion group.
  W9 inner-join   customers ⨝ orders ON c.id = o.customer_id,
                  per-PK-probe shape, single matching order per
                  customer.

Two notable plan deviations land alongside the workloads, both
captured in code + README:

W8 / 100k-cardinality on SQLRite — skipped by default. A first
measurement clocked ~245 s per criterion iteration (~41 min for
10 samples), strongly suggesting an O(n × cardinality) GROUP BY
path. Setting `SQLRITE_BENCH_W8_CARD_100K_SQLRITE=1` re-enables
the bucket. SQLR-19 tracks the investigation. SQLite runs the
same bucket fine.

W9 dataset scaled 100k rows → 10k rows. Plan target was two
100k-row tables, but a first smoke ran 88 minutes on SQLRite
without producing a single sample. SQLRite's join executor
(`src/sql/executor.rs::execute_select_rows_joined`) is a
left-folded nested-loop driver with no inner-side index probe,
so per-PK probe walks every row in `orders`. SQLR-20 tracks
the planner / index-pushdown fix; bumping back to 100k follows
that + a `W9.v2` tag.

Smoke run (M1 Pro, criterion --warm-up-time 1 --measurement-time 1
--sample-size 10):

  W7         sqlrite ~111 ms / sqlite ~31 ms       — ~3.5x
  W8 card-10  sqlrite ~204 ms / sqlite ~460 ms     — sqlrite wins (small samples;
                                                     SQLite picks sort-then-group)
  W8 card-1k  sqlrite ~1.50 s / sqlite ~242 ms     — ~6.2x
  W8 card-100k sqlrite SKIPPED / sqlite ~241 ms    — ⚠ SQLR-19
  W9 (10k)   sqlrite ~32 s / sqlite ~2.3 us         — ~14_000_000x ⚠ SQLR-20

W7's ~3.5x is the *closest* SQLRite has come to SQLite on any
workload — the per-row "touch" cost in the executor is tighter
than the per-iter parse cost dominates W1 / W6. Encouraging
signal for the executor itself.

Verification:
  - cargo fmt --all -- --check clean
  - cargo clippy -p sqlrite-benchmarks --all-targets clean
  - Full CI-equivalent build / test / doc green
    (--exclude desktop / python / nodejs / benchmarks)
  - Smoke run end-to-end across W7..W9 + aggregator → JSON envelope

Refs: SQLR-16 (parent), SQLR-19 (W8 high-cardinality),
SQLR-20 (W9 join planner).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant