@@ -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