Skip to content

Commit decd9ca

Browse files
committed
Enabled alias IO for python runtime and fixed comments
1 parent d1acd0b commit decd9ca

19 files changed

Lines changed: 499 additions & 125 deletions

core/runtime/TRTEngine.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -284,11 +284,6 @@ TRTEngine::TRTEngine(
284284
}
285285

286286
has_dynamic_inputs = engine_has_dynamic_inputs(cuda_engine.get(), in_binding_names);
287-
// Cache input binding metadata used by the runtime hot path, then reconstruct
288-
// optimization-profile info from the TRT API so multi-profile selection works
289-
// for any loaded engine.
290-
this->setup_input_binding_infos();
291-
this->setup_optimization_profiles();
292287

293288
// Store the build-time aliased_io map as the starting point. Then reconcile
294289
// against ICudaEngine::getAliasedInputTensor — that API is the source of
@@ -308,6 +303,17 @@ TRTEngine::TRTEngine(
308303
if (it == this->aliased_io.end()) {
309304
this->aliased_io[out_name] = AliasedIOSpec{std::string(aliased_in), AliasKind::kKVCacheUpdate};
310305
LOG_DEBUG("aliased_io reconciliation: discovered " << out_name << " -> " << aliased_in << " (kv_cache_update)");
306+
} else if (it->second.kind == AliasKind::kUser) {
307+
// A kUser alias is declared by Torch-TensorRT, not TRT. If the engine
308+
// also reports an alias for this output the two views should agree on the
309+
// source input; keep the kUser kind either way so the provenance recorded
310+
// at build time is not silently rewritten to kv_cache_update.
311+
if (it->second.input_binding_name != std::string(aliased_in)) {
312+
LOG_WARNING(
313+
"aliased_io: user-declared alias for output " << out_name << " (input: " << it->second.input_binding_name
314+
<< ") disagrees with engine-reported input " << aliased_in
315+
<< "; keeping the user-declared value.");
316+
}
311317
} else if (it->second.input_binding_name != std::string(aliased_in)) {
312318
LOG_WARNING(
313319
"aliased_io: build-time map disagrees with engine for output "
@@ -324,6 +330,19 @@ TRTEngine::TRTEngine(
324330
<< " is incompatible with dynamic output allocator. Aliasing requires fixed output shape.");
325331
}
326332

333+
// Precompute the set of input binding names that are the alias source of some
334+
// output so the per-call input-setup loop can test membership in O(1).
335+
this->aliased_input_binding_names.clear();
336+
for (const auto& kv : this->aliased_io) {
337+
this->aliased_input_binding_names.insert(kv.second.input_binding_name);
338+
}
339+
340+
// Cache input binding metadata used by the runtime hot path (depends on
341+
// aliased_input_binding_names above), then reconstruct optimization-profile
342+
// info from the TRT API so multi-profile selection works for any loaded engine.
343+
this->setup_input_binding_infos();
344+
this->setup_optimization_profiles();
345+
327346
#ifndef NDEBUG
328347
this->enable_profiling();
329348
#endif
@@ -611,7 +630,8 @@ void TRTEngine::setup_input_binding_infos() {
611630
input_binding_infos.push_back(
612631
{name,
613632
util::TRTDataTypeToScalarType(cuda_engine->getTensorDataType(name.c_str())),
614-
cuda_engine->isShapeInferenceIO(name.c_str())});
633+
cuda_engine->isShapeInferenceIO(name.c_str()),
634+
aliased_input_binding_names.find(name) != aliased_input_binding_names.end()});
615635
}
616636
}
617637

core/runtime/TRTEngine.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <memory>
77
#include <mutex>
88
#include <unordered_map>
9+
#include <unordered_set>
910
#include <utility>
1011

1112
#include "ATen/core/function_schema.h"
@@ -110,7 +111,9 @@ struct TorchTRTRuntimeStates {
110111
if (new_cudagraphs && (!old_cudagraphs || shape_changed || context_changed)) {
111112
need_cudagraphs_record = true;
112113
}
113-
// Pre-allocated output can be used when previous and current state are true without shape change
114+
// Pre-allocated output can be used when previous and current state are true without shape change.
115+
// Note: engines with aliased I/O disable pre-allocation entirely; that gating lives in
116+
// execute_engine (which has access to the engine's aliased_io map) rather than here.
114117
if (old_pre_allocated_outputs && new_pre_allocated_output && !shape_changed) {
115118
can_use_pre_allocated_outputs = true;
116119
}
@@ -181,6 +184,12 @@ struct TRTEngine : torch::CustomClassHolder {
181184
// the same device pointer as the source input.
182185
std::unordered_map<std::string, AliasedIOSpec> aliased_io = {};
183186

187+
// The set of input binding names that are the alias source of some output.
188+
// Derived once from aliased_io at construction so the per-call input-setup
189+
// loop can test membership in O(1) instead of scanning aliased_io on every
190+
// input, every execution.
191+
std::unordered_set<std::string> aliased_input_binding_names = {};
192+
184193
bool hardware_compatible = false; // Whether the engine was compiled in hardware compatible mode
185194
std::string serialized_metadata; // This is a base64 encoded pkl object used to store metadata such as settings used
186195
// in compilation
@@ -265,6 +274,10 @@ struct TRTEngine : torch::CustomClassHolder {
265274
std::string name;
266275
at::ScalarType expected_type;
267276
bool is_shape_tensor;
277+
// True when this input is the alias source of some output binding. Precomputed
278+
// here so the per-call input-setup loop avoids an aliased_input_binding_names
279+
// lookup on every input, every execution.
280+
bool is_aliased_input;
268281
};
269282
std::vector<InputBindingInfo> input_binding_infos = {};
270283
std::vector<at::Tensor> active_input_tensors = {};

core/runtime/execute_engine.cpp

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,10 @@ void setup_input_tensors(
145145
// aliased input we deliberately bind to the user's tensor instead, so
146146
// the engine writes through to the user's storage. The user is already
147147
// required to pass stable input addresses under cudagraphs, so the
148-
// aliasing contract is compatible.
149-
bool is_aliased_input = false;
150-
for (const auto& kv : compiled_engine->aliased_io) {
151-
if (kv.second.input_binding_name == name) {
152-
is_aliased_input = true;
153-
break;
154-
}
155-
}
148+
// aliasing contract is compatible. Membership is precomputed at engine
149+
// construction (InputBindingInfo::is_aliased_input) to avoid rescanning
150+
// aliased_io on every input, every execution.
151+
const bool is_aliased_input = binding.is_aliased_input;
156152

157153
if (need_cudagraphs_record && !is_aliased_input) {
158154
// Create a persistent CUDA graph input staging buffer with a stable replay address.
@@ -346,6 +342,23 @@ std::vector<at::Tensor> execute_engine(std::vector<at::Tensor> inputs, c10::intr
346342
bool can_use_pre_allocated_outputs = std::get<1>(result);
347343
bool need_cudagraphs_reset = std::get<2>(result);
348344

345+
// Pre-allocated outputs are disabled entirely for engines with aliased I/O.
346+
// An aliased output reuses the caller's input storage, so there is no
347+
// allocation to amortize by caching, and caching would pin a caller-owned
348+
// tensor across calls. Only the non-aliased outputs could benefit, and that
349+
// win is too small to justify the extra staleness/lifetime surface. See the
350+
// cache-refresh guard below, which is the counterpart to this one.
351+
if (!compiled_engine->aliased_io.empty()) {
352+
if (compiled_engine->use_pre_allocated_outputs) {
353+
LOG_WARNING(
354+
"pre_allocated_outputs is enabled but this engine has aliased I/O; "
355+
"pre-allocation is disabled for aliased engines (aliased outputs reuse "
356+
"the caller's input storage, so there is nothing to pre-allocate). "
357+
"Outputs are allocated fresh each call.");
358+
}
359+
can_use_pre_allocated_outputs = false;
360+
}
361+
349362
if (need_cudagraphs_reset) {
350363
compiled_engine->cudagraph.reset();
351364
}
@@ -367,8 +380,7 @@ std::vector<at::Tensor> execute_engine(std::vector<at::Tensor> inputs, c10::intr
367380
std::make_unique<torch::autograd::profiler::RecordProfile>(compiled_engine->input_profile_path);
368381
}
369382

370-
setup_input_tensors(
371-
inputs, compiled_engine, effective_cudagraphs, need_cudagraphs_record, bound_inputs_by_name);
383+
setup_input_tensors(inputs, compiled_engine, effective_cudagraphs, need_cudagraphs_record, bound_inputs_by_name);
372384
// Check if input shapes can be inferred.
373385
int32_t const io_size{compiled_engine->cuda_engine->getNbIOTensors()};
374386
std::vector<char const*> names(io_size);
@@ -387,6 +399,9 @@ std::vector<at::Tensor> execute_engine(std::vector<at::Tensor> inputs, c10::intr
387399
std::make_unique<torch::autograd::profiler::RecordProfile>(compiled_engine->output_profile_path);
388400
}
389401
if (can_use_pre_allocated_outputs) {
402+
// Never reached for engines with aliased I/O: can_use_pre_allocated_outputs
403+
// is forced false above when aliased_io is non-empty, so cached outputs
404+
// never hold an aliased (caller-owned) slot.
390405
outputs = compiled_engine->pre_allocated_outputs;
391406
} else {
392407
outputs = create_output_tensors(compiled_engine, bound_inputs_by_name);
@@ -493,16 +508,14 @@ std::vector<at::Tensor> execute_engine(std::vector<at::Tensor> inputs, c10::intr
493508
compiled_engine->clear_active_input_tensors();
494509

495510
// When the pre-allocated output mode is turned on, for intermediate modules, we only create the output in the first
496-
// execution or when shape is changed. If the engine has aliased outputs we
497-
// disable pre-allocation entirely: aliased outputs share storage with
498-
// user-supplied inputs that may change between calls, so caching the
499-
// tensor reference would lead to writes against stale storage.
500-
if (compiled_engine->use_pre_allocated_outputs && !compiled_engine->aliased_io.empty()) {
501-
LOG_DEBUG(
502-
"Skipping pre_allocated_outputs cache because engine has aliased I/O; "
503-
"aliased outputs reuse the user's input storage on every call.");
504-
} else if (
505-
compiled_engine->use_pre_allocated_outputs &&
511+
// execution or when shape is changed.
512+
//
513+
// Disabled entirely for engines with aliased I/O: an aliased output reuses
514+
// the caller's input storage (no allocation to amortize), and caching would
515+
// pin a caller-owned tensor across calls. This mirrors the
516+
// can_use_pre_allocated_outputs gate above so the cache is never populated
517+
// for aliased engines in the first place.
518+
if (compiled_engine->use_pre_allocated_outputs && compiled_engine->aliased_io.empty() &&
506519
(compiled_engine->pre_allocated_outputs.size() == 0 || compiled_engine->output_tensors_are_unowned ||
507520
shape_changed)) {
508521
compiled_engine->pre_allocated_outputs = create_output_tensors(compiled_engine, bound_inputs_by_name);

0 commit comments

Comments
 (0)