Skip to content

Commit a401812

Browse files
authored
compress-bench: write and scan Parquet the way DataFusion does by default (#10571)
Replace the bespoke Parquet parallelism from #10510 with a port of DataFusion's defaults: - Compress follows ParquetSink: one encoding task per leaf column of the open row group, fed through channels of 2 arrays, with the next row group starting while the previous one is closed and at most 1 finished row group queued for appending. Output leaves the writer buffer every ~1 MB and is joined once at the end. - Decompress follows DataFusion's file scan: the file is split into one byte range per runtime worker (one partition under 1 MiB), each row group goes to the range holding its first page, and each range is one sequential reader in 8,192-row batches with view types.
1 parent 3ad71dd commit a401812

4 files changed

Lines changed: 357 additions & 189 deletions

File tree

‎Cargo.lock‎

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎benchmarks/compress-bench/Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ indicatif = { workspace = true }
2626
itertools = { workspace = true }
2727
lance-bench = { path = "../lance-bench", optional = true }
2828
mimalloc = { workspace = true }
29+
parking_lot = { workspace = true }
2930
parquet = { workspace = true }
3031
regex = { workspace = true }
3132
serde = { workspace = true, features = ["derive"] }
@@ -40,6 +41,7 @@ vortex-cuda = { workspace = true, optional = true }
4041

4142
[dev-dependencies]
4243
arrow-select = { workspace = true }
44+
rstest = { workspace = true }
4345

4446
[features]
4547
cuda = ["dep:tempfile", "dep:vortex-cuda"]

‎benchmarks/compress-bench/README.md‎

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,25 @@ CI runs the timed benchmark on a fixed budget of 16 cores: `scripts/compress-spl
3131
`--threads 16`, so the result does not depend on how many cores the benchmark machine has.
3232
Pass `--cpus 0` to use every benchmark CPU instead.
3333

34-
Vortex schedules its own compression and scan work as tasks on the runtime. The Parquet
35-
compressor splits its work into tasks on the same runtime:
36-
37-
- **Compress** splits the input into row groups at the writer's default
38-
`max_row_group_row_count` (1,048,576 rows), the same boundaries `ArrowWriter` produces, then
39-
encodes every (row group, top-level field) chunk as its own task with parquet-rs's
40-
`ArrowRowGroupWriterFactory`, largest first, and appends the chunks in order into an output
41-
buffer sized once from the encoded chunk sizes. This is how DataFusion's parallel Parquet
42-
writer (`ParquetSink`) uses multiple cores.
43-
- **Decompress** scans the way DataFusion and DuckDB do: one task per row group, each decoding
44-
all projected columns of its row group, so the scan uses as many workers as the file has row
45-
groups. To match the Vortex reader's output, strings and binaries decode to `Utf8View` /
46-
`BinaryView`, in batches of 8,192 rows (the Vortex writer's row block size).
34+
Vortex schedules its own compression and scan work as tasks on the runtime. Parquet runs on
35+
the same runtime the way DataFusion's Parquet sink and scan do with their default options:
36+
37+
- **Compress** follows DataFusion's parallel `ParquetSink`. Input is split into row groups at
38+
the writer's default `max_row_group_row_count` (1,048,576 rows), the same boundaries
39+
`ArrowWriter` produces. Every leaf column of the open row group is encoded by its own task,
40+
fed through a channel of at most 2 arrays (`maximum_buffered_record_batches_per_stream`). When
41+
a row group is full, a separate task closes its column writers while the next row group
42+
starts, and at most 1 finished row group waits to be appended to the file
43+
(`maximum_parallel_row_group_writers`). Encoded bytes leave the writer's buffer every ~1 MB,
44+
as DataFusion streams them to the object store, and are joined into one buffer at the end.
45+
- **Decompress** follows DataFusion's file scan. The file's bytes are split into one range per
46+
runtime worker (`target_partitions`), unless the file is under 1 MiB
47+
(`repartition_file_min_size`), and each row group belongs to the range holding its first page.
48+
Each range is one task that decodes its row groups in order, in batches of 8,192 rows
49+
(`batch_size`, also the Vortex writer's row block size), reading strings and binaries as
50+
`Utf8View` / `BinaryView` (`schema_force_view_types`), the types the Vortex reader produces.
51+
Spark plans a single file the same way: byte-range splits, each row group read by the split
52+
holding its midpoint, one sequential reader per split.
4753

4854
The written file has the same row groups, encodings and compression as a sequential
4955
`ArrowWriter` with the same settings. Arrow IPC is a serialization baseline and stays

0 commit comments

Comments
 (0)