vllm.v1.conf_compute_utils
¶
Keep non-blocking host<->device copies non-blocking under NVIDIA CC.
Under bounce-buffer Confidential Computing, cudaMemcpyAsync is
host-synchronous: it returns only after the copy and all prior work on its
stream complete. install() patches torch's non_blocking copy entry points
so that:
- H2D runs on an idle per-device staging stream, so the host waits only for the transfer. The current stream then waits on the staging stream. A copy into an existing tensor lands in a staging tensor and is moved by a D2D on the current stream, so it is ordered exactly like the original copy. This applies to every caller.
- D2H issued on a stream created with
new_external_d2h_stream()is issued by a worker thread once the source is ready. Event / stream / device synchronize additionally wait for the D2H copies submitted before the event was recorded (or before the call). Using such a stream asserts that its D2H sources aren't overwritten until the copy is synced (later compute-stream work is never ordered after a side stream anyway), and that their completion is only awaited through the patched torch.cuda / torch.Event methods. D2H on any other stream keeps the original, host-blocking copy, since e.g. atorch.accelerator.current_stream().synchronize()can't be patched. A registered stream is only ever ordered after a producer stream bywait_stream()and then carries D2H copies, so the events that mark its position (the copies' readiness, and events recorded on it such as the caller's copy event) are recorded on that producer stream instead. Under CC an event recorded on an otherwise idle stream behind a cross-stream wait was observed to fire only once the compute stream's next graph had finished, which delayed every sampled token by a full step; an event recorded on the producer stream fires on time.
Copies made while torch.compile is tracing or a CUDA graph is being captured
are left alone, as are H2D copies into an existing tensor that would need a
staging buffer larger than _MAX_STAGED_BYTES (e.g. KV cache swaps).
Functions:
-
install–Install the patches. Call once per worker process, only under CC.
-
new_external_d2h_stream–Create a stream whose non_blocking D2H copies go through the worker
-
register_inplace_h2d_target–Let non_blocking H2D copies into
tensor(or a view of it) land in
_capture_originals()
¶
Save the torch.cuda methods the patches wrap, once.
Source code in vllm/v1/conf_compute_utils.py
_d2h(src, out=None)
¶
Returns None if the copy should fall back to the original method.
Source code in vllm/v1/conf_compute_utils.py
_new_external_stream(device)
¶
A stream created outside torch's per-device pool, so torch.cuda.Stream()
can never hand out its handle again (the pool is 32 streams, round-robin).
Source code in vllm/v1/conf_compute_utils.py
_patch_event_methods(cls)
¶
Wrap cls's record/synchronize/query so a recorded event also covers
the D2H copies submitted before it was recorded.
Source code in vllm/v1/conf_compute_utils.py
_record_target(stream)
¶
Where an event recorded on stream (None: the current stream) goes:
the producer stream of a registered D2H stream, otherwise stream.
Source code in vllm/v1/conf_compute_utils.py
install()
¶
Install the patches. Call once per worker process, only under CC.
torch.Event is an immutable C type, so it is replaced with a subclass:
events created through torch.Event(...) afterwards are covered, but
events created in C (e.g. torch.Stream.record_event()) are not, and
isinstance(e, torch.Event) becomes False for those and for
torch.cuda.Event instances (which subclass the original). Only D2H
copies on registered streams depend on these patches.
Source code in vllm/v1/conf_compute_utils.py
new_external_d2h_stream(device=None)
¶
Create a stream whose non_blocking D2H copies go through the worker
thread once install() has run (see the module docstring). Callers use
this through async_utils.new_d2h_stream().
The stream is created outside torch's stream pool: the patches key on the
raw handle, and a pool stream handed out later (e.g. by graph_capture()
or aux_stream()) must never share it, or its events would be redirected
to the producer stream. The stream lives for the life of the process.
Such a stream must only carry copies: readiness is tracked on the stream
it last waited on via wait_stream(), so a kernel launched on the copy
stream itself is not ordered before the copies of its output. Compute such
values on the producer stream first.
Source code in vllm/v1/conf_compute_utils.py
register_inplace_h2d_target(tensor)
¶
Let non_blocking H2D copies into tensor (or a view of it) land in
place on the staging stream once install() has run: no staging tensor
and no D2D. The caller guarantees that no GPU reader of tensor is still
in flight whenever it is rewritten (e.g. a round-robin pool slot whose
readers retired). The staging stream is ordered once after the current
stream, so work that initialized tensor (e.g. the zero fill of a fresh
allocation) completes before the first in-place upload. The registration
lapses with tensor, so a later tensor reusing its memory is not matched.