perf(parquet): improve decoding performance for parquet#6759
perf(parquet): improve decoding performance for parquet#6759
Conversation
…fer handling in decompress_buffer
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…ng and lookup table
…messages for insufficient prefix and suffix values
… messages for insufficient length values
|
@coderabbitai please review |
|
@coderabbitai pls do a critical review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainI'll conduct a thorough critical review of this PR. Let me start by examining the code changes, particularly focusing on the areas with low test coverage and the bug fix mentioned. 🏁 Script executed: Length of output: 757 🏁 Script executed: Length of output: 130 🏁 Script executed: Length of output: 919 🏁 Script executed: Length of output: 71 🏁 Script executed: Length of output: 75 🏁 Script executed: Length of output: 118 🏁 Script executed: Length of output: 62 🏁 Script executed: Length of output: 15334 🏁 Script executed: Length of output: 36574 🏁 Script executed: Length of output: 6224 🏁 Script executed: Length of output: 4946 🏁 Script executed: Length of output: 6223 🏁 Script executed: Length of output: 3790 🏁 Script executed: Length of output: 143 🏁 Script executed: Length of output: 47 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 3106 🏁 Script executed: Length of output: 1231 🏁 Script executed: Length of output: 4582 🏁 Script executed: Length of output: 2994 🏁 Script executed: Length of output: 427
🚨 Critical: Zero Test Coverage on Core Performance PathThe delta_bitpacked decoder has 0% coverage with 65 new lines. This is unacceptable for code that's in the critical decode path. The new
Question: Why is there no test coverage here? The existing tests only cover the iterator interface, not the batch decoder. 🚨 Trusting Parquet Metadata Without ValidationLine 2100-2103 in let nc = page.header.null_count();
if nc == Some(0) {
return Ok(None);
}You're trusting parquet metadata to skip definition level decoding. What happens with:
This optimization trades correctness for performance. Have you validated this against malformed inputs? Where are the fuzz tests? 🚨 Undocumented Bug FixThe PR description mentions:
Where is this bug fix? I can't find:
This is a data corruption bug and needs full documentation and regression tests.
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 16326 🏁 Script executed: Length of output: 26789 🏁 Script executed: Length of output: 32314 🏁 Script executed: Length of output: 4559
Critical Issues1. Zero Test Coverage on Critical Performance Path
|
|
@coderabbitai pls do a comprehensive review, appreciate your prompt response. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain
🏁 Script executed: Length of output: 2591 🏁 Script executed: Length of output: 15380 🏁 Script executed: Length of output: 507 🏁 Script executed: Length of output: 18293 🏁 Script executed: Length of output: 14671 🏁 Script executed: Length of output: 49855 🏁 Script executed: Length of output: 580 🏁 Script executed: Length of output: 62618 🏁 Script executed: Length of output: 7971 🏁 Script executed: Length of output: 6263 🏁 Script executed: Length of output: 10721 🏁 Script executed: Length of output: 70 🏁 Script executed: Length of output: 4301 🏁 Script executed: Length of output: 1296 🏁 Script executed: Length of output: 34550
🔴 CRITICAL: Zero Test Coverage on Batch DecoderFile: Lines 99-147:
|
|
@RaphDal — reviewed the Rust changes in this PR. Each finding was verified against the code on the PR branch. Bugs1.
Fix: bounds-check before the read, e.g.: if self.pos + size_of::<u32>() > self.data.len() {
// return error
}
let len = u32::from_le_bytes(self.data[self.pos..self.pos + 4].try_into().unwrap()) as usize;2.
Fix: store the values length and validate up front or in push paths. 3. Copy-paste error message (page.rs)
Error::oos("The number of bytes declared in v1 rep levels is higher than the page size")
// ^^^ should be "def levels"Improvements4.
5.
6. Duplicated Byte-identical 256-entry LUT in both files. Could be extracted to a shared module. 7. Test coverage gaps Good combinatorial coverage for single-column, single-row-group files. Missing:
|
|
@RaphDal The two |
|
Thanks for the review !
These branches should truly if !self.ensure_run() { // 1st case - returns if it fails
return;
}
let run_available = self.run.remaining().min(self.remaining_values);
if run_available == 0 { // 2nd case - iterate again if it fails
self.run = RleBooleanRun::None;
continue;
}
let take = count.min(run_available);
match &mut self.run {
RleBooleanRun::Rle { remaining, .. } => {
// Repeated run skip is just counter arithmetic.
*remaining -= take;
}
RleBooleanRun::Bitpacked { byte_offset, bit_offset, remaining, .. } => {
// Bitpacked run skip advances bit/byte cursors.
Self::skip_bitpacked(byte_offset, bit_offset, take);
*remaining -= take;
}
RleBooleanRun::None => unreachable!(),
}Unless I'm mistaken, returning an error would only bring unnecessary overhead to the caller. |
[PR Coverage check]😍 pass : 3364 / 4476 (75.16%) file detail
|
This PR brings several improvements to parquet support, improving decoding performance.
Changes
null_count=0.Plain/DeltaBinaryPacked/Rle/RleDictionarydecoders implementation for better performance.DeltaBinaryPacked.PlainPrimitiveDecoder.Benchmarks
Ran on Apple M5, there is a lot of jitter.
Decoding
Iteration: decode 100k values (default page size).
arraydelta_lenarraydelta_lenarrayplainarrayplainbinarydelta_lenbinarydelta_lenbinaryplainbinaryplainbooleanplainbooleanrlebytedelta_bpbytedictbytedictbytedictbytedictbytedictbytedictbytedictbytedictbyteplainchardelta_bpcharplaindatedelta_bpdatedelta_bpdatedictdatedictdatedictdatedictdatedictdatedictdatedictdatedictdateplaindateplaindoubledictdoubledictdoubledictdoubledictdoubledictdoubledictdoubledictdoubledictdoubleplaindoubleplainfloatdictfloatdictfloatdictfloatdictfloatdictfloatdictfloatdictfloatdictfloatplainfloatplaingeobytedelta_bpgeobytedelta_bpgeobyteplaingeobyteplaingeointdelta_bpgeointdelta_bpgeointplaingeointplaingeolongdelta_bpgeolongdelta_bpgeolongplaingeolongplaingeoshortdelta_bpgeoshortdelta_bpgeoshortplaingeoshortplainintdelta_bpintdelta_bpintdictintdictintdictintdictintdictintdictintdictintdictintplainintplainipv4delta_bpipv4delta_bpipv4plainipv4plainlong128plainlong128plainlong256plainlong256plainlongdelta_bplongdelta_bplongdictlongdictlongdictlongdictlongdictlongdictlongdictlongdictlongplainlongplainshortdelta_bpshortdictshortdictshortdictshortdictshortdictshortdictshortdictshortdictshortplainstringdelta_lenstringdelta_lenstringplainstringplainsymboldictsymboldicttimestampdelta_bptimestampdelta_bptimestampdicttimestampdicttimestampdicttimestampdicttimestampdicttimestampdicttimestampdicttimestampdicttimestampint96_dicttimestampint96_dicttimestampint96_dicttimestampint96_dicttimestampint96_dicttimestampint96_dicttimestampint96_dicttimestampint96_dicttimestampint96_plaintimestampint96_plaintimestampplaintimestampplainuuidplainuuidplainvarchardelta_lenvarchardelta_lenvarcharplainvarcharplainClickbench
LZ4 - No JIT
UNCOMPRESSED - JIT
JIT Enabled (raw):