Repository navigation
Read legacy vortex.decimal nodes into the canonical array or a Widen - #10995
Conversation
Merging this PR will degrade performance by 18.08%2 warnings
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
29d4a4a to
9c42fc9
Compare
bad5407 to
d2a4285
Compare
9c42fc9 to
fdf9ac2
Compare
d2a4285 to
3cfb191
Compare
eb6bfc5 to
b3b006c
Compare
3cfb191 to
852affb
Compare
b3b006c to
e50341e
Compare
852affb to
c6f1a4c
Compare
e50341e to
d030931
Compare
c6f1a4c to
6ec1784
Compare
6ec1784 to
ac9bc1e
Compare
73a9038 to
bf6bc81
Compare
ac9bc1e to
1c710a6
Compare
bf6bc81 to
0892933
Compare
1c710a6 to
05e0b63
Compare
0892933 to
a1837f2
Compare
1e28863 to
fbb5f7e
Compare
0ed981b to
280c0a8
Compare
4951602 to
7b2c2f1
Compare
107a5b0 to
73da43c
Compare
|
IIUC, i dont understand why this isn't decimal.v2 or similar since we want to enforce a new invariant? would also simplify plugin logic |
73da43c to
f6d0e12
Compare
|
I think if we can avoid introducing new serialized ids we should, this maximizes compatibility. The old decimal let you do whatever you wanted - this is tightening of the bounds so old readers can always read the current values and new readers use Widen to get the desired behaviour |
|
This LGTM now but would get someone with CUDA integration experience to review the CUDA part? |
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
Serde is moving out of vtables, so the Decimal vtable now returns None from serialize and bails in deserialize, as FoR and BitPacked do. DecimalPlugin owns the metadata, the serializer, and StoredDecimal, and the vtable serde tests move into the plugin tests. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
StoredDecimal now keeps the row count from the containing format, so into_array_parts uses it directly instead of deriving it from the buffer. The byte length check stays, because a legacy read still takes its row count from the buffer. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
a345eac to
a865895
Compare
|
oh wait I can make a cosmetic change first (store len in the stored decimal struct so we dont recalculate it / keep it close to the rest of the metadata) |
| /// A device buffer that holds its bytes on the host. A copy to the host succeeds unless | ||
| /// `fails_host_copy` is set. | ||
| #[derive(Debug, PartialEq, Eq, Hash)] | ||
| struct TestDeviceBuffer { |
There was a problem hiding this comment.
just remove these tests and the copy to host logic - we need to add handling for this in cuda executor not here
Choosing the child precision of a legacy node scans its valid values on the CPU. Copying device buffers to the host for that scan mixes host and device execution, so from_stored_decimal now returns an error for noncanonical values or validity in device memory. The CUDA executor can read legacy nodes itself later. A canonical node still keeps its device buffer. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
## Summary Removes the blanket `impl<V: VTable> ArrayPlugin for V` and the `serialize` / `deserialize` methods on `VTable`. Every serializable encoding now implements `ArrayPlugin` explicitly on its vtable type, in a `plugin.rs` child module next to its `impl VTable`. Redo of #10111 on current develop. ## Changes - `VTable` covers only in-memory behaviour; serde is `impl ArrayPlugin for X`. - Serde bodies are ported verbatim, so wire formats are unchanged. - `read_ids` / `write_ids` default to `vec![self.id()]`, what the blanket impl declared. Plugins with one wire ID no longer spell them out, including `ALPPatchedPlugin`, `ScalarFnArrayPlugin` and the Python stub. - Inside an impl the wire ID is `ArrayPlugin::id(self)`; only the `id` method itself delegates to `VTable::id`. - `session.arrays().register(X)` is unchanged for every encoding that was serializable. - In-memory-only vtables lose the two stub methods and get no plugin: Filter, Slice, Interleave, ScalarFn, ScalarFnValidity, ForeignArray, PiecewiseSequence, Widen, Shared, test-harness vtables. - `DecimalPlugin` (#10995) becomes `impl ArrayPlugin for Decimal`: one wire ID, same legacy-node reads, no separate type. Its module, `StoredDecimal` and tests stay where they are. - Multi-ID plugins (`BitPackedPlugin`, `FoRPlugin`, `DecimalBytePartsPlugin`) and `WidenPlugin` are untouched; `ALPPatchedPlugin` now delegates to `ArrayPlugin::deserialize(&ALP, ..)`. - `PythonVTable` keeps a stub plugin so the Python `register` API still compiles. - Vtable files that gained a `plugin.rs` child are directory modules (`array.rs` becomes `array/mod.rs`, 14 pure renames). ALP's own impl and `ALPPatchedPlugin` share `alp/array/plugin.rs`. - Tests that called the vtable serde directly (ALP, ALPRD, Dict, List, RunEnd, VarBin, RLE, serde.rs, unary, zstd buffers) go through `ArrayPlugin` instead. | | count | |---|---| | new `impl ArrayPlugin` (vortex-array) | 20 | | new `impl ArrayPlugin` (encodings + python) | 17 | | vtables that only lose the stub methods | 14 | ## API changes - `VTable` loses `serialize` and `deserialize`. An external vtable that was registered through the blanket impl must now `impl ArrayPlugin` to be registrable. - `ArrayPlugin::read_ids` / `write_ids` gain defaults of `vec![self.id()]`. - `vortex_array::arrays::DecimalPlugin` is removed; register `Decimal` instead. ## Behaviour changes - `PiecewiseSequence` is no longer registered by `ArraySession::default()`; it never serialized, and its error is now "is not registered for serialization". - The writer seeds the footer's encoding list from the session registry, so dropping `vortex.piecewise-sequence` makes every default-session file 48 bytes smaller. `test_stock_ticker_file_size` expectations move accordingly (70060 → 70012, 69996 → 69948, 55144 → 55096). - `DecimalByteParts`, `BitPacked` and `FoR` can no longer be registered bare, so `bare_vtable_refuses_serde` and `vtable_serde_requires_plugin` are removed. ## Testing - `cargo clippy --all-targets --all-features -- -D warnings` and `cargo +nightly-2026-09-10 fmt --check` on every touched crate (vortex-array, vortex-file, vortex-layout, vortex-python and the 13 encodings crates). - `cargo nextest run` on all touched crates plus vortex-ipc and vortex-scan: 8981 passed, 0 failed; `cargo test --doc` on vortex-array, fastlanes, decimal-byte-parts and alp. - `cargo nextest run -p vortex-file -E 'test(test_stock_ticker_file_size)'` with and without `--features zstd`, to derive the new sizes. ## Follow-up - Pre-existing, unchanged here: the Patched, RLE, OnPair, ALPRD, FSST and Zstd deserializers index children or buffers before checking their counts, so a malformed node panics in `SerializedArray::child` instead of returning an error. Worth hoisting the count checks in a separate PR. - Fold `FoRPlugin` / `BitPackedPlugin` / `DecimalBytePartsPlugin` into `impl ArrayPlugin for FoR` etc. if wanted. - Overlaps with #11298, which makes the same change with inline impls and trait defaults for `read_ids` / `write_ids`. --------- Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Summary
Every decimal leaf must now have the canonical width of its precision, which makes legacy
vortex.decimalnodes at other widths invalid as leaves. Those nodes still exist: old files store a decimal at whatever width held its values, andWidenPluginwrites a decimalWidenas a legacy node, sincevortex.widenhas no wire form yet. Reading a legacy node into the canonical array or a decimalWidenkeeps it readable.Changes
The reader is a
DecimalPluginbecause a vtable'sdeserializemust return its own array type, while a plugin can return aWiden. It picks the child withWidenArray::from_stored_decimal, like DecimalByteParts decoding (#10905), so both paths give the same values the same precision. The plugin also owns the serializer, so theDecimalvtable has no serde, as withFoR. The plugin checks the wire layout first, so a malformed node returns an error instead of panicking.Known cost: each legacy read scans its valid values once, because stored statistics are attached only after the plugin returns. This also applies to IPC and writes without a compressor, not only to old files.
Choosing the child precision of a legacy node scans its valid values on the CPU, so a legacy node in device memory now returns an error instead of being copied to the host. Known regression: a CUDA read of a legacy node, which worked before, fails until the CUDA executor reads legacy decimal nodes itself.
Follow-up: a legacy node can now read as a
Widen, which has no Python subclass.API Changes
The default session registers the new public
DecimalPluginin place ofDecimal. TheDecimalvtable no longer serializes or deserializes, so a session that registersDecimaldirectly cannot read or write decimal arrays.