Fix use-after-destroy and stream ordering in Parquet IO utils - #22529
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughBatches host reads for parquet byte-range transfers: adds CUDA memcpy and tuple includes, reserves merged-range vectors, collects host-read futures returning owning buffers, resolves them to extract source pointers, and enqueues a single cudf::detail::memcpy_batch_async to copy to device destinations. Deferred return waits only on device-read futures. ChangesHost-to-Device Batched Transfer
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cpp/src/io/parquet/io_utils/parquet_io_utils.cpp (1)
136-184:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGate the blocking synchronizations to the paths that need them.
Line 137 now blocks even when every range uses
host_read_async, and Lines 174-175 block even when every range usesdevice_read_async, so this*_asynchelper can end up fully synchronous before it returns. Those waits also happen while the static mutex from Line 142 is held, which serializes concurrent callers for the entire host-read/H2D phase.Please restrict the pre-sync to the device-read path, keep the post-copy sync inside the host-read branch, and release the mutex once the async reads have been enqueued. As per coding guidelines, "Avoid unnecessary host-device synchronization that blocks the GPU pipeline".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/io/parquet/io_utils/parquet_io_utils.cpp` around lines 136 - 184, The current implementation calls stream.synchronize() unconditionally and holds mutex across async enqueues and waits, causing unnecessary blocking; change the logic in the function that builds device_read_tasks/host_read_tasks so that (1) the initial stream.synchronize() is only invoked when at least one device read is scheduled (i.e. when datasource.supports_device_read() && is_device_read_preferred(...) leads to any device_read_tasks), (2) release the std::lock_guard<std::mutex> mutex immediately after enqueuing both device_read_tasks and host_read_tasks so the critical section does not cover subsequent waits or host-device copies, and (3) move the second stream.synchronize() so it only runs inside the branch that performed host reads (immediately after calling cudf::detail::memcpy_batch_async and before host_read_buffers/other temporaries go out of scope); keep the deferred sync_function that waits on device_read_tasks unchanged but ensure it is created after the mutex is released.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@cpp/src/io/parquet/io_utils/parquet_io_utils.cpp`:
- Around line 136-184: The current implementation calls stream.synchronize()
unconditionally and holds mutex across async enqueues and waits, causing
unnecessary blocking; change the logic in the function that builds
device_read_tasks/host_read_tasks so that (1) the initial stream.synchronize()
is only invoked when at least one device read is scheduled (i.e. when
datasource.supports_device_read() && is_device_read_preferred(...) leads to any
device_read_tasks), (2) release the std::lock_guard<std::mutex> mutex
immediately after enqueuing both device_read_tasks and host_read_tasks so the
critical section does not cover subsequent waits or host-device copies, and (3)
move the second stream.synchronize() so it only runs inside the branch that
performed host reads (immediately after calling cudf::detail::memcpy_batch_async
and before host_read_buffers/other temporaries go out of scope); keep the
deferred sync_function that waits on device_read_tasks unchanged but ensure it
is created after the mutex is released.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 3ecc1eea-9a86-4ce3-aed7-7b7a5e03514c
📒 Files selected for processing (1)
cpp/src/io/parquet/io_utils/parquet_io_utils.cpp
3d54fcf to
be7694a
Compare
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
vuule
left a comment
There was a problem hiding this comment.
Having a hard time convincing myself that there's no performance impact here in any case, but the changes look good.
Do you have any performance numbers?
Let me gather and paste some here |
Performance ImpactThis PR shows a rough ~12% end-to-end improvement when reading 16 x (1GB disk size) Parquet files (clickbench) using the Note: Perf for
CC: @vuule |
|
/merge |
65f8363
into
NVIDIA:release/26.06
…#22529) This PR fixes the use-after-destroy and stream ordering (with PTDS input) issue (with host buffer source) in the `fetch_byte_ranges_to_device_async` IO utility used by parquet and hybrid scan. See follow up PR NVIDIA#22550 that reduces the locked region size by moving all `host_read_async` outside it. Authors: - Muhammad Haseeb (https://github.com/mhaseeb123) Approvers: - Bradley Dice (https://github.com/bdice) - Amin Aramoon (https://github.com/aminaramoon) - Vukasin Milovanovic (https://github.com/vuule) URL: NVIDIA#22529
Follow up #22529 This PR optimizes the `fetch_byte_ranges_to_device_async` parquet IO utility by moving all host buffer reads outside the locked region. This PR adds another ~5% end to end speed improvement over the results posted at #22529 (comment) Authors: - Muhammad Haseeb (https://github.com/mhaseeb123) Approvers: - Vukasin Milovanovic (https://github.com/vuule) - Vyas Ramasubramani (https://github.com/vyasr) URL: #22550

Description
This PR fixes the use-after-destroy and stream ordering (with PTDS input) issue (with host buffer source) in the
fetch_byte_ranges_to_device_asyncIO utility used by parquet and hybrid scan.See follow up PR #22550 that reduces the locked region size by moving all
host_read_asyncoutside it.Checklist