Repository navigation
bench(manifest): add criterion benchmark for Manifest::parse_avro - #3468
Draft
kevinjqliu wants to merge 4 commits into
Draft
kevinjqliu wants to merge 4 commits into
kevinjqliu wants to merge 4 commits into
Conversation
Measures parsing V2 data manifests with 1k, 10k, and 50k entries, each with stats for 12 columns and an identity partition on a string column. The manifests are written in the bench with ManifestWriter. A second case drops the data_file fields PyIceberg doesn't write, so the writer schema differs from the reader schema. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Rewrite the second case the way Iceberg Java writes V2 manifests, with deflate and 64 KB blocks, and measure 1k to 100k entries. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Parse manifests laid out the way Iceberg Java writes them by default: without the data_file fields it doesn't write, deflate, and 64 KB blocks. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add a group that parses 10,000-entry manifests made larger by adding columns with stats, from about 1 MB to 100 MB, and reports MB/s. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
What changes are included in this PR?
Adds the repo's first benchmark,
crates/iceberg/benches/manifest_parse.rs(criterion), forManifest::parse_avroon V2 data manifests with full stats for every column and an identity partition on a string column. It has two groups:manifest_parse_avro: 1k to 100k entries with 12 columns.manifest_parse_avro_by_columns: 10,000 entries with 9 to 610 columns, which makes the files about 1 MB to 100 MB. It reports MB/s. This group takes about 3 minutes and peaks at about 6 GB of memory.The bench writes each manifest with
ManifestWriter, then rewrites it the way Iceberg Java writes manifests by default, which is how most manifests are written: withoutfirst_row_id,content_offset, andcontent_size_in_bytes, with deflate, and with 64 KB blocks. Its writer schema differs from iceberg-rust's reader schema, which is the case #3354 sped up. No fixtures, Python, or JVM are needed. Run withcargo bench -p iceberg --bench manifest_parse.Comparison with other Iceberg readers
A cross-language comparison can't run as a criterion bench, so the harness is below rather than in the repo. Iceberg Java 1.12.0 writes the manifests with default settings. Five readers parse each file from memory:
ManifestFiles.read, callingcopy()on eachDataFilebecause the reader reuses containers. The JVM runs with a 4 GB heap.ManifestFile.fetch_manifest_entrywith its Cython decoder.iceberg.ReadManifest.ecba0b537):Manifest::parse_avro.Manifest::parse_avro.Each reader warms up, then parses each file at least 15 times (5 times in the file size sweep) and for at least 3 s. All readers run single threaded on an Apple M5 Max (JDK 17, Go 1.25, Python 3.13).
By number of entries, Rust after #3354 parses 5.9x to 6.1x faster than Rust before it. It is 1.1x to 1.2x faster than Go and 1.7x to 2.6x faster than Python, and it takes 1.11x to 1.16x as long as Java.
By file size, Rust after #3354 parses 5.3x to 6.0x faster than Rust before it, and runs within 8% of Go and within 20% of Java. Python is the fastest reader from 5 MB on: at 98 MB it takes 0.82 s, against 1.97 s for Rust after #3354. Its Cython decoder returns the four count maps as a
LazyDictthat builds the dict on first access, and it keeps bounds as bytes, while iceberg-rust builds everyHashMapand converts every bound to aDatumwhile parsing. Building the dicts adds 0.56 s at 98 MB, so Python is still faster.By number of entries, median time per parse with the interquartile range:
By file size (10,000 entries), median time per parse with the interquartile range:
The criterion bench gives 51.7 ms at 10k entries and 2.23 s at 610 columns. Its stand-in uses apache-avro's default deflate level, and its schema attributes differ, so its files aren't the same size as Java's.
Harness
run.shandrun_size.shwrite the manifests with Iceberg Java, time each reader, and plot the results.rustandrust-oldare the same Rust project; theiricebergpath dependency points at this branch and atecba0b537, respectively. The Python environment haspyiceberg==0.12.0,pyarrow, andmatplotlib. Each harness prints onereader,entries,msrow per timed run.run.sh:run_size.sh:ManifestBench.java(iceberg-core1.12.0 andslf4j-nop):go/main.go(github.com/apache/iceberg-go v0.7.0):bench_pyiceberg.py:Rust
Cargo.tomlandsrc/main.rs:plot.py:Are these changes tested?
This PR only adds a benchmark. I ran:
cargo bench -p iceberg --bench manifest_parse: passed.cargo clippy -p iceberg --all-targets --all-features -- -D warnings: passed.cargo fmt --all -- --check,taplo fmt --check, andcargo machete: all passed.cargo deny check licenses bans: passed on the first commit; the later commits don't change dependencies.AI Disclosure
Written with GitHub Copilot (Claude Opus 5.5). Please check:
copy()of eachDataFile. PyIceberg builds its count maps lazily and keeps bounds as bytes. iceberg-rust builds everything eagerly.default-features = false(no plotters or rayon). It adds 11 crates toCargo.lockand movescoloredandwinapi-utilto the newerwindows-sysalready in the lock.