-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Zero-fill non-nullable string offsets with a nullable ancestor #23879
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
Merged
rapids-bot
merged 12 commits into
NVIDIA:main
from
mhaseeb123:bug/parquet-null-ancestor-string-gaps
Aug 31, 2026
+265
−11
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
05368fe
Pre-zero required string offsets under a nullable ancestor
mhaseeb123 887d602
Merge branch 'main' into bug/parquet-null-ancestor-string-gaps
mhaseeb123 67fd464
Remove unnecessary test
mhaseeb123 60ef3cd
minor
mhaseeb123 7820580
minor docs update
mhaseeb123 a1fe6f2
style
mhaseeb123 665bee9
minor syntax fix
mhaseeb123 5b7f7cf
clang-format for the trillionth time
mhaseeb123 b06df3b
Improve doc
mhaseeb123 67431e0
Fix tests
mhaseeb123 3d8a179
Minor
mhaseeb123 2d672a5
Merge branch 'main' into bug/parquet-null-ancestor-string-gaps
mhaseeb123 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -948,14 +948,33 @@ void reader_impl::allocate_columns(read_mode mode, size_t skip_rows, size_t num_ | |
| // Validity Buffer is a uint32_t pointer | ||
| std::vector<cudf::device_span<cudf::bitmask_type>> nullmask_bufs; | ||
|
|
||
| // An optional ancestor leaves unwritten output slots until the next repeated level. So, for a | ||
| // non-nullable STRING (FIELD) with a nullable ancestor, the column is nullable and not all rows | ||
| // will be decoded. The decoder may not detect this because it may not have a validity map from | ||
| // the ancestor. To avoid this, zero-fill such STRING buffers here as their uninitialized lengths | ||
| // are converted to offsets. No handling needed here for nullable strings (zero-filled by decoder | ||
| // using their own validity bitmap), fixed-width (masked), LIST offsets (never have gaps), and | ||
| // dictionary indices (have no ancestors). | ||
| auto const compute_has_unwritten_slots = [](auto const& out_buf, bool has_nullable_ancestor) { | ||
| return has_nullable_ancestor and out_buf.type.id() == type_id::STRING and | ||
| not out_buf.is_nullable; | ||
| }; | ||
| auto unwritten_bufs = cudf::detail::make_empty_pinned_vector<cudf::device_span<cuda::std::byte>>( | ||
| _input_columns.size(), _stream); | ||
|
|
||
| for (auto const& input_col : _input_columns) { | ||
| size_t const max_depth = input_col.nesting_depth(); | ||
|
|
||
| auto* cols = &_output_buffers; | ||
| auto* cols = &_output_buffers; | ||
| bool has_nullable_ancestor = false; | ||
| for (size_t l_idx = 0; l_idx < max_depth; l_idx++) { | ||
| auto& out_buf = (*cols)[input_col.nesting[l_idx]]; | ||
| cols = &out_buf.children; | ||
|
|
||
| auto const has_unwritten_slots = compute_has_unwritten_slots(out_buf, has_nullable_ancestor); | ||
| has_nullable_ancestor = | ||
| out_buf.type.id() == type_id::LIST ? false : (has_nullable_ancestor or out_buf.is_nullable); | ||
|
|
||
| // if this has a list parent, we have to get column sizes from the | ||
| // data computed during compute_page_sizes | ||
| if (out_buf.user_data & PARQUET_COLUMN_BUFFER_FLAG_HAS_LIST_PARENT) { | ||
|
|
@@ -976,6 +995,10 @@ void reader_impl::allocate_columns(read_mode mode, size_t skip_rows, size_t num_ | |
| out_buf.null_mask(), | ||
| cudf::util::round_up_safe(out_buf.null_mask_size(), sizeof(cudf::bitmask_type)) / | ||
| sizeof(cudf::bitmask_type)); | ||
| if (has_unwritten_slots and out_buf.data() != nullptr) { | ||
| unwritten_bufs.push_back( | ||
| {static_cast<cuda::std::byte*>(out_buf.data()), out_buf.data_size()}); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1068,10 +1091,19 @@ void reader_impl::allocate_columns(read_mode mode, size_t skip_rows, size_t num_ | |
| for (size_type idx = 0; idx < static_cast<size_type>(_input_columns.size()); idx++) { | ||
| auto const& input_col = _input_columns[idx]; | ||
| auto* cols = &_output_buffers; | ||
| // See the identically named variable in the non-list allocation loop above | ||
| bool has_nullable_ancestor = false; | ||
| for (size_type l_idx = 0; l_idx < static_cast<size_type>(input_col.nesting_depth()); | ||
| l_idx++) { | ||
| auto& out_buf = (*cols)[input_col.nesting[l_idx]]; | ||
| cols = &out_buf.children; | ||
|
|
||
| auto const has_unwritten_slots = | ||
| compute_has_unwritten_slots(out_buf, has_nullable_ancestor); | ||
| has_nullable_ancestor = out_buf.type.id() == type_id::LIST | ||
| ? false | ||
| : (has_nullable_ancestor or out_buf.is_nullable); | ||
|
|
||
| // if this buffer is part of a list hierarchy, we need to determine it's | ||
| // final size and allocate it here. | ||
| // | ||
|
|
@@ -1095,6 +1127,10 @@ void reader_impl::allocate_columns(read_mode mode, size_t skip_rows, size_t num_ | |
| out_buf.null_mask(), | ||
| cudf::util::round_up_safe(out_buf.null_mask_size(), sizeof(cudf::bitmask_type)) / | ||
| sizeof(cudf::bitmask_type)); | ||
| if (has_unwritten_slots and out_buf.data() != nullptr) { | ||
| unwritten_bufs.push_back( | ||
| {static_cast<cuda::std::byte*>(out_buf.data()), out_buf.data_size()}); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -1105,6 +1141,14 @@ void reader_impl::allocate_columns(read_mode mode, size_t skip_rows, size_t num_ | |
| cudf::host_span<cudf::device_span<cudf::bitmask_type> const>{nullmask_bufs}, _stream); | ||
| cudf::detail::batched_memset<cudf::bitmask_type>( | ||
| pinned_nullmask_bufs, std::numeric_limits<cudf::bitmask_type>::max(), _stream); | ||
|
|
||
| // Need to zero non-nullable string lengths with nullable ancestors | ||
| if (not unwritten_bufs.empty()) { | ||
| cudf::detail::batched_memset<cuda::std::byte>( | ||
|
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. And this is zeroing the inherited nulls. |
||
| cudf::host_span<cudf::device_span<cuda::std::byte> const>{unwritten_bufs}, | ||
| static_cast<cuda::std::byte>(0), | ||
| _stream); | ||
| } | ||
| } | ||
|
|
||
| void reader_impl::fill_pruned_offsets(size_t skip_rows, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Must use the block size of the kernel calling
zero_fill_null_positions_sharedThere 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.
OK so this is fixing the leaf node nulls.
Uh oh!
There was an error while loading. Please reload this page.
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.
Yes, this fills for all but non-nullable nested string leaves with nullable ancestors