Skip to content

Support device buffer for the new libcurl multi poll-based backend (5/n) - #966

Merged
rapids-bot[bot] merged 88 commits into
rapidsai:mainfrom
kingcrimsontianyu:multi-poll-device
Aug 5, 2026
Merged

Support device buffer for the new libcurl multi poll-based backend (5/n)#966
rapids-bot[bot] merged 88 commits into
rapidsai:mainfrom
kingcrimsontianyu:multi-poll-device

Conversation

@kingcrimsontianyu

@kingcrimsontianyu kingcrimsontianyu commented May 24, 2026

Copy link
Copy Markdown
Contributor

This PR supports device buffer for the multi poll-based remote I/O backend. In addition, this PR makes 5 improvements:

  • Refactor ConcurrentRequestLimiter with RAII Slot. try_acquire() now returns a move-only Slot that auto-releases on destruction. Acquire/release pairing is now structurally enforced.
  • Add a new CurlMultiAttachment RAII guard, that removes the easy handle from the multi handle on destruction.
  • Batch the transfer submission to reduce the number of curl_multi_wakeup calls from the initial "per sub-range" to the current "per reactor, per pread".
  • Copy _inbox (shared by the submission thread and reactor thread) to a reactor-private _pending deque, to decouple the submission latency from reactor's admission walk.
  • Use AI to significantly condense the comments and remove incorrect, unclear or redundant comments.

@kingcrimsontianyu
kingcrimsontianyu marked this pull request as ready for review July 20, 2026 03:33
@kingcrimsontianyu
kingcrimsontianyu requested review from a team as code owners July 20, 2026 03:33
@kingcrimsontianyu

Copy link
Copy Markdown
Contributor Author

Sending this PR to draft. The reactor thread logic is very messy and can potentially be improved by RAII. Refactoring to have a cleaner code now.

@kingcrimsontianyu
kingcrimsontianyu marked this pull request as draft July 20, 2026 19:09
@vuule vuule moved this from Burndown to Slip in libcudf Jul 20, 2026
@kingcrimsontianyu

kingcrimsontianyu commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Performance results

  • 2026.07.24 cudf-polars TPC-H queries. "v2" is simply a rerun.
t: number of threads
r: number reactors
c: connections
+----------------------+----------+------------+--------------+
| Case                 | Date     | Total (s)  | Δ from base  |
+----------------------+----------+------------+--------------+
| easy_256t (baseline) | 07.25    |  278.6069  |     ——       |
| easy_256t (v2)       | 07.25    |  272.7131  |    -2.12%    | --> best result
| easy_256t            | 07.24    |  284.9486  |    +2.28%    |
| easy_256t (v2)       | 07.24    |  293.2409  |    +5.25%    |
+----------------------+----------+------------+--------------+
| mp_16r/512c          | 07.25    |  294.6391  |    +5.75%    |
| mp_16r/512c (v2)     | 07.25    |  291.7514  |    +4.72%    |
| mp_16r/512c          | 07.24    |  302.0218  |    +8.40%    |
| mp_16r/512c (v2)     | 07.24    |  300.1998  |    +7.75%    |
| mp_16r/256c          | 07.25    |  282.7292  |    +1.48%    |
| mp_16r/256c (v2)     | 07.25    |  280.0991  |    +0.54%    | --> best result
+----------------------+----------+------------+--------------+

The results above show that: (1) For EC2-S3, there exists certain level of network bandwidth variance. (2) Comparing the best results, the new multi-poll backend is ~3% slower than the existing easy thread pool backend, with the benefit of thread usage cut by a factor of 16.

@kingcrimsontianyu
kingcrimsontianyu marked this pull request as ready for review July 24, 2026 19:41
@kingcrimsontianyu

Copy link
Copy Markdown
Contributor Author

Event pool vs on-the-fly creation/destruction

On a temporary branch, the event pool was modified such that the get() method creates a CUDA event and put() method destroys it. Comparing use of event pool vs on-the-fly event creation/destruction on cudf-polars TPC-H SF-1k, no statistically significant difference in performance was observed.

  • Event pool runs: total time ranges 280~285 seconds.
  • On-the-fly creation/destruction: total time ranges 284~287 seconds.

cc @madsbk

/**
* @brief Construct a barrier carrying `cuda_context` as metadata.
*
* @param cuda_context The CUDA context that pred's H2Ds will land in. Stored only for callers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param cuda_context The CUDA context that pred's H2Ds will land in. Stored only for callers
* @param cuda_context The CUDA context that pread's H2Ds will land in. Stored only for callers

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

explicit RemoteMultiAggregateContext(std::size_t num_subranges);

/**
* @brief Per-pread event watermark for the device-buffer path.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "watermark" a common term of art here? I'm not familiar with what this means, it might be made up by an LLM?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No jargon used here. The per-pread, per-thread event from the IoEventBarrier is simply used repeatedly this way:

H2D (from pread 1) -> record event 1 -> 
H2D (from pread 1) -> record the same event (from pread 1) ->
H2D (from pread 2), record event 2"
...

Anyway I've removed the mention of "watermark" to avoid confusion.

{
}

ConcurrentRequestLimiter::Slot& ConcurrentRequestLimiter::Slot::operator=(Slot&& o) noexcept

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use other instead of o. One character variables are generally not favored in our codebases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Also replaced other uses of o with other.

Comment thread cpp/src/detail/multi_poll_reactor.cpp Outdated
detach_from_multi(_multi, _easy);
}

CurlMultiAttachment::CurlMultiAttachment(CurlMultiAttachment&& o) noexcept

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use other here and throughout instead of o.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread cpp/src/detail/multi_poll_reactor.cpp Outdated
{
}

CurlMultiAttachment& CurlMultiAttachment::operator=(CurlMultiAttachment&& o) noexcept

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to explicitly = delete; the copy constructor and copy assignment? (Trying to remember the rules.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The move functions cause the copy functions to be deleted, but the rule of five recommends explicit deletion for readability.

Comment thread cpp/src/detail/multi_poll_reactor.cpp Outdated
}
}

// Admission walk over the reactor-private _pending. Each entry is either admitted to libcurl

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Admission walk over the reactor-private _pending." What does this mean? See if you can rewrite this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified a bit.

@kingcrimsontianyu
kingcrimsontianyu requested a review from bdice July 27, 2026 21:24
@vuule
vuule self-requested a review July 31, 2026 01:40

@vuule vuule left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, just one potential concern

Comment thread cpp/src/remote_handle.cpp
return std::async(std::launch::deferred,
[fut = std::move(fut), io_event_barrier]() mutable -> std::size_t {
auto const n = fut.get();
io_event_barrier->sync_all_events();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sync_all_events() is documented as context-agnostic, but it synchronizes events created in the pread’s CUDA context. Make IoEventBarrier::sync_all_events() push _cuda_context before synchronizing its events, and add coverage where the future is consumed on a thread with no current CUDA context.

@kingcrimsontianyu kingcrimsontianyu Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this lambda with deferred policy is evaluated at the future.get() time, where the CUDA context is allowed to be different from pread's. I've updated the doc to clarify what context-agnostic means.

I've added a unit test for sync_all_events() which is called when (1) there is no context current on the calling thread, (2) there is a different context.

@kingcrimsontianyu
kingcrimsontianyu requested a review from vuule July 31, 2026 16:02
@kingcrimsontianyu

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit 9af385c into rapidsai:main Aug 5, 2026
64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Affects the C++ API of KvikIO feature request New feature or request non-breaking Introduces a non-breaking change

Projects

Status: Slip

Development

Successfully merging this pull request may close these issues.

4 participants