Better fixed-width buffer filtering - #8723
Conversation
Merging this PR will regress 2 benchmarks
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
|
Moved benchmarks here #8727 |
21c1bd7 to
423cee5
Compare
8633f26 to
5efe3a9
Compare
Polar Signals Profiling ResultsLatest Run
Previous Runs (3)
Powered by Polar Signals Cloud |
Benchmarks: Vortex queries 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (1.028x ➖, 0↑ 0↓)
datafusion / parquet (0.934x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed (0.953x ➖, 0↑ 0↓)
duckdb / parquet (0.993x ➖, 0↑ 0↓)
No file size changes detected. |
Benchmarks: PolarSignals Profiling 📖Vortex (geomean): 1.038x ➖ How to read Verdict and Engines
datafusion / vortex-file-compressed (1.038x ➖, 0↑ 3↓)
No file size changes detected. |
Benchmarks: FineWeb NVMe 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.964x ➖, 2↑ 0↓)
datafusion / parquet (0.980x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed (0.966x ➖, 2↑ 0↓)
duckdb / parquet (0.996x ➖, 0↑ 0↓)
File Size Changes (2 files changed, -46.3% overall, 0↑ 2↓)
Totals:
|
Benchmarks: TPC-H SF=1 on NVME 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.997x ➖, 0↑ 0↓)
datafusion / parquet (1.004x ➖, 0↑ 0↓)
datafusion / arrow (1.002x ➖, 1↑ 1↓)
duckdb / vortex-file-compressed (0.989x ➖, 0↑ 0↓)
duckdb / parquet (1.015x ➖, 0↑ 2↓)
File Size Changes (10 files changed, -43.9% overall, 0↑ 10↓)
Totals:
|
Benchmarks: TPC-DS SF=1 on NVME 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.978x ➖, 13↑ 3↓)
datafusion / parquet (0.965x ➖, 9↑ 3↓)
duckdb / vortex-file-compressed (0.963x ➖, 4↑ 1↓)
duckdb / parquet (0.997x ➖, 1↑ 1↓)
File Size Changes (25 files changed, -43.5% overall, 0↑ 25↓)
Totals:
|
Benchmarks: Clickbench on NVME 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.973x ➖, 3↑ 0↓)
datafusion / parquet (0.997x ➖, 0↑ 1↓)
duckdb / vortex-file-compressed (0.992x ➖, 3↑ 0↓)
duckdb / parquet (1.000x ➖, 0↑ 0↓)
File Size Changes (101 files changed, -39.2% overall, 0↑ 101↓)
Totals:
|
Benchmarks: FineWeb S3 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (1.149x ➖, 0↑ 1↓)
datafusion / parquet (1.036x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed (1.148x ➖, 0↑ 0↓)
duckdb / parquet (1.026x ➖, 0↑ 0↓)
|
Benchmarks: Statistical and Population Genetics 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
duckdb / vortex-file-compressed (0.985x ➖, 0↑ 0↓)
duckdb / parquet (1.021x ➖, 0↑ 0↓)
File Size Changes (2 files changed, -32.3% overall, 0↑ 2↓)
Totals:
|
Benchmarks: Clickbench Sorted on NVME 📖Verdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.952x ➖, 1↑ 0↓)
datafusion / parquet (0.992x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed (0.996x ➖, 0↑ 2↓)
duckdb / parquet (1.010x ➖, 0↑ 0↓)
File Size Changes (201 files changed, -42.8% overall, 61↑ 140↓)
Totals:
|
Benchmarks: TPC-H SF=10 on NVME 📖Verdict: No clear signal (environment too noisy confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.952x ➖, 3↑ 1↓)
datafusion / parquet (0.939x ➖, 7↑ 2↓)
datafusion / arrow (0.971x ➖, 5↑ 4↓)
duckdb / vortex-file-compressed (0.957x ➖, 4↑ 0↓)
duckdb / parquet (1.009x ➖, 0↑ 0↓)
File Size Changes (25 files changed, -44.0% overall, 0↑ 25↓)
Totals:
|
Benchmarks: TPC-H SF=1 on S3 📖Verdict: No clear signal (environment too noisy confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.967x ➖, 1↑ 0↓)
datafusion / parquet (1.050x ➖, 0↑ 1↓)
duckdb / vortex-file-compressed (1.014x ➖, 0↑ 0↓)
duckdb / parquet (1.017x ➖, 0↑ 0↓)
|
Signed-off-by: Robert Kruszewski <github@robertk.io>
Signed-off-by: Robert Kruszewski <github@robertk.io>
Adds a NEON tier to the fixed-width filter compress kernels and retunes the
x86 tier against real AVX-512 hardware.
NEON has no `vpcompress`, but `tbl` *is* a cross-lane byte shuffle, so a
byte-index LUT covering one vector of lanes serves every element width -
unlike AVX2, which has no byte shuffle and so leaves 1/2-byte elements on the
scalar byte-LUT. Lanes per shuffle is 8/8/4/2 for 1/2/4/8-byte elements.
The chunk loop is deliberately branchless. An `m != 0` guard looks like a
cheap skip but is a large pessimization: the mask bits of one chunk are as
random as the data, so `P(m == 0) = (1 - density)^lanes` crosses 0.5 inside
the useful density range for every width and the branch mispredicts
near-maximally right where it would pay off. Guarding cost 1.95x at density
0.5 for 4-byte elements and 1.45x at 0.1 for 1-byte. Storing a full chunk for
an empty mask is harmless because `write_pos` does not advance, so the next
store overwrites the garbage.
x86's `MIN_DENSITY_WIDE = 0.05` was set without AVX-512 hardware and with only
two density points measured, so it never probed its own crossover. On Zen 5 it
regressed 4-byte and 8-byte filtering by up to 32%, and AVX2 8-byte by 2.3x.
Both tiers now carry per-(kernel, width) floors, since what sets the floor is
how many lanes one compress covers.
Bands are measured per architecture, interleaved against the same binary with
the kernels disabled, and sit just above the crossover so a core with a
different shuffle-to-scalar balance cannot regress. Verified no regression at
any density on both architectures. Within band:
NEON (Apple M-series): 1 byte 1.4-10.1x, 2 bytes 1.3-9.2x,
4 bytes 1.2-5.1x, 8 bytes 1.3x over 0.50..0.80
x86 (Zen 5, AVX-512): 1 byte 1.9-5.7x, 2 bytes 1.1-3.9x,
4 bytes 1.1-2.8x, 8 bytes 1.1-3.1x
x86 (Zen 5, AVX2 only): 4 bytes 1.1-2.5x, 8 bytes 1.4-2.0x
The 8-byte NEON kernel is the only one needing an upper bound: two lanes per
shuffle never amortizes far, and past ~0.8 the byte-LUT's all-set bulk copies
overtake it.
Also promotes simd_compress to a directory module, and installs mimalloc as
the global allocator in the benchmark. Without it the system allocator's
behaviour at these output sizes dominates and destabilizes divan's
per-iteration timing, swinging identical code by up to 20x.
Checks: cargo +nightly fmt --all --check; cargo clippy -p vortex-array
--all-targets --all-features; cargo nextest run -p vortex-array -p
vortex-buffer -p vortex-mask (4058 passed); cargo test --doc; rustdoc with
-D warnings. Full vortex-array suite also run on the AVX-512 machine (3180
passed), which is the first time the AVX-512 and AVX2 kernels have executed.
Signed-off-by: Robert Kruszewski <robert@spiraldb.com>
Gives 1- and 2-byte elements a vector compress on the AVX2 tier, where they previously had none and fell through to the scalar byte-LUT. The justification I committed for that gap was wrong. It claimed AVX2 has "no cross-lane byte shuffle, so 1/2-byte elements stay on the scalar byte-LUT" - but `pshufb` only has to span as many bytes as the chunk holds, and eight lanes of a 1- or 2-byte element need at most a 16-byte table. One 128-bit lane covers that, so the NEON `tbl` construction ports over unchanged: same LUT layout, same branchless chunk loop, only the load/shuffle/store intrinsics differ. `compress_lut` and `compress_tail` move up to the parent module and are now shared by both. Enabling AVX2 rather than SSSE3 gets the VEX-encoded `vpshufb`, avoiding AVX/SSE transition penalties in mixed code. AVX-512F parts without VBMI2 (Skylake-X) fall through to these too. Measured on Zen 5 with AVX-512 detection forced off, interleaved against the same binary with the kernels disabled: 1 byte, floor 0.15: 1.5x at floor, 2.6x at 0.8 2 bytes, floor 0.25: 1.0x at floor, 2.6x at 0.65 The floors differ despite the identical lane count because the two widths compete against different scalar strategies: `byte_compress_density_threshold` sends 1-byte elements to the byte-LUT at every density on x86, and that LUT is weak on a sparse mask, while 2-byte elements fall to the set-bit walk, which holds out to 0.25. A floor of 0.15 for 2-byte regressed 27%. Retuning that threshold would raise the 1-byte floor to match, and is noted in the docs. Also corrects two numbers in the previous commit that came from a single unreplicated run: the AVX2 8-byte `vpermd` kernel is 1.0x at its 0.45 floor and 1.4x at 0.65, not 1.4x and 2.0x. Two further runs agree. Verified no regression at any density on either x86 tier, and that the shared helper move left the NEON kernels' timings unchanged. `avx2_kernels_match_scalar` now covers the pshufb kernels directly, since a VBMI2 machine's dispatcher never selects them. Checks: cargo +nightly fmt --all --check; cargo clippy -p vortex-array --all-targets --all-features; cargo clippy --target x86_64-unknown-linux-gnu -p vortex-array --lib; cargo nextest run -p vortex-array -p vortex-buffer -p vortex-mask (4058 passed); rustdoc with -D warnings. On the AVX-512 machine: full vortex-array suite (3180 passed) and clippy --all-targets. Signed-off-by: Robert Kruszewski <robert@spiraldb.com>
be5e458 to
b471442
Compare
Port logic from Primitive types to Decimal types and adjust the way we choose
filter strategies from simple threshold for indices and slices