[Parquet] ALP encoder/decoder support - #9372
Conversation
|
amaaazing |
|
@alamb I worked with codex on replicating the c++ implementation reviewing commit by commit. I'll do one final read myself, but this should be ready for initial review. |
|
Thank you -- I will put this on my short list to reivew I was out last week |
|
@devanbenz Sorry for taking so long to address your comments. I'll make sure to work on it this week! |
devanbenz
left a comment
There was a problem hiding this comment.
Comment about header regarding publication of spec.
|
Thanks @devanbenz, I've finally addressed your reviews! |
Please cargo fmt the code 🫡 |
|
|
🚀 |
alamb
left a comment
There was a problem hiding this comment.
Update:
- I created a smaller parquet-testing file that exercises more corner cases here: apache/parquet-testing#119
- I started trying to consolidate the parquet reader / round trip tests: #10540
@sdf-jkl for this PR i would like to propose:
- Update the tests to use apache/parquet-testing#119 (I will be working on getting some more consensus about that on the parquet mailing list)
- Add a little more end to end test coverage (round trip more data through ALP and ensure the data comes through completely)
I am happy to work on the testing (and push directly to this branch) if that is ok with you, but I wanted to check with you first
Feedback addressed, re-review requested
Added more coverage here - 5a10e68 |
|
I will have some time to review this tomorrow or friday FYI :) |
|
I merged up from main to get the most recent changes, and I am now reviewing this one more time |
|
|
||
| const MAX_EXPONENT: u8 = ALP_MAX_EXPONENT_F32; | ||
| const MAGIC_NUMBER: Self = 12582912.0; // 2^22 + 2^23 | ||
| const ENCODING_UPPER_LIMIT: Self = 2147483520.0; |
There was a problem hiding this comment.
The new clippy lints claim this value is out of the precision range of f32 (and similarly for the others)
I did what it told me in 68010c9
--> parquet/src/encodings/alp.rs:496:40
|
496 | const ENCODING_UPPER_LIMIT: Self = 2147483520.0;
| ^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.97.0/index.html#lossy_float_literal
= note: requested on the command line with `-W clippy::lossy-float-literal`
help: consider changing the type or replacing it with
|
496 - const ENCODING_UPPER_LIMIT: Self = 2147483520.0;
496 + const ENCODING_UPPER_LIMIT: Self = 2_147_483_500.0;
|
alamb
left a comment
There was a problem hiding this comment.
Thank you @sdf-jkl and @devanbenz -- I went through this some more and I think it is really nice. THank you so much.
I will wait for @devanbenz 's review as well, but I think this is now ready to merge once the upstream parquet-testing PR has merged
👏 -- I think we will have the first open source ALP implementation delivered
In terms of next steps, I plan to focus on the blog post next
| /// floating-point columns. | ||
| /// | ||
| /// [`ParquetValueType`]: crate::data_type::private::ParquetValueType | ||
| pub trait GetEncoder { |
There was a problem hiding this comment.
I asked claude about this -- it says this trait is needed because
Since get_encoder<T: DataType> is generic, every arm of its match must type-check for every T it's monomorphized with. So this doesn't compile:
Encoding::ALP => match T::get_physical_type() {
Type::FLOAT | Type::DOUBLE => Box::new(AlpEncoder::new()), // error: `i32: AlpFloat` not satisfied
...
}Because we basically need to only invoke this for certain T types (f32 and f64)
|
Here are some follow ups suggested by claude
Since an all-null leading page is a realistic input, this is probably something we should fix (as a follow on PR)
|
|
Before we proceed to the benchmarks for the blog, should we work on some follow up Perf 🚀🚀🚀 PRs? I have a related PR for improving bit packing speed. I started working on it to bring it to ALP later. |
Well, clearly having better performnce for the blog would be good :) But at the moment I have no idea where we stand compared to ZSTD in the rust implementation so I would probably build the benchmark scripts first and then decide if we needed to optimize more before publishing It would also be interesting to show the effect of hardware (aka can this implementaton take advantage of SIMD instructions) |
I was trying to follow the ALP spirit as much as possible and make everything vectorizable. Bit packing should be the last thing not vectorized yet. |
The results would be dataset dependent. Not sure we can get a reliable benchmark using random generated ones. |
Yes for sure -- I think we can start with using data from the paper as a starting point (they list several datasets) |
Some of them are here - https://github.com/cwida/ALP/tree/main/benchmarks I'll work on the benchmarks PR |
I think there is two usecases:
|
devanbenz
left a comment
There was a problem hiding this comment.
Just the one comment, spent the morning looking over this PR and playing around with it locally. It is a very large body of work, looks good to me. My comment is non-blocking but maybe something to look in to.
| + self.streaming.estimated_memory_size() | ||
| } | ||
|
|
||
| fn flush_buffer(&mut self) -> Result<Bytes> { |
There was a problem hiding this comment.
Can this ever be called when an encoder has no values set or null value? For example, if you create a new encoder with AlpEncoder::<DoubleType>::new() and call flush_buffer all while later pages have well behaved data. Will it cause issues? Could you add a test for this if it's possible?
There was a problem hiding this comment.
Maybe something like:
#[test]
fn test_empty_first_page() {
let mut encoder = AlpEncoder::<DoubleType>::new();
// First page flushed with no values
let empty = encoder.flush_buffer().unwrap();
assert_eq!(empty.len(), ALP_HEADER_SIZE);
// Later pages carry well-behaved decimal data.
let values: Vec<f64> = (0..1500).map(|i| (i as f64) * 0.01).collect();
encoder.put(&values).unwrap();
let page = encoder.flush_buffer().unwrap();
assert!(page.len() < values.len() * 8);
}
Which issue does this PR close?
Rationale for this change
check issue
What changes are included in this PR?
Are these changes tested?
Are there any user-facing changes?