Skip to content

Commit 4875b43

Browse files
author
pytorchbot
committed
2026-06-09 nightly release (7124e43)
1 parent 3177d26 commit 4875b43

247 files changed

Lines changed: 1331 additions & 295 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cpp/include/torch_tensorrt/executorch/TensorRTBackend.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ struct EngineHandle {
5050
TRTUniquePtr<nvinfer1::IRuntime> runtime;
5151
TRTUniquePtr<nvinfer1::ICudaEngine> engine;
5252
TRTUniquePtr<nvinfer1::IExecutionContext> exec_ctx;
53-
cudaStream_t stream = nullptr;
5453
std::vector<std::string> input_binding_names;
5554
std::vector<std::string> output_binding_names;
5655
std::vector<InputProfileBounds> input_profile_bounds;
@@ -63,6 +62,13 @@ struct EngineHandle {
6362
int device_id = 0;
6463
bool unified_memory = false;
6564
std::mutex mu;
65+
// Makes the skip-sync fast path safe to reuse: TensorRT forbids reconfiguring or
66+
// destroying an execution context while one of its enqueues is in flight, so when
67+
// execute() returns without an end sync it records this event; the next execute()
68+
// and the destructor wait on it before touching exec_ctx. One event/flag pair
69+
// suffices because a handle runs on a single thread at a time.
70+
cudaEvent_t inflight_event = nullptr;
71+
bool inflight_pending = false;
6672

6773
~EngineHandle();
6874
};
@@ -84,5 +90,34 @@ class TensorRTBackend final : public ::executorch::runtime::BackendInterface {
8490
void destroy(::executorch::runtime::DelegateHandle* handle) const override;
8591
};
8692

93+
// Selects, for the calling thread, the CUDA stream the delegate runs TensorRT on;
94+
// scope it around execution.
95+
//
96+
// Confines inference to a CUDA green context's SM partition when the caller
97+
// passes a cuGreenCtxStreamCreate stream: confinement rides the stream (the green
98+
// context need not be current), and cudaStreamPerThread — the no-guard default —
99+
// is rejected while a green context is current. While active, device-resident
100+
// outputs are left enqueued on the stream (no end sync) to compose with later GPU
101+
// work.
102+
//
103+
// Contract: the stream is on the engine's device and outlives the guard; a handle
104+
// is executed by one thread at a time. On the no-end-sync path (guard active, all
105+
// I/O device-resident) execute() returns with the TensorRT enqueue still in flight
106+
// on the stream; the delegate itself orders the next execute() on, and the
107+
// destruction of, that handle after the work completes (via an internal completion
108+
// event), so the caller need only synchronize the stream before reading
109+
// device-resident outputs.
110+
class CudaStreamGuard {
111+
public:
112+
explicit CudaStreamGuard(cudaStream_t stream);
113+
~CudaStreamGuard();
114+
CudaStreamGuard(const CudaStreamGuard&) = delete;
115+
CudaStreamGuard& operator=(const CudaStreamGuard&) = delete;
116+
117+
private:
118+
cudaStream_t prev_stream_;
119+
bool prev_set_;
120+
};
121+
87122
} // namespace executorch_backend
88123
} // namespace torch_tensorrt

cpp/src/torch_tensorrt/executorch/TensorRTBackend.cpp

Lines changed: 129 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ using ::executorch::runtime::Span;
4848
} \
4949
} while (false)
5050

51+
namespace {
52+
thread_local cudaStream_t g_user_stream = nullptr;
53+
thread_local bool g_user_stream_set = false;
54+
} // namespace
55+
56+
CudaStreamGuard::CudaStreamGuard(cudaStream_t stream) : prev_stream_(g_user_stream), prev_set_(g_user_stream_set) {
57+
g_user_stream = stream;
58+
g_user_stream_set = true;
59+
}
60+
61+
CudaStreamGuard::~CudaStreamGuard() {
62+
g_user_stream = prev_stream_;
63+
g_user_stream_set = prev_set_;
64+
}
65+
5166
void TRTLogger::log(Severity severity, const char* msg) noexcept {
5267
if (severity <= Severity::kERROR) {
5368
ET_LOG(Error, "TensorRT: %s", msg);
@@ -58,8 +73,23 @@ void TRTLogger::log(Severity severity, const char* msg) noexcept {
5873

5974
EngineHandle::~EngineHandle() {
6075
cudaSetDevice(device_id);
61-
if (stream != nullptr) {
62-
cudaStreamSynchronize(stream);
76+
// A fast-path execute() may have returned with its enqueue still in flight on the
77+
// caller's stream, still using exec_ctx and the cached staging buffers. Wait on
78+
// the recorded completion event before destroying the context or freeing the
79+
// buffers. We wait on the event, not the stream, so this stays valid even if the
80+
// caller already destroyed the stream. Non-skip executes synchronized inline, so
81+
// inflight_pending is false there. Fall back to a device sync if no event exists.
82+
if (inflight_event != nullptr) {
83+
if (inflight_pending) {
84+
cudaError_t err = cudaEventSynchronize(inflight_event);
85+
if (err != cudaSuccess) {
86+
ET_LOG(Error, "EngineHandle::~EngineHandle: cudaEventSynchronize failed: %s", cudaGetErrorString(err));
87+
cudaGetLastError(); // clear sticky error; tear down regardless
88+
}
89+
inflight_pending = false;
90+
}
91+
} else {
92+
cudaDeviceSynchronize();
6393
}
6494
for (void* p : cached_input_ptrs) {
6595
if (p != nullptr) {
@@ -74,9 +104,9 @@ EngineHandle::~EngineHandle() {
74104
exec_ctx.reset();
75105
engine.reset();
76106
runtime.reset();
77-
if (stream != nullptr) {
78-
cudaStreamDestroy(stream);
79-
stream = nullptr;
107+
if (inflight_event != nullptr) {
108+
cudaEventDestroy(inflight_event);
109+
inflight_event = nullptr;
80110
}
81111
}
82112

@@ -226,9 +256,12 @@ Result<DelegateHandle*> TensorRTBackend::init(
226256
return Error::InvalidProgram;
227257
}
228258

229-
cuda_err = cudaStreamCreate(&handle->stream);
259+
// Created while device_id is current so the event belongs to the engine's device.
260+
// It orders a later execute()/teardown after a skip-sync enqueue (see execute()
261+
// and ~EngineHandle). Blocking-sync so the host yields instead of busy-spinning.
262+
cuda_err = cudaEventCreateWithFlags(&handle->inflight_event, cudaEventDisableTiming | cudaEventBlockingSync);
230263
if (cuda_err != cudaSuccess) {
231-
ET_LOG(Error, "TensorRTBackend::init: cudaStreamCreate failed: %s", cudaGetErrorString(cuda_err));
264+
ET_LOG(Error, "TensorRTBackend::init: cudaEventCreateWithFlags failed: %s", cudaGetErrorString(cuda_err));
232265
return Error::InvalidProgram;
233266
}
234267

@@ -298,22 +331,57 @@ Error TensorRTBackend::execute(BackendExecutionContext& context, DelegateHandle*
298331
return Error::InvalidArgument;
299332
}
300333

301-
cudaError_t cuda_err = cudaSetDevice(engine->device_id);
334+
int entry_device = -1;
335+
cudaError_t cuda_err = cudaGetDevice(&entry_device);
302336
if (cuda_err != cudaSuccess) {
303-
ET_LOG(
304-
Error,
305-
"TensorRTBackend::execute: cudaSetDevice(%d) failed: %s",
306-
engine->device_id,
307-
cudaGetErrorString(cuda_err));
337+
ET_LOG(Error, "TensorRTBackend::execute: cudaGetDevice failed: %s", cudaGetErrorString(cuda_err));
308338
return Error::InvalidProgram;
309339
}
340+
// Put the engine on its own device for multi-GPU correctness, restoring the
341+
// caller's device on exit; green-context confinement rides the selected stream,
342+
// independent of the current device/context.
343+
const bool switch_device = (entry_device != engine->device_id);
344+
if (switch_device) {
345+
cuda_err = cudaSetDevice(engine->device_id);
346+
if (cuda_err != cudaSuccess) {
347+
ET_LOG(
348+
Error,
349+
"TensorRTBackend::execute: cudaSetDevice(%d) failed: %s",
350+
engine->device_id,
351+
cudaGetErrorString(cuda_err));
352+
return Error::InvalidProgram;
353+
}
354+
}
355+
struct DeviceRestore {
356+
int device;
357+
bool active;
358+
~DeviceRestore() {
359+
if (active) {
360+
cudaSetDevice(device);
361+
}
362+
}
363+
} device_restore{entry_device, switch_device};
310364

311365
std::unique_lock<std::mutex> lock(engine->mu);
312366

313367
nvinfer1::IExecutionContext* ctx = engine->exec_ctx.get();
314-
cudaStream_t stream = engine->stream;
315368
TORCHTRT_ET_CHECK_NOT_NULL(ctx, Error::InvalidState, "TensorRTBackend::execute: backend is not initialized");
316-
TORCHTRT_ET_CHECK_NOT_NULL(stream, Error::InvalidState, "TensorRTBackend::execute: backend is not initialized");
369+
370+
// A prior fast-path execute() may have returned with its enqueue still in flight
371+
// on the shared exec_ctx. Wait for it before reconfiguring the context below:
372+
// TensorRT forbids mutating a context while one of its enqueues is in flight, and
373+
// setInputShape/setTensorAddress run on the host, so this must be a host-side wait.
374+
if (engine->inflight_pending) {
375+
cuda_err = cudaEventSynchronize(engine->inflight_event);
376+
engine->inflight_pending = false;
377+
if (cuda_err != cudaSuccess) {
378+
ET_LOG(Error, "TensorRTBackend::execute: cudaEventSynchronize failed: %s", cudaGetErrorString(cuda_err));
379+
return Error::InvalidProgram;
380+
}
381+
}
382+
cudaStream_t stream = g_user_stream_set ? g_user_stream : cudaStreamPerThread;
383+
bool output_staged_to_host = false;
384+
bool input_staged_from_host = false;
317385

318386
if (engine->cached_input_ptrs.empty()) {
319387
engine->cached_input_ptrs.resize(num_inputs, nullptr);
@@ -392,6 +460,7 @@ Error TensorRTBackend::execute(BackendExecutionContext& context, DelegateHandle*
392460
engine->cached_input_sizes[i] = needed;
393461
}
394462
bind_ptr = engine->cached_input_ptrs[i];
463+
input_staged_from_host = true;
395464
cuda_err = cudaMemcpyAsync(bind_ptr, et_in.const_data_ptr(), needed, cudaMemcpyHostToDevice, stream);
396465
if (cuda_err != cudaSuccess) {
397466
ET_LOG(
@@ -486,6 +555,7 @@ Error TensorRTBackend::execute(BackendExecutionContext& context, DelegateHandle*
486555
engine->cached_output_sizes[o] = needed;
487556
}
488557
bind_ptr = engine->cached_output_ptrs[o];
558+
output_staged_to_host = true;
489559
outputs_needing_copy.push_back({o, bind_ptr});
490560
}
491561

@@ -499,28 +569,56 @@ Error TensorRTBackend::execute(BackendExecutionContext& context, DelegateHandle*
499569
// 4. Enqueue inference on the current CUDA stream
500570
// ------------------------------------------------------------------
501571
if (!ctx->enqueueV3(stream)) {
502-
ET_LOG(Error, "TensorRTBackend::execute: enqueueV3 failed");
572+
ET_LOG(
573+
Error,
574+
"TensorRTBackend::execute: enqueueV3 failed. If a CUDA green context is "
575+
"current, scope a CudaStreamGuard with a green-context stream: "
576+
"cudaStreamPerThread is invalid while a green context is current.");
503577
return Error::InvalidState;
504578
}
505579

506-
for (auto& output : outputs_needing_copy) {
507-
exec_aten::Tensor et_out = args[num_inputs + output.first]->toTensor();
508-
cuda_err =
509-
cudaMemcpyAsync(et_out.mutable_data_ptr(), output.second, et_out.nbytes(), cudaMemcpyDeviceToHost, stream);
580+
// The engine work is now in flight on `stream`. Decide whether to wait for it:
581+
// must_sync = an output is staged to host (the caller reads the D2H result on
582+
// return), an input was staged from host (its async H2D read the caller's host
583+
// buffer, which the caller may reuse once we return), or no caller stream is
584+
// active (preserve the historical "results ready on return" behavior).
585+
// Otherwise (caller stream + all I/O device-resident) leave the work enqueued so
586+
// it composes with the caller's later GPU work, and record inflight_event so the
587+
// next execute() and the destructor wait before reusing/freeing exec_ctx. The D2H
588+
// copies live in the must_sync branch: an output staged to host always sets
589+
// output_staged_to_host, so outputs_needing_copy is empty on the skip path.
590+
const bool must_sync = output_staged_to_host || input_staged_from_host || !g_user_stream_set;
591+
if (must_sync) {
592+
for (auto& output : outputs_needing_copy) {
593+
exec_aten::Tensor et_out = args[num_inputs + output.first]->toTensor();
594+
cuda_err =
595+
cudaMemcpyAsync(et_out.mutable_data_ptr(), output.second, et_out.nbytes(), cudaMemcpyDeviceToHost, stream);
596+
if (cuda_err != cudaSuccess) {
597+
ET_LOG(
598+
Error,
599+
"TensorRTBackend::execute: D2H copy failed for output %zu: %s",
600+
output.first,
601+
cudaGetErrorString(cuda_err));
602+
return Error::InvalidProgram;
603+
}
604+
}
605+
cuda_err = cudaStreamSynchronize(stream);
606+
engine->inflight_pending = false;
510607
if (cuda_err != cudaSuccess) {
511-
ET_LOG(
512-
Error,
513-
"TensorRTBackend::execute: D2H copy failed for output %zu: %s",
514-
output.first,
515-
cudaGetErrorString(cuda_err));
608+
ET_LOG(Error, "TensorRTBackend::execute: cudaStreamSynchronize failed: %s", cudaGetErrorString(cuda_err));
516609
return Error::InvalidProgram;
517610
}
518-
}
519-
520-
cuda_err = cudaStreamSynchronize(stream);
521-
if (cuda_err != cudaSuccess) {
522-
ET_LOG(Error, "TensorRTBackend::execute: cudaStreamSynchronize failed: %s", cudaGetErrorString(cuda_err));
523-
return Error::InvalidProgram;
611+
} else {
612+
cuda_err = cudaEventRecord(engine->inflight_event, stream);
613+
if (cuda_err != cudaSuccess) {
614+
// Could not arm the completion marker; drain now so a later execute() or the
615+
// destructor never reconfigures or frees exec_ctx while this enqueue runs.
616+
ET_LOG(Error, "TensorRTBackend::execute: cudaEventRecord failed: %s", cudaGetErrorString(cuda_err));
617+
(void)cudaStreamSynchronize(stream);
618+
engine->inflight_pending = false;
619+
return Error::InvalidProgram;
620+
}
621+
engine->inflight_pending = true;
524622
}
525623
return Error::Ok;
526624
}

docs/_cpp_api/classtorch__tensorrt_1_1DataType.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<link rel="preload" as="script" href="../_static/scripts/pydata-sphinx-theme.js?digest=dfe6caa3a7d634c4db9b" />
4444
<script src="../_static/vendor/fontawesome/6.5.2/js/all.min.js?digest=dfe6caa3a7d634c4db9b"></script>
4545

46-
<script src="../_static/documentation_options.js?v=5534de61"></script>
46+
<script src="../_static/documentation_options.js?v=e18999b2"></script>
4747
<script src="../_static/doctools.js?v=888ff710"></script>
4848
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
4949
<script src="../_static/collapsible-lists/js/CollapsibleLists.compressed.js?v=73120307"></script>
@@ -462,6 +462,7 @@
462462
<li class="toctree-l3"><a class="reference internal" href="structtorch__tensorrt_1_1torchscript_1_1CompileSpec.html">Struct CompileSpec</a></li>
463463
<li class="toctree-l3 current active"><a class="current reference internal" href="#">Class DataType</a></li>
464464
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1Device_1_1DeviceType.html">Class Device::DeviceType</a></li>
465+
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1CudaStreamGuard.html">Class CudaStreamGuard</a></li>
465466
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1TensorRTBackend.html">Class TensorRTBackend</a></li>
466467
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1TRTLogger.html">Class TRTLogger</a></li>
467468
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1TensorFormat.html">Class TensorFormat</a></li>

docs/_cpp_api/classtorch__tensorrt_1_1Device_1_1DeviceType.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<link rel="preload" as="script" href="../_static/scripts/pydata-sphinx-theme.js?digest=dfe6caa3a7d634c4db9b" />
4444
<script src="../_static/vendor/fontawesome/6.5.2/js/all.min.js?digest=dfe6caa3a7d634c4db9b"></script>
4545

46-
<script src="../_static/documentation_options.js?v=5534de61"></script>
46+
<script src="../_static/documentation_options.js?v=e18999b2"></script>
4747
<script src="../_static/doctools.js?v=888ff710"></script>
4848
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
4949
<script src="../_static/collapsible-lists/js/CollapsibleLists.compressed.js?v=73120307"></script>
@@ -58,7 +58,7 @@
5858
</script>
5959
<link rel="index" title="Index" href="../genindex.html" />
6060
<link rel="search" title="Search" href="../search.html" />
61-
<link rel="next" title="Class TensorRTBackend" href="classtorch__tensorrt_1_1executorch__backend_1_1TensorRTBackend.html" />
61+
<link rel="next" title="Class CudaStreamGuard" href="classtorch__tensorrt_1_1executorch__backend_1_1CudaStreamGuard.html" />
6262
<link rel="prev" title="Class DataType" href="classtorch__tensorrt_1_1DataType.html" />
6363
<meta name="viewport" content="width=device-width, initial-scale=1"/>
6464
<meta name="docsearch:language" content="en"/>
@@ -462,6 +462,7 @@
462462
<li class="toctree-l3"><a class="reference internal" href="structtorch__tensorrt_1_1torchscript_1_1CompileSpec.html">Struct CompileSpec</a></li>
463463
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1DataType.html">Class DataType</a></li>
464464
<li class="toctree-l3 current active"><a class="current reference internal" href="#">Class Device::DeviceType</a></li>
465+
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1CudaStreamGuard.html">Class CudaStreamGuard</a></li>
465466
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1TensorRTBackend.html">Class TensorRTBackend</a></li>
466467
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1TRTLogger.html">Class TRTLogger</a></li>
467468
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1TensorFormat.html">Class TensorFormat</a></li>
@@ -702,11 +703,11 @@ <h2>Class Documentation<a class="headerlink" href="#class-documentation" title="
702703
</div>
703704
</a>
704705
<a class="right-next"
705-
href="classtorch__tensorrt_1_1executorch__backend_1_1TensorRTBackend.html"
706+
href="classtorch__tensorrt_1_1executorch__backend_1_1CudaStreamGuard.html"
706707
title="next page">
707708
<div class="prev-next-info">
708709
<p class="prev-next-subtitle">next</p>
709-
<p class="prev-next-title">Class TensorRTBackend</p>
710+
<p class="prev-next-title">Class CudaStreamGuard</p>
710711
</div>
711712
<i class="fa-solid fa-angle-right"></i>
712713
</a>

docs/_cpp_api/classtorch__tensorrt_1_1TensorFormat.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<link rel="preload" as="script" href="../_static/scripts/pydata-sphinx-theme.js?digest=dfe6caa3a7d634c4db9b" />
4444
<script src="../_static/vendor/fontawesome/6.5.2/js/all.min.js?digest=dfe6caa3a7d634c4db9b"></script>
4545

46-
<script src="../_static/documentation_options.js?v=5534de61"></script>
46+
<script src="../_static/documentation_options.js?v=e18999b2"></script>
4747
<script src="../_static/doctools.js?v=888ff710"></script>
4848
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
4949
<script src="../_static/collapsible-lists/js/CollapsibleLists.compressed.js?v=73120307"></script>
@@ -462,6 +462,7 @@
462462
<li class="toctree-l3"><a class="reference internal" href="structtorch__tensorrt_1_1torchscript_1_1CompileSpec.html">Struct CompileSpec</a></li>
463463
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1DataType.html">Class DataType</a></li>
464464
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1Device_1_1DeviceType.html">Class Device::DeviceType</a></li>
465+
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1CudaStreamGuard.html">Class CudaStreamGuard</a></li>
465466
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1TensorRTBackend.html">Class TensorRTBackend</a></li>
466467
<li class="toctree-l3"><a class="reference internal" href="classtorch__tensorrt_1_1executorch__backend_1_1TRTLogger.html">Class TRTLogger</a></li>
467468
<li class="toctree-l3 current active"><a class="current reference internal" href="#">Class TensorFormat</a></li>

0 commit comments

Comments
 (0)