feat(arrow): export StringArray and BinaryArray directly#8902
Conversation
0dade7d to
4807bd2
Compare
9a91ff0 to
02fa0e3
Compare
Problem Vortex exports UTF-8 and binary data as Arrow view arrays by default. Some consumers require Arrow arrays that use 32-bit or 64-bit offsets. These consumers must cast each view array and rebuild its buffers. Implementation Add VarBinBufferBuilder for 32-bit and 64-bit offsets. Write Arrow value, offset, and validity buffers in this builder. Decode FSST, OnPair, Zstd, Dict, RunEnd, and Sparse arrays directly into this builder. Do not create an intermediate VarBinViewArray for these paths. Interface Add an optional Arrow type to the Rust and Python array APIs. Add an optional Arrow schema to the Python file API. Keep Arrow view arrays as the default output. Result Arrow uses the builder buffers without another copy. Callers can select StringArray, BinaryArray, LargeStringArray, or LargeBinaryArray. Signed-off-by: cl <cailue@apache.org>
02fa0e3 to
a377a9b
Compare
StringArray and BinaryArray
StringArray and BinaryArraySigned-off-by: cl <cailue@apache.org>
Signed-off-by: cl <cailue@apache.org>
Signed-off-by: cl <cailue@apache.org>
e3a099c to
6f03c1d
Compare
Signed-off-by: cl <cailue@apache.org>
Signed-off-by: cl <cailue@apache.org>
b99072f to
413501e
Compare
Signed-off-by: 蔡略 <cailue@apache.org>
Signed-off-by: 蔡略 <cailue@apache.org>
Merging this PR will degrade performance by 24.19%
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
…w-byte-arrays Signed-off-by: 蔡略 <cailue@apache.org>
90b3f97 to
ef75f15
Compare
|
The regression is strange, nowhere can the code be touched. GPT blames codegen unit difference, it insists code in this patch disturbs LLVM's optimization and thus produced benchmark with different layouts. I've benchmarked the patch against baseline under 1 CGU. Their results are close, and regression turns out to be solved. It is interesting, but I will keep this patch free of codegen changes. |
|
Some of the microbenchmarks are sensitive to code changes. I don't think it's related to your change. Thanks for your contribution. I will review it tomorrow! |
Summary
This PR adds direct export to Arrow
StringArrayandBinaryArray.A caller can request one of these Arrow types:
Utf8LargeUtf8BinaryLargeBinaryVortex builds a
VarBinArrayfor the requested Arrow type. Arrow uses the buffers in theVarBinArray.The Python API accepts the requested Arrow type:
Vortex still exports
StringViewArrayandBinaryViewArraywhen the caller does not request a type or schema.Direct export paths
These encodings can write directly to the
VarBinArraybuilder:The Dict path also supports compressed dictionary values. Examples include
Dict<FSST>andDict<Zstd>.Sparse and RunEnd use the normal canonical path. The Rust benchmark showed a 55% gain for the Sparse canonical path. The Vortex compressor does not create RunEnd string arrays.
Reason for the change
The old path created a
StringViewArrayorBinaryViewArray. Arrow then cast the result to aStringArrayorBinaryArray.The cast copied or reordered the value bytes.
The new path creates a
StringArrayorBinaryArraydirectly.Performance
This Python benchmark used 500,000 repeated URL strings. Each result is the median of 10 runs.
StringArrayStringViewArray, then cast toStringArrayDirect export was 59.4% faster in this benchmark.
The Rust benchmark covers these layouts and encodings:
Dict<FSST>Dict<Zstd>Chunked<FSST>The Filter cases do not use the direct encoding paths.
Chunked<FSST>uses the direct FSST path. Chunked passes the output builder to each child.AI assistance
I used GPT5.6sol to help develop this change. I reviewed and tested all changes.
Verification
cargo nextest run -p vortex-arrow -p vortex-fsst -p vortex-onpair -p vortex-runend -p vortex-sparse -p vortex-zstdpassed 529 tests.cargo nextest run -p vortex-array --no-fail-fastpassed 3,003 tests and skipped 1 test.cargo clippy -p vortex-arrow -p vortex-sparse -p vortex-runend --all-targets --all-features -- -D warningspassed.cargo +nightly fmt --allpassed.git diff --checkpassed.Coverage
Rust coverage used LLVM branch instrumentation for
vortex-array,vortex-arrow,vortex-fsst,vortex-onpair,vortex-runend,vortex-sparse, andvortex-zstd.The original targeted run passed 3,550 tests and skipped 2 tests. After adding focused tests, this command refreshed coverage for the changed
vortex-arraypaths:The refreshed run passed 3,003 tests and skipped 1 test.
The changed-line results compare this branch with
57962f4df9af99ebad584bf3e59310e4bf552fc1. They intersect added lines with the LLVM LCOV line and branch records. The report combines the refreshedvortex-arrayLCOV with the existing LCOV for the unchanged encoding and Arrow crates. This Rust scope does not include benchmarks or the PyO3 crate.Python branch coverage used the changed array and file test modules.
The run completed with 19 passed, 1 pre-existing xfail (
test_scalar_at_out_of_bounds), and 0 failed.vortex/file.py: 32 of 47 statements, or 68.09%.vortex/file.py: 0 of 2 branches. Both branches are in unchanged code.Python coverage does not instrument the Rust code in the extension. The Python tests exercise that code through the public API.
Next steps
Pass the output builder through lazy arrays such as Filter, Slice, and Take. This change can remove more intermediate canonical arrays. Track this work in #7674.