bench: write Lance at storage version 2.2 in the compression benchmark - #9832
bench: write Lance at storage version 2.2 in the compression benchmark#9832joseph-isaacs wants to merge 6 commits into
Conversation
`LanceCompressor` wrote storage version 2.0, the pre-structural-encoding layout that applies almost no compression. The other two Lance writers in `lance-bench` (`convert.rs`, `random_access.rs`) already use 2.1, which is also Lance's own default and the first version with the compressive encodings (bitpacking, FSST, general compression). The compression suite was therefore charting a near-uncompressed Lance file against compressed Vortex and Parquet. Lance was also missing from pull-request compression runs: `pr-bench-compress.yml` left `with_lance` at its default of false, so the binary was built without the feature and `compress-split.py` defaulted to `arrow-ipc,parquet,vortex`. Only the `develop` run covered Lance. - Write Lance at storage version 2.1 in `LanceCompressor`. - Put `lance` in `compress-split.py`'s default `--formats`, matching `random-access-split.py`, and drop the now-redundant explicit list from `develop-bench.yml`. - Set `with_lance: true` for the PR compression benchmark. - Document the feature, the format list and the storage version in the compress-bench README. Lance rows in the PR `Compression` comment will read as a large change against the S3 baseline, which was recorded at 2.0. Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TkDpMmCYNaj43ENFUyDULe
Polar Signals Profiling ResultsLatest Run
Powered by Polar Signals Cloud |
Merging this PR will degrade performance by 13.06%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | WallTime | arrow_checked_add_u32_neon[16384] |
13.3 µs | 20.4 µs | -34.91% |
| ⚡ | WallTime | deferred_i64_avx512[PerRowPerRow] |
11.6 µs | 10 µs | +16.12% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing claude/lance-compression-benchmarks-sgwcnd (b7e0285) with develop (2dffb68)
Footnotes
-
218 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
This PR changes six files: three workflow YAMLs, The report also contradicts itself on hardware:
A NEON benchmark and an AVX-512 benchmark cannot both execute on one machine, so these samples were not all taken on the same hardware as their baselines. That is precisely what CodSpeed's own two warnings on the report say — "Different runtime environments detected" and "Unknown Walltime execution environment detected — using the Walltime instrument on standard Hosted Runners will lead to inconsistent data". All three regressions are WallTime-mode; the single Simulation-mode change on the report ( I have not spent a re-run on this one. A CodSpeed re-run would resample the same cross-environment variance rather than settle anything, and the static evidence above is stronger than another sample would be. Separately, Happy to revisit if a reviewer reads the CodSpeed data differently. Generated by Claude Code |
Benchmarks: String Encoding 📖Commits: PR vortex / vortex-file-compressed / ms (0.996x ➖, 0↑ 0↓)
vortex / vortex-file-compressed / % (1.000x ➖, 0↑ 0↓)
|
Benchmarks: PolarSignals Profiling 📖Commits: PR datafusion / vortex-file-compressed / ns (1.011x ➖, 0↑ 0↓)
No file size changes detected. |
Benchmarks: FineWeb NVMe 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-file-compressed / ns (0.984x ➖, 0↑ 1↓)
datafusion / vortex-compact / ns (0.985x ➖, 0↑ 0↓)
datafusion / parquet / ns (0.937x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed / ns (1.013x ➖, 0↑ 1↓)
duckdb / vortex-compact / ns (1.022x ➖, 0↑ 1↓)
duckdb / parquet / ns (0.980x ➖, 0↑ 0↓)
No file size changes detected. |
Benchmarks: TPC-DS SF=1 on NVME 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-file-compressed / ns (0.997x ➖, 1↑ 1↓)
datafusion / vortex-compact / ns (0.999x ➖, 0↑ 1↓)
datafusion / parquet / ns (0.996x ➖, 0↑ 1↓)
duckdb / vortex-file-compressed / ns (0.997x ➖, 0↑ 4↓)
duckdb / vortex-compact / ns (0.996x ➖, 1↑ 1↓)
duckdb / parquet / ns (0.995x ➖, 1↑ 0↓)
No file size changes detected. |
Benchmarks: Appian on NVME 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-compact / ns (1.004x ➖, 0↑ 0↓)
datafusion / parquet / ns (1.003x ➖, 0↑ 0↓)
duckdb / vortex-compact / ns (0.990x ➖, 0↑ 0↓)
duckdb / parquet / ns (0.986x ➖, 1↑ 0↓)
No file size changes detected. |
Benchmarks: Statistical and Population Genetics 📖Commits: PR How to read Verdict and Engines
duckdb / vortex-file-compressed / ns (1.001x ➖, 0↑ 0↓)
duckdb / vortex-compact / ns (1.006x ➖, 0↑ 0↓)
duckdb / parquet / ns (0.988x ➖, 0↑ 0↓)
No file size changes detected. |
Benchmarks: Clickbench on NVME 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-file-compressed / ns (1.003x ➖, 0↑ 1↓)
datafusion / vortex-compact / ns (1.003x ➖, 0↑ 0↓)
datafusion / parquet / ns (1.006x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed / ns (1.013x ➖, 1↑ 3↓)
duckdb / vortex-compact / ns (1.013x ➖, 1↑ 2↓)
duckdb / parquet / ns (1.010x ➖, 0↑ 1↓)
No file size changes detected. |
Benchmarks: FineWeb S3 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-file-compressed / ns (0.998x ➖, 1↑ 1↓)
datafusion / vortex-compact / ns (1.063x ➖, 0↑ 1↓)
datafusion / parquet / ns (0.858x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed / ns (1.072x ➖, 0↑ 2↓)
duckdb / vortex-compact / ns (1.104x ➖, 0↑ 1↓)
duckdb / parquet / ns (0.977x ➖, 0↑ 0↓)
|
Benchmarks: TPC-H SF=1 on NVME 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-file-compressed / ns (1.002x ➖, 0↑ 0↓)
datafusion / vortex-compact / ns (1.009x ➖, 0↑ 0↓)
datafusion / parquet / ns (1.006x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed / ns (1.011x ➖, 0↑ 1↓)
duckdb / vortex-compact / ns (1.001x ➖, 1↑ 1↓)
duckdb / parquet / ns (1.008x ➖, 0↑ 0↓)
No file size changes detected. |
Benchmarks: TPC-H SF=10 on NVME 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-file-compressed / ns (0.993x ➖, 0↑ 0↓)
datafusion / vortex-compact / ns (1.000x ➖, 0↑ 0↓)
datafusion / parquet / ns (1.000x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed / ns (1.010x ➖, 0↑ 1↓)
duckdb / vortex-compact / ns (1.003x ➖, 0↑ 0↓)
duckdb / parquet / ns (1.003x ➖, 0↑ 0↓)
No file size changes detected. |
Benchmarks: Clickbench Sorted on NVME 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-file-compressed / ns (1.003x ➖, 0↑ 0↓)
datafusion / vortex-compact / ns (1.004x ➖, 0↑ 0↓)
datafusion / parquet / ns (1.011x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed / ns (0.947x ➖, 2↑ 0↓)
duckdb / vortex-compact / ns (0.988x ➖, 1↑ 0↓)
duckdb / parquet / ns (0.981x ➖, 1↑ 0↓)
File Size Changes (200 files changed, +0.0% overall, 88↑ 112↓)
Totals:
|
Revert the pull-request side of the previous commit. Lance stays on the post-merge `develop` benchmark only, which already passes `--formats arrow-ipc,parquet,lance,vortex`; the PR `Compression` matrix is left as it was so it does not grow. `pr-bench-compress.yml`, `pr-bench-runner.yml` and `develop-bench.yml` are back to their original contents, and `compress-split.py` keeps its original default format list. What remains is the storage-version fix plus docs. Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TkDpMmCYNaj43ENFUyDULe
Benchmarks: TPC-H SF=1 on S3 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-file-compressed / ns (1.034x ➖, 1↑ 1↓)
datafusion / vortex-compact / ns (1.132x ➖, 1↑ 4↓)
datafusion / parquet / ns (0.999x ➖, 1↑ 0↓)
duckdb / vortex-file-compressed / ns (1.069x ➖, 0↑ 0↓)
duckdb / vortex-compact / ns (1.072x ➖, 0↑ 1↓)
duckdb / parquet / ns (1.020x ➖, 0↑ 0↓)
|
|
Reproduced on plain developI checked out Identical to CI. Re-running the job will not help: it is a deterministic snapshot mismatch, not flake, so I have not spent a re-run on it. What changedTwo commits on develop combine badly:
The snapshot #9710 committed holds the pre-#9806 onpair output — Why it is not this PRThis PR is three files: The timing corroborates it: my first head Proposed fix, for whoever owns #9806 / #9710Regenerate the snapshot against current onpair: cargo insta test -p vortex-btrblocks --test golden --acceptI have not pushed that here. It belongs on develop rather than inside an unrelated benchmark PR, and accepting a golden snapshot is exactly the change that should be confirmed by the author of the compression change — the new numbers need to be someone's intended onpair output, not just whatever the compressor now emits. Worth noting the new dictionary is slightly larger (11550 to 11866 bytes) while the code stream shortens (64202 to 63845), which is consistent with #9806's row-training change and not obviously a regression, but that is a judgement for its author. Happy to apply it here instead if a maintainer would rather unblock this PR directly. Generated by Claude Code |
Benchmarks: Compression 📖Commits: PR vortex / vortex-file-compressed / ns (0.991x ➖, 0↑ 0↓)
vortex / vortex-file-compressed / bytes (1.000x ➖, 0↑ 0↓)
vortex / vortex-file-compressed / ratio (1.096x ➖, 14↑ 21↓)
vortex / parquet / ns (0.998x ➖, 0↑ 0↓)
vortex / parquet / bytes (1.000x ➖, 0↑ 0↓)
vortex / lance / ns (1.104x ❌, 6↑ 14↓)
vortex / lance / bytes (0.457x ✅, 15↑ 0↓)
vortex / arrow-ipc / ns (1.003x ➖, 0↑ 0↓)
vortex / arrow-ipc / bytes (1.000x ➖, 0↑ 0↓)
|
Benchmarks: TPC-H SF=10 on S3 📖Commits: PR How to read Verdict and Engines
datafusion / vortex-compact / ns (0.951x ➖, 2↑ 1↓)
datafusion / parquet / ns (1.161x ➖, 0↑ 3↓)
duckdb / vortex-compact / ns (0.940x ➖, 0↑ 0↓)
duckdb / parquet / ns (1.105x ➖, 0↑ 3↓)
|
Benchmarks: Random Access 📖Commits: PR How to read Verdict and Engines
vortex / arrow-ipc / ns (0.896x ✅, 7↑ 1↓)
random-access / vortex-file-compressed / ns (0.995x ➖, 0↑ 0↓)
random-access / parquet / ns (1.004x ➖, 0↑ 1↓)
random-access / lance / ns (1.000x ➖, 0↑ 0↓)
|
2.2 is the newest stable Lance storage version. `is_unstable` is `self >= Next`, and the variant order is `Legacy < V2_0 < V2_1 < Stable < V2_2 < Next < V2_3`, so 2.2 sits below the unstable boundary. lance-file maps it to `ConcreteFileVersion::V2_2` for writing and resolves `(2, 2)` back to it when reading, and `is_structural = version >= V2_1` still holds, so the compressive encodings stay on. Named explicitly rather than through `LanceFileVersion::Stable`, which resolves to `default()` — that is 2.1, the default version for new datasets, not the newest stable version. `convert.rs` and `random_access.rs` are left at 2.1 so the SQL and random-access suites keep writing the data they already write. Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TkDpMmCYNaj43ENFUyDULe
…ession-benchmarks-sgwcnd
The code comment carried the `is_unstable` variant-order proof and the 2.0 history, which belong in the commit message and the pull request, not next to the line. What a reader of `compress.rs` needs is the two non-obvious facts: 2.1 and above is where structural encoding applies the compressive encodings, and `LanceFileVersion::Stable` is not a way to ask for the newest stable version. The README loses the same 2.0 history, which describes a bug no longer present in the code. Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TkDpMmCYNaj43ENFUyDULe
Summary
LanceCompressorwrote storage version 2.0. Structural encoding arrives in 2.1 (is_structural = version >= V2_1), and that is what turns on Lance's compressive encodings — bitpacking, FSST, general compression. At 2.0 the compression suite was charting a near-uncompressed Lance file against compressed Vortex and Parquet, and reporting Lance files larger than Parquet's. The Lance ratios in thedevelopbenchmark history are therefore not a measurement of Lance's compression.This writes 2.2, the newest stable storage version. The other two Lance writers in
lance-bench—convert.rs(SQL suite) andrandom_access.rs— remain at 2.1, so the SQL and random-access suites keep writing the data they already write.Why 2.2 is named explicitly
LanceFileVersion::Stableresolves todefault(), which is 2.1 — it marks the default version for new datasets, not the newest stable version. Reaching forStablewould leave the writer where it already was, so 2.2 has to be named directly.2.2 is stable rather than experimental:
is_unstable()isself >= Next, and the variant order isLegacy < V2_0 < V2_1 < Stable < V2_2 < Next < V2_3, so 2.2 sits below the unstable boundary. Verified against the pinned lance 10.0.0 — lance-file mapsV2_2 → ConcreteFileVersion::V2_2for writing and resolves(2, 2)back toV2_2when reading, andis_structuralstill holds at 2.2.What 2.2 writes locally
Each version measured on
wide table cols=1000 chunks=1 rows=1000with a binary differing only in the storage-version constant, against an identical Parquet baseline of 9,324,004 bytes in all three runs, so the rows are directly comparable:2.2 is 1.94x smaller than the 2.0 file, and a further 2.8% below 2.1. The run included the
decompressop, so the pinned reader reads back what the 2.2 writer emits — this is a round-trip, not a write-only check. Timings from that run are from a debug build and are not reported here; the hardware numbers below come from CI.Benchmark result
These numbers were measured at 2.1, from the
action/bench-allrun on commit98736325, which wrote 2.1. They establish the size of the 2.0 bug; 2.2 has not been measured on the benchmark hardware, and the first 2.2 numbers there will come from the post-mergedeveloprun. The local table above suggests 2.2 lands a further ~3% below these figures.vortex / lance / bytes: 0.457x, 15 improved, 0 regressed— every dataset's Lance file shrank against the 2.0 baseline:Vortex, Parquet and Arrow IPC sizes were unchanged at +0.0%, as expected — the change only affects what the Lance writer emits.
Two consistency checks
wide table cols=1000 chunks=1 rows=1000came back as 8,649,004 / 16,315,490 bytes, byte-for-byte identical to a local A/B built either side of the one-line change. CI on dedicated hardware reproduced the local number exactly.The
Random Accesssuite reported Lance at 1.000x, 0 up 0 down. That is the control:random_access.rsalready wrote 2.1, so it should not move, and it did not. Only the suite whose writer changed changed — and that property is whyrandom_access.rsis being left alone here.Times move too, and that is the point
Lance decompression got slower at 2.1 — Euro2016 +413%, HashTags +264%, TPC-H l_comment +194%, Bimbo +116%. That is not a regression: at 2.0 Lance was reading a barely-compressed file, so there was little to decode. Compress time is mixed for the same reason (Arade -88.1%, CMSprovider -70.5%, TPC-H -56%, against Bimbo +29.1%, HashTags +18.9%, taxi +18.7%).
One ratio is worth calling out rather than burying: on the synthetic wide tables the
vortex:lancesize ratio moved from 0.571 to 1.076, meaning Lance produced a smaller file than Vortex there. That comparison was previously hidden behind an uncompressed baseline.Changes
benchmarks/lance-bench/src/compress.rs— writeLanceFileVersion::V2_2instead ofV2_0, with a short comment on the two non-obvious facts: 2.1 and above is where structural encoding applies the compressive encodings, andStableis not a way to ask for the newest stable version. The variant-order proof lives in the commit message, not next to the line.benchmarks/compress-bench/README.md— document thelancefeature, the local invocation, the storage version and theStable-resolves-to-2.1 gotcha.scripts/compress-split.py— note in--formatshelp thatlanceneeds a binary built with the feature.Benchmark coverage is deliberately unchanged. Lance runs in the post-merge
developbenchmark only, which already passes--formats arrow-ipc,parquet,lance,vortex; the pull-requestCompressionmatrix is left alone so it does not grow. No workflow files are modified.Because the
developS3 baseline was recorded at 2.0, the first post-merge run will show the Lance rows moving by roughly the amounts above. That is the correction landing, not a regression.CI status
Five checks were red on this branch for a reason that was not this PR:
develophad a stale onpair golden snapshot, which brokeRust (btrblocks golden corpus)and the workspaceRust testsjobs on every open PR. That is fixed on develop by #9835, and this branch has merged it —cargo test -p vortex-btrblocks --test goldenpasses locally on the merge.No CI job exercises the Lance path on a pull request, because the PR
Compressionmatrix is built without thelancefeature. The first CI run to write a Lance file at 2.2 will be the post-mergedevelopbenchmark, which makes the local round-trip above the only pre-merge validation of the 2.2 writer.Checks
cargo build -p compress-bench --features lanceat 2.2 — clean.compress-bench --ops compress --ops decompresson the synthetic wide table at 2.2 — round-trips, 8,404,981 bytes (table above).ConcreteFileVersion::V2_2, readable via the reader's(2, 2)mapping,is_structuralsatisfied.cargo test -p vortex-btrblocks --test goldenafter merging Fix onpair btrblocks golden snapshot #9835 —golden_onpairandgolden_regularboth pass.cargo +nightly fmt --all -- --check— clean.ruff format --checkandruff checkonscripts/compress-split.py— pass.action/bench-allrun at 2.1: Compression, Random Access, String Encoding and the SQL matrix. Every SQL suite returned "No clear signal", correct for a change that only alters what the compression benchmark writes.Not run: the benchmark hardware has not measured 2.2, and no PR CI job builds the
lancefeature, so the CI table above remains a 2.1 measurement.🤖 Generated with Claude Code
https://claude.ai/code/session_01TkDpMmCYNaj43ENFUyDULe