Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 31 additions & 9 deletions benchmarks/sql/distinct_by_order_take.das
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,35 @@ options gen2
options persistent_heap

require _common public
require daslib/sql
require sqlite/sqlite_boost
require sqlite/sqlite_linq

let TAKE_N = 10

// _distinct_by(_.dealer_id) |> _order_by(_.price) |> take(N) |> to_array — pick one car
// per dealer, sort by price, take top N (Theme 3 Phase 3, audit C1).
// SQL: no faithful translation — "pick one row per dealer" requires window functions
// (ROW_NUMBER OVER PARTITION BY dealer_id) which sqlite_linq does not lower. Follow-up
// TODO 2026-05-25.
// Array/Decs splice through plan_order_family / plan_decs_order_family's bounded-heap
// + set-gate path: declares `var dset : table<typedecl(...)>` above source loop, wraps
// per-element push by `if (!key_exists(dset, dkey)) { dset|>insert(dkey); HEAP_UPDATE }`.
// Single source pass, no full distinct materialization.
// `_distinct_by(_.dealer_id) |> _order_by(_.price) |> take(N) |> to_array` — pick first
// car per dealer (by source/PK order), sort by price, take top N.
// SQL (m1) — lowers via the bare-aggregate trick from PR #2906 + the Arm A passthrough
// composition: `SELECT … FROM (SELECT *, MIN(pk) FROM Cars GROUP BY dealer_id) AS t0
// ORDER BY price ASC LIMIT N`. See sqlite_linq.das::finalize_distinct_by_passthrough_wrap.
// Array/Decs (m3f/m4) splice through plan_order_family / plan_decs_order_family's
// bounded-heap + set-gate path (Theme 3 Phase 3).

def run_m1(b : B?; n : int) {
with_sqlite(":memory:") $(db) {
fixture_db(db, n)
b |> run("m1_sql/{n}", n) {
let rows <- _sql(db |> select_from(type<Car>)
|> _distinct_by(_.dealer_id)
|> _order_by(_.price)
|> take(TAKE_N))
b |> accept(rows)
if (empty(rows)) {
b->failNow()
}
}
}
}

def run_m3f(b : B?; n : int) {
let arr <- fixture_array(n)
Expand Down Expand Up @@ -41,6 +58,11 @@ def run_m4(b : B?; n : int) {
}
}

[benchmark]
def distinct_by_order_take_m1(b : B?) {
run_m1(b, 100000)
}

[benchmark]
def distinct_by_order_take_m3f(b : B?) {
run_m3f(b, 100000)
Expand Down
44 changes: 30 additions & 14 deletions benchmarks/sql/distinct_by_order_to_array.das
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,32 @@ options gen2
options persistent_heap

require _common public
require daslib/sql
require sqlite/sqlite_boost
require sqlite/sqlite_linq

// _distinct_by(_.dealer_id) |> _order_by(_.price) |> to_array — pick first car per
// dealer, sort by price. NO take suffix (compare against distinct_by_order_take,
// which adds `take(N)` and routes through emit_bounded_heap). n=100k, DEALER_COUNT=100
// → output is 100 rows. Theme 8 audit 3b (PR #2875).
// Both lanes splice through emit_fused_prefilter (linq_fold.das:2351) — single fused
// loop: per-element dset gate (`var order_dset : table<int>`) writes into fresh
// `buf`, then `order_inplace(buf, ...)` tail. Saves cascade's distinct_by_to_array
// intermediate iterator setup. Composes across source modes via the shared FoldArraySpec
// dispatch (PR F4) — same emit fn drives array (each(arr)) and decs
// (from_decs_template(...)) sources.
// SQL (m1) — same window-function story as distinct_by_order_take: "one row per
// dealer" needs `ROW_NUMBER() OVER (PARTITION BY dealer_id ORDER BY price) WHERE rn=1`.
// sqlite_linq does not currently lower window functions — see
// benchmarks/sql/sqlite_linq_gaps.md "window functions".
// `_distinct_by(_.dealer_id) |> _order_by(_.price) |> to_array` — pick first car per
// dealer (by source/PK order), sort by price. NO take suffix (compare against
// distinct_by_order_take, which adds `take(N)`). n=100k, DEALER_COUNT=100 → 100 rows.
// SQL (m1) — bare-aggregate trick + outer ORDER BY via Arm A passthrough (PR #2906's
// chunk N+1): `SELECT … FROM (SELECT *, MIN(pk) FROM Cars GROUP BY dealer_id) ORDER BY price ASC`.
// Array/Decs (m3f/m4) splice through emit_fused_prefilter (linq_fold.das:2351) — single
// fused loop with per-element dset gate then `order_inplace(buf, ...)` tail. Theme 8 3b.

def run_m1(b : B?; n : int) {
with_sqlite(":memory:") $(db) {
fixture_db(db, n)
b |> run("m1_sql/{n}", n) {
let rows <- _sql(db |> select_from(type<Car>)
|> _distinct_by(_.dealer_id)
|> _order_by(_.price))
b |> accept(rows)
if (empty(rows)) {
b->failNow()
}
}
}
}

def run_m3f(b : B?; n : int) {
let arr <- fixture_array(n)
Expand Down Expand Up @@ -44,6 +55,11 @@ def run_m4(b : B?; n : int) {
}
}

[benchmark]
def distinct_by_order_to_array_m1(b : B?) {
run_m1(b, 100000)
}

[benchmark]
def distinct_by_order_to_array_m3f(b : B?) {
run_m3f(b, 100000)
Expand Down
37 changes: 31 additions & 6 deletions benchmarks/sql/groupby_first.das
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,33 @@ options gen2
options persistent_heap

require _common public
require daslib/sql
require sqlite/sqlite_boost
require sqlite/sqlite_linq

// _group_by(_.brand) |> _select((Brand=key, FirstCar=first source elem per group)).
// SQL equivalent would need a window function (`ROW_NUMBER() OVER (PARTITION BY brand
// ORDER BY id)` + outer WHERE rn=1); sqlite_linq does not currently lower window
// functions. [Follow-up TODO 2026-05-23: window-function lowering in sqlite_linq.]
// Array vs Decs is the headline comparison. Splice (PR-A2) emits miss-branch-only
// loop (no hit update).
// `_group_by(_.brand) |> _select((Brand=key, FirstCar=first source elem per group))` —
// for each brand, pair the key with the row that arrived first (by source/PK order).
// SQL (m1) — Arm C: tuple-projection over bare-aggregate inner subquery.
// `SELECT "brand", "id", "name", "price", "brand", "year", "dealer_id"
// FROM (SELECT *, MIN(pk) FROM Cars GROUP BY brand) AS t0`.
// See sqlite_linq.das::is_first_of_group_call + build_row_make_struct.
// Array/Decs (m3f/m4) splice through plan_group_by_core's miss-branch-only loop (PR-A2).

def run_m1(b : B?; n : int) {
with_sqlite(":memory:") $(db) {
fixture_db(db, n)
b |> run("m1_sql/{n}", n) {
let groups <- _sql(db |> select_from(type<Car>)
|> _group_by(_.brand)
|> _select((Brand = _._0,
FirstCar = _._1 |> first())))
b |> accept(groups)
if (empty(groups)) {
b->failNow()
}
}
}
}

def run_m3f(b : B?; n : int) {
let arr <- fixture_array(n)
Expand Down Expand Up @@ -40,6 +60,11 @@ def run_m4(b : B?; n : int) {
}
}

[benchmark]
def groupby_first_m1(b : B?) {
run_m1(b, 100000)
}

[benchmark]
def groupby_first_m3f(b : B?) {
run_m3f(b, 100000)
Expand Down
Loading
Loading