-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix Parquet V2 inputs to decompression scratch queries #24006
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -732,7 +732,7 @@ rmm::device_uvector<size_t> compute_decompression_scratch_sizes( | |
| auto temp_cost = cudf::detail::make_pinned_vector_async<size_t>(pages.size(), stream); | ||
| auto h_decomp_info = cudf::detail::make_pinned_vector(decomp_info, stream); | ||
| std::transform(h_decomp_info.begin(), h_decomp_info.end(), temp_cost.begin(), [](auto const& d) { | ||
| return cudf::io::detail::get_decompression_scratch_size(d); | ||
| return d.num_pages == 0 ? 0 : cudf::io::detail::get_decompression_scratch_size(d); | ||
| }); | ||
|
|
||
| rmm::device_uvector<size_t> d_temp_cost = | ||
|
|
@@ -756,25 +756,22 @@ rmm::device_uvector<size_t> compute_decompression_scratch_sizes( | |
| decomp_sum{}, | ||
| stream); | ||
|
|
||
| // Collect pages with matching codecs | ||
| // Use the same page selection for the input spans and decompression-size estimates. | ||
| rmm::device_uvector<device_span<uint8_t const>> temp_spans(pages.size(), stream); | ||
| auto iter = cuda::counting_iterator{size_t{0}}; | ||
| thrust::for_each( | ||
| rmm::exec_policy_nosync(stream, cudf::get_current_device_resource_ref()), | ||
| iter, | ||
| iter + pages.size(), | ||
| [pages = pages.begin(), | ||
| chunks = chunks.begin(), | ||
| temp_spans = temp_spans.begin(), | ||
| codec] __device__(size_t i) { | ||
| auto const& page = pages[i]; | ||
| if (parquet_compression_support(chunks[page.chunk_idx].codec).first == codec) { | ||
| temp_spans[i] = device_span<uint8_t const>( | ||
| page.page_data, static_cast<size_t>(page.compressed_page_size)); | ||
| } else { | ||
| temp_spans[i] = device_span<uint8_t const>(); // Mark pages with other codecs as empty | ||
| } | ||
| }); | ||
| thrust::for_each(rmm::exec_policy_nosync(stream, cudf::get_current_device_resource_ref()), | ||
| iter, | ||
| iter + pages.size(), | ||
| [pages = pages.begin(), | ||
| chunks = chunks.begin(), | ||
| temp_spans = temp_spans.begin(), | ||
| codec] __device__(size_t i) { | ||
| auto const& page = pages[i]; | ||
| temp_spans[i] = | ||
| parquet_compression_support(chunks[page.chunk_idx].codec).first == codec | ||
| ? get_decompression_input{}(page) | ||
| : device_span<uint8_t const>{}; | ||
| }); | ||
| // Copy only non-null spans | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| rmm::device_uvector<device_span<uint8_t const>> page_spans(pages.size(), stream); | ||
| auto end_iter = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -217,7 +217,7 @@ void detect_malformed_pages(device_span<PageInfo const> pages, | |
| /** | ||
| * @brief Computes the per-page scratch space required for decompression. | ||
| */ | ||
| rmm::device_uvector<size_t> compute_decompression_scratch_sizes( | ||
| CUDF_EXPORT rmm::device_uvector<size_t> compute_decompression_scratch_sizes( | ||
| device_span<ColumnChunkDesc const> chunks, | ||
| device_span<PageInfo const> pages, | ||
| cuda::stream_ref stream); | ||
|
|
@@ -416,14 +416,31 @@ struct codec_stats { | |
| host_span<bool const> page_mask); | ||
| }; | ||
|
|
||
| /** | ||
| * @brief Returns the compressed values passed to the decompressor, excluding V2 level bytes. | ||
| */ | ||
| struct get_decompression_input { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The definition of "what the decompressor actually consumes" now lives in three places: Declaring the functor |
||
| __device__ inline device_span<uint8_t const> operator()(PageInfo const& page) const | ||
| { | ||
| auto const is_compressed = (page.flags & PAGEINFO_FLAGS_V2) ? page.is_compressed : true; | ||
| auto const offset = | ||
| page.lvl_bytes[level_type::DEFINITION] + page.lvl_bytes[level_type::REPETITION]; | ||
| if (not is_compressed or page.compressed_page_size <= offset) { return {}; } | ||
| return {page.page_data + offset, static_cast<size_t>(page.compressed_page_size - offset)}; | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * @brief Functor which retrieves per-page decompression information. | ||
| */ | ||
| struct get_decomp_info { | ||
| device_span<ColumnChunkDesc const> chunks; | ||
| __device__ inline decompression_info operator()(PageInfo const& p) const | ||
| { | ||
| return {parquet_compression_support(chunks[p.chunk_idx].codec).first, | ||
| auto const codec = parquet_compression_support(chunks[p.chunk_idx].codec).first; | ||
| if (get_decompression_input{}(p).empty()) { return {codec, 0, 0, 0}; } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With The gap is small (such pages carry only level bytes) and the previous code overestimated, so this is not a blocker, but it is the same class of divergence the PR sets out to remove and is worth either closing or calling out in the comment. |
||
| // Keep the full page size as a conservative bound, as in codec_stats::add_pages. | ||
| return {codec, | ||
| 1, | ||
| static_cast<size_t>(p.uncompressed_page_size), | ||
| static_cast<size_t>(p.uncompressed_page_size)}; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding
compression_type,write_v2_headers, andpage_level_compressionas single-value axes is forced by sharing one callable across two registrations, but it changes every existingparquet_read_subrowgroup_chunksstate string and so breaks continuity with previously recorded results. A thin wrapper function that hardcodes the Snappy/V1 values for the original benchmark would keep the old axis set intact.