Skip to content

Commit 0179f67

Browse files
committed
refactor(benchmarks): keep focused parquet regression
1 parent 4e566c0 commit 0179f67

6 files changed

Lines changed: 128 additions & 1849 deletions

File tree

‎benchmarks/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,13 @@ regex.workspace = true
6060
serde = { version = "1.0.228", features = ["derive"] }
6161
serde_json = { workspace = true }
6262
snmalloc-rs = { version = "0.7", optional = true }
63-
tempfile = { workspace = true }
6463
tokio = { workspace = true, features = ["rt-multi-thread", "parking_lot"] }
6564
tokio-util = { version = "0.7.17" }
6665
toml = "1.1.3"
6766

6867
[dev-dependencies]
6968
datafusion-proto = { workspace = true, features = ["parquet"] }
69+
tempfile = { workspace = true }
7070
tokio = { workspace = true, features = ["test-util"] }
7171

7272
[[bench]]

‎benchmarks/README.md‎

Lines changed: 7 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,123 +1145,16 @@ adjacent chunks at RG boundaries.
11451145
./bench.sh run sort_pushdown_inexact_overlap
11461146
```
11471147

1148-
## Dictionary vs Utf8View Encoding Sweep
1149-
1150-
`dict_encoding_sweep` compares `Dictionary<Int32, Utf8>` against `Utf8View` for
1151-
the same operator across a cardinality sweep. Most groups generate two
1152-
in-memory tables (one per encoding) that share values, SQL text, and seed. The
1153-
`parquet_scan` group instead writes the same rows with both `PLAIN` and
1154-
`RLE_DICTIONARY` physical encodings, then scans each file as both Arrow output
1155-
representations. File generation is outside the timed region and the harness
1156-
uses a neutral `Utf8` writer schema so neither requested output representation
1157-
is inherited from Parquet metadata. It verifies every data page has the
1158-
requested physical encoding before recording results.
1159-
1160-
Warmup and timed iterations are interleaved between Arrow output encodings so
1161-
cache and thermal state stay symmetric. Median / min / p25 / p75 runtimes
1162-
(nanoseconds) are appended to a CSV.
1163-
1164-
The intent is to make it easy for other engineers to reproduce where dictionary
1165-
encoding wins, where it loses, and at which NDV it crosses over, without
1166-
touching the harness itself.
1167-
1168-
### Operator groups
1169-
1170-
Pass `--group` to pick one of:
1171-
1172-
| Group | Queries |
1173-
|-------|---------|
1174-
| `scan` | `scan_k`, `scan_all`, `scan_count_star`, `scan_count_k` |
1175-
| `parquet_scan` | `scan_k`, `scan_all`; runs the `PLAIN`/`RLE_DICTIONARY` × `Utf8View`/dictionary matrix |
1176-
| `filter` | `filter_eq`, `filter_ne`, `filter_in10`, `filter_like_prefix`, `filter_gt`, `filter_1pct_group` |
1177-
| `project` | `proj_length`, `proj_substr`, `proj_upper`, `proj_concat`, `proj_case_like` |
1178-
| `aggregation` | `agg_count_distinct_k`, `agg_group_count_k`, `agg_group_sum_v_k`, `agg_group_count_kw`, `agg_having_k` |
1179-
| `sort` | `sort_k`, `sort_k_desc`, `sort_k_limit_1000`, `sort_kw`, `sort_v_by_k` |
1180-
| `join` | `join_inner`, `join_inner_project`, `join_left`, `join_semi`, `join_anti_half` |
1181-
| `limit` | `limit_k_100`, `limit_k_1k`, `limit_k_10k`, `limit_all_1k`, `limit_offset_50k` |
1182-
| `repartition` | `repart_group_k`, `repart_hash_join`, `repart_project_k` |
1183-
| `coalesce_batches` | `cb_filter_eq`, `cb_filter_project`, `cb_filter_group` |
1184-
| `coalesce_partitions` | `cp_scan_k`, `cp_count_star`, `cp_sum_v` |
1185-
| `spm` | `spm_sort_k`, `spm_sort_k_limit`, `spm_sort_v_by_k` |
1186-
1187-
Narrow to one query with `--query <id>`.
1148+
## Parquet RLE dictionary scan
11881149

1189-
### Running
1190-
1191-
```bash
1192-
# build release binary (one time)
1193-
cargo build --release --bin dict_encoding_sweep -p datafusion-benchmarks
1194-
1195-
# single cell: filter group, 1M rows, sweep over NDVs
1196-
./target/release/dict_encoding_sweep \
1197-
--group filter --rows 1000000 \
1198-
--ndvs 10,100,500,1000,4000,8192 \
1199-
--iters 15 --warmups 3 \
1200-
--output /tmp/dict_sweep/filter_1m.csv
1201-
```
1202-
1203-
For scan-level Parquet decoding, one invocation produces four rows per query
1204-
and NDV: `PLAIN→Utf8View`, `PLAIN→Dictionary`,
1205-
`RLE_DICTIONARY→Utf8View`, and `RLE_DICTIONARY→Dictionary`.
1206-
1207-
```bash
1208-
./target/release/dict_encoding_sweep \
1209-
--group parquet_scan --query scan_k --rows 1000000 \
1210-
--ndvs 10,100,500,1000,2000,4000,8192 \
1211-
--iters 15 --warmups 3 \
1212-
--output /tmp/dict_sweep/parquet_scan_1m.csv
1213-
```
1214-
1215-
The generated Parquet files are uncompressed so the measurements emphasize
1216-
page decoding and Arrow array construction. `k` is the only column whose
1217-
physical encoding changes. An explicit target schema requests both Arrow
1218-
representations from both files, making source encoding and output
1219-
representation independent axes. Focused coverage also verifies the four
1220-
physical plans decode directly in `DataSourceExec`, without a projection cast
1221-
between `Utf8View` and dictionary arrays.
1222-
1223-
For a Criterion target suitable for the benchmark bot, the following command
1224-
compares native `Utf8View` and dictionary output from an `RLE_DICTIONARY`
1225-
Parquet source across NDVs 1 through 500,000:
1150+
This Criterion target compares `Utf8View` and `Dictionary<Int32, Utf8>` output
1151+
from the same `RLE_DICTIONARY` Parquet data at NDVs from 1 through 500,000:
12261152

12271153
```bash
12281154
cargo bench -p datafusion-benchmarks --bench parquet_rle_dictionary_scan
12291155
```
12301156

1231-
The target fixes the input at 1,000,000 rows of 16-byte strings in two
1232-
500,000-row groups. It verifies the physical page encoding and rejects plans
1233-
that insert a representation cast above `DataSourceExec`.
1234-
1235-
Useful flags:
1236-
1237-
| Flag | Default | Notes |
1238-
|------|---------|-------|
1239-
| `--group` | required | Operator group (see table above). |
1240-
| `--query` | all | Restrict to a single query id within the group. |
1241-
| `--rows` | required | Rows in the generated table. |
1242-
| `--ndvs` | required | Comma-separated distinct-value counts to sweep. |
1243-
| `--width` | `16` | Fixed byte width per value. |
1244-
| `--nulls` | `0.0` | Null fraction in column `k`. |
1245-
| `--batch-size` | `8192` | Session + record-batch size. |
1246-
| `--target-partitions` | `1` | Set `>1` to force `RepartitionExec` nodes. |
1247-
| `--dict-chunk` | `500000` | Rows per shared dictionary values-array chunk; also the Parquet row-group size for `parquet_scan`. |
1248-
| `--iters` | `15` | Timed iterations per cell per encoding. |
1249-
| `--warmups` | `3` | Untimed warmup iterations per cell per encoding. |
1250-
1251-
### CSV schema
1252-
1253-
The output CSV uses one row per `(encoding, cell)`. Columns:
1254-
1255-
```
1256-
git_sha, group, query_id, source_encoding, encoding, rows, ndv, width_bytes, null_rate,
1257-
batch_size, dict_chunk_rows, process_id,
1258-
median_ns, min_ns, p25_ns, p75_ns, iters, note
1259-
```
1260-
1261-
`source_encoding` is `memory`, `parquet_plain`, or
1262-
`parquet_rle_dictionary`. Parquet rows include the generated file size in
1263-
`note`.
1264-
1265-
Pair the two rows for the same cell by `(group, query_id, rows, ndv,
1266-
width_bytes, null_rate, batch_size, dict_chunk_rows, source_encoding)` and divide
1267-
`dict.median_ns / view.median_ns` to get the per-cell speedup.
1157+
Each case scans 1,000,000 16-byte strings in two 500,000-row groups. It covers
1158+
both annotated UTF-8 and unannotated binary sources, verifies the physical page
1159+
encoding, and rejects plans that insert a representation cast above
1160+
`DataSourceExec`.

0 commit comments

Comments
 (0)