Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
6ba3bdb
fix(executorch): show the TensorRT build number of the runtime in loa…
shoumikhin Oct 30, 2026
4dfe307
fix(dynamo): save programs that take no inputs
shoumikhin Oct 30, 2026
3716cd5
fix(dynamo): keep the range of an int input when no input is a tensor
shoumikhin Oct 30, 2026
63b50ec
test(dynamo): make the no-input lift test check values, not only the …
shoumikhin Oct 30, 2026
376c95f
test(executorch): save a compiled program that takes no inputs
shoumikhin Oct 30, 2026
7097bbd
docs(dynamo): note that the fake mode lookup in lift also covers int-…
shoumikhin Oct 30, 2026
4ac30f1
fix(dynamo): keep shape ops on uint8 tensors in PyTorch
shoumikhin Oct 30, 2026
2b97622
fix(dynamo): run views left to PyTorch as aten.reshape, not aten._res…
shoumikhin Oct 30, 2026
1edc8b6
fix(lowering): only replace an attention bias with is_causal when it …
shoumikhin Oct 30, 2026
98a65c4
fix(lowering): prove an attention bias causal under torch.compile too
shoumikhin Oct 30, 2026
ef269df
docs(lowering): describe the proven causal attention bias in the sett…
shoumikhin Oct 30, 2026
3d45429
fix(dynamo): keep the copy when a view left to PyTorch becomes aten.r…
shoumikhin Oct 30, 2026
c2adbdf
fix(dynamo): run partial slice_scatter writes on uint8 tensors in PyT…
shoumikhin Oct 30, 2026
2d5365c
fix(lowering): let attn_bias_is_causal=True trust a bias the pass can…
shoumikhin Oct 30, 2026
092859c
fix(dynamo): keep a torch.cond branch that makes a uint8 tensor in Py…
shoumikhin Oct 30, 2026
c53cd5c
test(dynamo): cover split in the uint8 shape op test and check one en…
shoumikhin Oct 30, 2026
2ceabce
docs(examples): drop a stale attn_bias_is_causal note in the multi-pr…
shoumikhin Oct 30, 2026
55e8b5c
fix(dynamo): make the restored reshape copy contiguous, as aten._resh…
shoumikhin Oct 30, 2026
9cf17bb
fix(dynamo): exempt a full uint8 slice_scatter only when its source i…
shoumikhin Oct 30, 2026
f94a2b7
test(lowering): check that attn_bias_is_causal=True keeps a bias that…
shoumikhin Oct 30, 2026
ceed75d
fix(dynamo): keep a torch.cond with a uint8 operand in PyTorch
shoumikhin Oct 30, 2026
8cecaf5
refactor(dynamo): import the partitioner's uint8 rule lazily in the c…
shoumikhin Oct 30, 2026
839dc42
docs(lowering): say "prove" for what attn_bias_is_causal=True trusts
shoumikhin Oct 30, 2026
7172d92
fix(executorch): refuse at save an engine that uses the libtorch Scat…
shoumikhin Oct 31, 2026
15c374d
fix(lowering): clear an unused log_sumexp request when keeping an att…
shoumikhin Oct 31, 2026
4f8765b
fix(lowering): keep an attention bias whose causal check fails
shoumikhin Oct 31, 2026
1ef87d0
perf(lowering): check a causal attention bias without indexing it
shoumikhin Oct 31, 2026
8603f09
perf(dynamo): restore a view left to PyTorch with one copy
shoumikhin Oct 31, 2026
4d8f22e
fix(dynamo): leave a where between uint8 tensors to TensorRT
shoumikhin Oct 31, 2026
e2756b7
test(lowering): cover the False opt-out and the zero pattern of a cau…
shoumikhin Oct 31, 2026
ef4c474
fix(dynamo): put node arguments in the engine cache key
shoumikhin Oct 31, 2026
0bae89c
docs(lowering): give the right reason to keep a bias whose causal che…
shoumikhin Oct 31, 2026
c6a913a
test(lowering): check that a read log_sumexp or a dropout keeps the flag
shoumikhin Oct 31, 2026
77c538f
fix(dynamo): leave a where of uint8 and bool values to TensorRT too
shoumikhin Oct 31, 2026
f20fe4a
refactor(dynamo): shorten the engine cache key comment and test
shoumikhin Oct 31, 2026
34382de
fix(dynamo): put torch.cond branch bodies in the engine cache key
shoumikhin Oct 31, 2026
08ba203
fix(dynamo): put the shape and dtype of a constant in the engine cach…
shoumikhin Oct 31, 2026
3c7c625
fix(dynamo): fail a refit whose new weights do not fit the engine
shoumikhin Oct 31, 2026
48ffc25
fix(dynamo): check only uint8 values for rank and constants in the wh…
shoumikhin Oct 31, 2026
4531425
fix(dynamo): keep a where with a uint8 value of lower rank or a uint8…
shoumikhin Oct 31, 2026
0f8ce50
docs(dynamo): correct a test name and two comments
shoumikhin Oct 31, 2026
8b0651f
fix(dynamo): say what TensorRT expected when it refuses a refit weight
shoumikhin Oct 31, 2026
3a0b7b4
feat(dynamo): add fallback_pytorch_plugin_ops, name ops whose plugins…
shoumikhin Oct 31, 2026
a5682a7
fix(dynamo): widen uint8 values in the where converter instead of spe…
shoumikhin Oct 31, 2026
6ea1fbf
test(dynamo): check the exact counts in the refit error
shoumikhin Oct 31, 2026
74cf0b6
fix(dynamo): widen a uint8 where constant outside the fake mode
shoumikhin Oct 31, 2026
fa34d78
fix(dynamo): give true division and float powers of integers a float …
shoumikhin Oct 31, 2026
3e8315a
fix(dynamo): keep the float type of a constant in integer division an…
shoumikhin Oct 31, 2026
abed8ef
refactor(dynamo): remove cast_int_int_div_trt_tensor
shoumikhin Oct 31, 2026
f1f9b67
test(dynamo): require full compilation in the torch.compile where test
shoumikhin Oct 31, 2026
35cddee
test(dynamo): drop require_full_compilation from the torch.compile wh…
shoumikhin Oct 31, 2026
9da0004
refactor(dynamo): drop the List import that the removed helper used
shoumikhin Oct 31, 2026
8d9d083
fix(dynamo): truncate integers in prims.div
shoumikhin Oct 31, 2026
eef322e
fix(dynamo): divide integers in prims.div with TensorRT's DIV
shoumikhin Oct 31, 2026
2c1d8c8
feat(dynamo): run accumulating index_put with TensorRT's ScatterEleme…
shoumikhin Oct 31, 2026
c3b6353
refactor(dynamo): make prims.div a single TensorRT DIV
shoumikhin Oct 31, 2026
b09e700
fix(dynamo): scatter an accumulating index_put along its one index axis
shoumikhin Oct 31, 2026
a25a423
test(dynamo): pin which accumulating index_put inputs use ScatterElem…
shoumikhin Oct 31, 2026
bd032a8
fix(dynamo): read a module pickled before the PyTorch plugin mark as …
shoumikhin Oct 31, 2026
0780385
test(dynamo): check the PyTorch plugin mark survives a state_dict rou…
shoumikhin Oct 31, 2026
3f748d5
fix(dynamo): leave data movement on a widened uint8 where to TensorRT
shoumikhin Oct 31, 2026
a0d673a
test(dynamo): fail the restored-view alias test on a compile error
shoumikhin Oct 31, 2026
d340909
docs(lowering): describe the unknown middle of the causal check
shoumikhin Oct 31, 2026
ab01550
test(lowering): cover the unknown middle of the causal check
shoumikhin Oct 31, 2026
72a556a
docs: list every setting that invalidates cached engines
shoumikhin Oct 31, 2026
fc8777b
fix(dynamo): widen uint8 for ops that only move data instead of guess…
shoumikhin Oct 31, 2026
79e2b46
test(dynamo): reload the PyTorch plugin mark into a new module
shoumikhin Oct 31, 2026
d2135e5
docs: note that strip_engine_weights invalidates cached engines on ol…
shoumikhin Oct 31, 2026
f6680d2
fix(dynamo): use ScatterElements for an accumulating index_put only w…
shoumikhin Oct 31, 2026
629bcbb
fix(dynamo): leave an accumulating index_put with a bool mask or a No…
shoumikhin Oct 31, 2026
64c2c89
test(dynamo): keep a trailing None of an accumulating index_put on Sc…
shoumikhin Oct 31, 2026
b6df6b2
fix(dynamo): keep True as the attn_bias_is_causal default
shoumikhin Oct 31, 2026
d1c3542
fix(dynamo): fold before moving offloaded constants back to the CPU i…
shoumikhin Oct 31, 2026
2a95a3a
fix(dynamo): broadcast a runtime size of 1 to a runtime size in expand
shoumikhin Oct 31, 2026
b9ce6db
fix(dynamo): broadcast index_put values to the indexed shape
shoumikhin Oct 31, 2026
bfdf94b
fix(dynamo): use every dim of a boolean mask next to other indices
shoumikhin Oct 31, 2026
b70b402
Revert "fix(dynamo): fold before moving offloaded constants back to t…
shoumikhin Oct 31, 2026
4d694e3
fix(dynamo): fold an offloaded constant on its device when an op also…
shoumikhin Oct 31, 2026
df331e8
fix(dynamo): pass every size when moving index_put values for an empt…
shoumikhin Oct 31, 2026
2a502d7
fix(dynamo): keep a widened source of a full uint8 slice_scatter as i…
shoumikhin Oct 31, 2026
6e00940
fix(dynamo): do not copy a repeated engine output that a cast already…
shoumikhin Oct 31, 2026
db53646
fix(dynamo): widen uint8 constants of data movement ops too
shoumikhin Oct 31, 2026
694f69d
fix(lowering): treat an unchecked attention bias as unknown, as main did
shoumikhin Oct 31, 2026
895ab4b
test(lowering): check that the default replaces a causal bias from an…
shoumikhin Oct 31, 2026
af3f2b5
fix(dynamo): handle an index_put index that is empty at runtime, and …
shoumikhin Oct 31, 2026
80555ae
fix(dynamo): move an offloaded constant to its device only when an op…
shoumikhin Oct 31, 2026
d0978a9
fix(dynamo): accept a computed scalar index_put value next to 0-d ind…
shoumikhin Oct 31, 2026
c778370
fix(dynamo): keep an op's own error when it also fails with offloaded…
shoumikhin Oct 31, 2026
345e670
fix(dynamo): retry folded ops only in graphs that hold offloaded cons…
shoumikhin Oct 31, 2026
2024d69
fix(dynamo): keep a computed 0-d index_put index 0-d
shoumikhin Oct 31, 2026
b45c19c
fix(dynamo): let an index_put with empty values still check the index…
shoumikhin Oct 31, 2026
120de9c
test(dynamo): check the rotary output exactly in the offloaded comple…
shoumikhin Oct 31, 2026
1dbb88a
fix(dynamo): leave a computed 0-d index_put mask as it is
shoumikhin Oct 31, 2026
dd01ad9
fix(dynamo): reject index_put values whose runtime size does not broa…
shoumikhin Oct 31, 2026
5567d20
style(tests): format test_aten_lowering_passes.py with black
shoumikhin Oct 31, 2026
610ad47
fix(dynamo): reject index_put shapes that do not broadcast
shoumikhin Nov 1, 2026
18237d7
fix(dynamo): check index_put values against runtime sizes and an empt…
shoumikhin Nov 1, 2026
bc1171a
fix(dynamo): let a one-element CPU value fill an index_put mask
shoumikhin Nov 1, 2026
80ea250
docs(dynamo): say that a converter, not TensorRT, makes a 0-d value 1-D
shoumikhin Nov 1, 2026
e7b630c
fix(dynamo): fill all of the input for a 0-d index_put mask
shoumikhin Nov 1, 2026
637e0fa
Revert "fix(dynamo): fill all of the input for a 0-d index_put mask"
shoumikhin Nov 1, 2026
61390ee
fix(dynamo): reject index_put masks that do not fit the input
shoumikhin Nov 1, 2026
cbbc0b5
fix(dynamo): let a masked_fill index_put keep Nones after its mask
shoumikhin Nov 1, 2026
f073410
fix(dynamo): stop the index_put mask check at an entry without metadata
shoumikhin Nov 1, 2026
e22f74c
refactor(dynamo): treat only bool tensors as index_put masks in the n…
shoumikhin Nov 1, 2026
8492063
style(dynamo): compare the index_put mask dtype with is
shoumikhin Nov 1, 2026
0a40014
fix(dynamo): let index_put Nones run past the last input dim
shoumikhin Nov 1, 2026
70107b3
fix(dynamo): save programs where PyTorch gives an engine a float64 input
shoumikhin Nov 1, 2026
735c729
fix(dynamo): check only masks against the input dims they cover
shoumikhin Nov 1, 2026
584552f
fix(dynamo): reject index_put values of too high a rank only when the…
shoumikhin Nov 1, 2026
1e3d97b
fix(dynamo): let the masked_fill test use what the graph does show
shoumikhin Nov 1, 2026
373e429
test(dynamo): skip the index_put mask shape case on TensorRT-RTX
shoumikhin Nov 1, 2026
49e5c66
fix(dynamo): use version 1 of TensorRT's ScatterElements plugin for a…
shoumikhin Nov 1, 2026
69bbdaf
fix(dynamo): keep the ScatterElements validator as it was and state t…
shoumikhin Nov 1, 2026
0aa63b1
fix(runtime): raise a clear error instead of crashing when TensorRT r…
shoumikhin Nov 1, 2026
676f943
fix(executorch): copy an input in another dim order to row-major orde…
shoumikhin Nov 2, 2026
6fa7d1b
test(runtime): turn CUDA graphs off in the engine assertion test
shoumikhin Nov 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions core/runtime/execute_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,12 @@ std::vector<at::Tensor> execute_engine(std::vector<at::Tensor> inputs, c10::intr
int32_t const io_size{compiled_engine->cuda_engine->getNbIOTensors()};
std::vector<char const*> names(io_size);
int32_t const nbNames = ctx->inferShapes(names.size(), names.data());
// TensorRT returns -1 and writes no names when it rejects the shapes, such as on a failed assertion.
TORCHTRT_CHECK(
nbNames >= 0,
"Engine " << compiled_engine->name
<< " rejected the input shapes, see the TensorRT error above (for example a failed assertion)");
names.resize(static_cast<size_t>(nbNames));
TORCHTRT_CHECK(
nbNames == 0,
"The shapes of the inputs: " << names << " of engine " << compiled_engine->name
Expand Down Expand Up @@ -669,6 +675,12 @@ std::vector<at::Tensor> execute_engine(std::vector<at::Tensor> inputs, c10::intr
int32_t const io_size{compiled_engine->cuda_engine->getNbIOTensors()};
std::vector<char const*> names(io_size);
int32_t const nbNames = ctx->inferShapes(names.size(), names.data());
// TensorRT returns -1 and writes no names when it rejects the shapes, such as on a failed assertion.
TORCHTRT_CHECK(
nbNames >= 0,
"Engine " << compiled_engine->name
<< " rejected the input shapes, see the TensorRT error above (for example a failed assertion)");
names.resize(static_cast<size_t>(nbNames));
TORCHTRT_CHECK(
nbNames == 0,
"The shapes of the inputs: " << names << " of engine " << compiled_engine->name
Expand Down
2 changes: 2 additions & 0 deletions cpp/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,14 @@ cc_library(
"@executorch//:executorch_headers",
"@executorch//:extension_cuda",
"@tensorrt//:nvinfer",
"@tensorrt//:nvinferplugin",
],
":sbsa": [
"@cuda//:cudart",
"@executorch//:executorch_headers",
"@executorch//:extension_cuda",
"@tensorrt_sbsa//:nvinfer",
"@tensorrt_sbsa//:nvinferplugin",
],
"//conditions:default": [],
}),
Expand Down
1 change: 1 addition & 0 deletions cpp/src/torch_tensorrt/executorch/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ endif()
set(_executorch_backend_tensorrt_link_libraries
CUDA::cudart
TensorRT::nvinfer
TensorRT::nvinfer_plugin
Threads::Threads
extension_cuda
)
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/torch_tensorrt/executorch/EngineHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ struct EngineHandle {
bool pin_ignored_reported = false;
// Same reasoning: an unaligned buffer is usually the same view on every call.
bool unaligned_io_reported = false;
// And a program gives an input the same dim order on every call.
bool reordered_input_reported = false;

~EngineHandle();
};
Expand Down
14 changes: 12 additions & 2 deletions cpp/src/torch_tensorrt/executorch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ cmake --build build-torchtrt-executorch --target executorch_trt_backend -j
- Link the archive into your program with `-Wl,--whole-archive`, together with
`libextension_cuda.so`, ExecuTorch's portable kernels (`libportable_ops_lib.a`, also with
`-Wl,--whole-archive`, and `libportable_kernels.a`), the ExecuTorch core (`libexecutorch_core.a`),
TensorRT's `nvinfer`, the CUDA runtime and pthreads. The delegate and the kernels register
themselves from static initializers, and a plain static link drops them.
TensorRT's `nvinfer` and `nvinfer_plugin`, the CUDA runtime and pthreads. The delegate and the
kernels register themselves from static initializers, and a plain static link drops them.
- The portable kernels include the two copy operators that a default export puts at the edges of
the program, `et_copy::_h2d_copy.out` and `et_copy::_d2h_copy.out`. Without them, loading fails
with `Error::OperatorMissing`, and a build with ExecuTorch logging on also logs
Expand Down Expand Up @@ -160,6 +160,16 @@ in place. `torch_tensorrt.save()` plans memory on 256-byte boundaries. ExecuTorc
16 bytes, so a program finalized with `to_executorch()` directly should pass
`MemoryPlanningPass(alignment=256)`, or its planned buffers are copied too.

TensorRT also reads every input in row-major order. ExecuTorch can give an input another dim order,
for example a channels-last program input, or the output of efficient attention in the CUDA backend.
The delegate copies such an input into row-major order before every call, so the answer is right,
and logs this once per engine. The cost depends on the layout. Attention's output and channels-last
need only a few copy calls: about 0.25 ms for a 640x480 float32 image on an A100. A transposed
matrix needs one call per row: about 6 ms for 1024x1024. From host memory the copy is much slower,
tens of milliseconds or more for an image, so export such an input in row-major order where you can.
An input that the engine updates in place must be row-major, or the call fails with
`Error::InvalidArgument`.

### CUDA streams

- With no stream chosen, the delegate runs on `cudaStreamPerThread`, the default stream of the
Expand Down
109 changes: 97 additions & 12 deletions cpp/src/torch_tensorrt/executorch/TensorRTBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <vector>

#include <NvInfer.h>
#include <NvInferPlugin.h>
#include <cuda_runtime.h>

#include <executorch/extension/cuda/caller_stream.h>
Expand Down Expand Up @@ -133,26 +134,29 @@ Error report_load_error(const char* operation) {
// TensorRT reads an engine only with the version that built it, unless it was built version
// compatible, and its own error then names internal serialization tags rather than versions.
void log_tensorrt_versions(const std::string& exported_version) {
// TensorRT 10 and later encode the version as major * 10000 + minor * 100 + patch.
const int32_t version = getInferLibVersion();
const int major = version / 10000;
const int minor = version / 100 % 100;
const int patch = version % 100;
// The exporter records tensorrt.__version__, which includes the build number, so print the
// runtime's build number too. Otherwise equal versions read as different.
const int major = getInferLibMajorVersion();
const int minor = getInferLibMinorVersion();
const int patch = getInferLibPatchVersion();
const int build = getInferLibBuildVersion();
if (exported_version.empty()) {
ET_LOG(
Error,
"TensorRTBackend::init: this runtime is TensorRT %d.%d.%d, and the program does not record which TensorRT exported the engine",
"TensorRTBackend::init: this runtime is TensorRT %d.%d.%d.%d, and the program does not record which TensorRT exported the engine",
major,
minor,
patch);
patch,
build);
} else {
ET_LOG(
Error,
"TensorRTBackend::init: the engine was exported with TensorRT %s, and this runtime is TensorRT %d.%d.%d",
"TensorRTBackend::init: the engine was exported with TensorRT %s, and this runtime is TensorRT %d.%d.%d.%d",
exported_version.c_str(),
major,
minor,
patch);
patch,
build);
}
}

Expand Down Expand Up @@ -293,6 +297,9 @@ nvinfer1::IRuntime* TensorRTBackend::shared_runtime() {
}
if (logger == nullptr) {
logger = new TRTLogger();
// Engines can use TensorRT's standard plugins, such as ScatterElements for an
// accumulating index_put, and their creators are only registered on request.
initLibNvInferPlugins(logger, "");
}
runtime = nvinfer1::createInferRuntime(*logger);
if (runtime != nullptr) {
Expand Down Expand Up @@ -740,6 +747,67 @@ bool is_tensor_address_aligned(const void* ptr) {
return reinterpret_cast<uintptr_t>(ptr) % kTensorAddressAlignment == 0;
}

// Size-1 dims are skipped, since they move no element, and a tensor built without strides is row-major.
int innermost_non_row_major_dim(const exec_aten::Tensor& t) {
const auto strides = t.strides();
if (strides.data() == nullptr) {
return -1;
}
int64_t row_major_stride = 1;
for (int d = static_cast<int>(t.dim()) - 1; d >= 0; --d) {
if (t.size(d) != 1 && strides[d] != row_major_stride) {
return d;
}
row_major_stride *= t.size(d);
}
return -1;
}

// Each row of the 2-D copies is a run of the row-major inner dims, so no CUDA kernel is needed. An outer dim whose
// stride spans all the rows so far joins them, so channels-last costs one copy per channel, not one per image row.
cudaError_t copy_to_row_major(void* dst, const exec_aten::Tensor& src, cudaStream_t stream) {
const int dim = innermost_non_row_major_dim(src);
const size_t element_size = static_cast<size_t>(src.element_size());
size_t row_bytes = element_size;
for (int d = dim + 1; d < src.dim(); ++d) {
row_bytes *= static_cast<size_t>(src.size(d));
}
const size_t pitch = static_cast<size_t>(src.strides()[dim]);
size_t rows = static_cast<size_t>(src.size(dim));
int top = dim;
while (top > 0 && static_cast<size_t>(src.strides()[top - 1]) == rows * pitch) {
rows *= static_cast<size_t>(src.size(--top));
}
size_t blocks = 1;
for (int d = 0; d < top; ++d) {
blocks *= static_cast<size_t>(src.size(d));
}
const auto* src_bytes = static_cast<const uint8_t*>(src.const_data_ptr());
auto* dst_bytes = static_cast<uint8_t*>(dst);
for (size_t block = 0; block < blocks; ++block) {
size_t src_offset = 0;
size_t index = block;
for (int d = top - 1; d >= 0; --d) {
const size_t size = static_cast<size_t>(src.size(d));
src_offset += index % size * static_cast<size_t>(src.strides()[d]);
index /= size;
}
const cudaError_t err = cudaMemcpy2DAsync(
dst_bytes + block * rows * row_bytes,
row_bytes,
src_bytes + src_offset * element_size,
pitch * element_size,
row_bytes,
rows,
cudaMemcpyDefault,
stream);
if (err != cudaSuccess) {
return err;
}
}
return cudaSuccess;
}

bool is_cuda_accessible_ptr(const void* ptr) {
if (ptr == nullptr) {
return false;
Expand Down Expand Up @@ -1898,6 +1966,15 @@ Error TensorRTBackend::execute(BackendExecutionContext& context, DelegateHandle*
name.c_str());
return Error::InvalidArgument;
}
// TensorRT reads row-major, and ExecuTorch can plan another dim order, such as efficient attention's output.
const bool row_major = innermost_non_row_major_dim(et_in) < 0;
if (engine->input_is_alias_target[i] && !row_major) {
ET_LOG(
Error,
"TensorRTBackend::execute: aliased input '%s' must be row-major, because the engine updates it in place",
name.c_str());
return Error::InvalidArgument;
}

void* bind_ptr = nullptr;
if (et_in.nbytes() == 0) {
Expand All @@ -1909,11 +1986,18 @@ Error TensorRTBackend::execute(BackendExecutionContext& context, DelegateHandle*
engine->cached_input_sizes[i] = 1;
}
bind_ptr = engine->cached_input_ptrs[i];
} else if (device_reachable && is_tensor_address_aligned(et_in.const_data_ptr())) {
} else if (device_reachable && row_major && is_tensor_address_aligned(et_in.const_data_ptr())) {
bind_ptr = et_in.mutable_data_ptr();
} else {
if (device_reachable) {
if (device_reachable && row_major) {
report_unaligned(name, et_in.const_data_ptr());
} else if (!row_major && !engine->reordered_input_reported) {
engine->reordered_input_reported = true;
ET_LOG(
Info,
"TensorRTBackend::execute: input '%s' is not in row-major dim order, and TensorRT reads row-major, so "
"it is copied into row-major order on every call (reported once per engine)",
name.c_str());
}
const size_t needed = et_in.nbytes();
if (needed > engine->cached_input_sizes[i]) {
Expand All @@ -1929,7 +2013,8 @@ Error TensorRTBackend::execute(BackendExecutionContext& context, DelegateHandle*
engine->cached_input_sizes[i] = needed;
}
bind_ptr = engine->cached_input_ptrs[i];
cuda_err = cudaMemcpyAsync(bind_ptr, et_in.const_data_ptr(), needed, cudaMemcpyDefault, stream);
cuda_err = row_major ? cudaMemcpyAsync(bind_ptr, et_in.const_data_ptr(), needed, cudaMemcpyDefault, stream)
: copy_to_row_major(bind_ptr, et_in, stream);
drain_on_early_return.arm();
if (cuda_err != cudaSuccess) {
ET_LOG(
Expand Down
24 changes: 18 additions & 6 deletions docsrc/user_guide/compilation/compilation_settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,18 @@ Optimization Tuning
Disable only when you need strict IEEE FP32 semantics.
* - ``attn_bias_is_causal``
- ``True``
- Whether the attn_bias in efficient SDPA is causal. Default is True. This can
accelerate models from HF because attn_bias is always a causal mask in HF.
If you want to use non-causal attn_bias, you can set this to False.
- Which attn_bias of efficient SDPA to replace with TensorRT's built-in causal
mask, which is faster and more accurate. ``True``: a bias proven causal, one
that depends on no graph input and holds 0 on and below the diagonal and -inf
(or the dtype's lowest value) above, and also a bias that cannot be proven,
such as one built from an input, with a dynamic sequence length, or from a
module buffer under ``torch.compile``; you assert that it is causal. ``None``:
only a bias proven causal, for models whose input-built masks may not be
causal, such as masks with padding. A bias that is computed and found not
causal is always kept. A mask longer than 4096, or one whose check fails, is
not proven: ``True`` replaces it and ``None`` keeps it. ``False``: never
replace. A replaced bias is not read again, so refitting the weights it came
from does not change the engine.

----

Expand Down Expand Up @@ -450,13 +459,16 @@ rebuilt from scratch:

``max_aux_streams``, ``version_compatible``,
``optimization_level``, ``disable_tf32``, ``sparse_weights``,
``engine_capability``, ``hardware_compatible``, ``refit_identical_engine_weights``,
``engine_capability``, ``hardware_compatible``, ``target_compute_capabilities``,
``refit_identical_engine_weights``,
``immutable_weights``, ``enable_weight_streaming``, ``tiling_optimization_level``,
``l2_limit_for_tiling``, ``enable_autocast``, ``autocast_low_precision_type``,
``autocast_excluded_nodes``, ``autocast_excluded_ops``,
``autocast_max_output_threshold``, ``autocast_max_depth_of_reduction``,
``autocast_calibration_dataloader``, ``decompose_attention``, ``attn_bias_is_causal``,
``fold_shared_kv_attention``.
``autocast_calibration_dataloader``, ``decompose_attention``,
``disabled_constant_fold_exclusions``, ``attn_bias_is_causal``,
``fold_shared_kv_attention``, ``build_route``, ``fallback_pytorch_plugin_ops``.
On TensorRT older than 10.14, ``strip_engine_weights`` also invalidates cached engines.

Settings not in this list (e.g., ``debug``, ``dryrun``, ``pass_through_build_failures``)
can be changed without invalidating the cache.
20 changes: 20 additions & 0 deletions docsrc/user_guide/runtime_performance/executorch.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,26 @@ in `Run from Python`_ and `Run from C++`_. If the CUDA part has weights, loading
the same directory overwrites the first one's weights. The first model can then return wrong
answers with no error, or fail to load. Save each model into its own directory.

Keep plugins that run PyTorch out of the engine
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Some converters build a TensorRT plugin that runs PyTorch when the engine runs: ``aten.index_put``
with ``accumulate=True`` uses Torch-TensorRT's ScatterAdd plugin, and a plugin made with
``torch_tensorrt.dynamo.conversion.plugins.custom_op`` that does not use an AOT kernel runs the
PyTorch op from Python. The ExecuTorch runtime does not load PyTorch, so ``save()`` refuses such an
engine and names the operators. Compile with ``fallback_pytorch_plugin_ops=True`` to keep those
operators out of TensorRT. A ``torch.cond`` with one of them in a branch then runs in PyTorch too.
With the setting, an accumulating ``index_put`` with one 1-D index stays in TensorRT instead,
through TensorRT's own ScatterElements plugin, which needs no PyTorch.

.. code-block:: python

trt_gm = torch_tensorrt.dynamo.compile(
exported,
arg_inputs=example_inputs,
fallback_pytorch_plugin_ops=True,
)

Keep inputs and outputs on the GPU
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
4 changes: 1 addition & 3 deletions examples/torchtrt_executorch_example/export_multi_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ def stamp(phase: str) -> None:
# narrower and shallower. ``sliding_window`` keeps the 1B model's 512, which is
# wider than MAX_SEQ, so as in the real model the window never binds over the
# exported range and every layer attends to the whole prefix. Narrowing it below
# MAX_SEQ would make the sliding layers genuinely windowed, and the engine would
# then need ``attn_bias_is_causal=False`` to keep the mask instead of assuming
# plain causality.
# MAX_SEQ would make the sliding layers genuinely windowed.
MINI_CONFIG = dict(
vocab_size=2048,
hidden_size=320,
Expand Down
10 changes: 8 additions & 2 deletions py/torch-tensorrt-executorch-runtime/native/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ cmake(
deps = [
"@cuda//:cudart",
] + select({
":aarch64_linux": ["@tensorrt_sbsa//:nvinfer"],
"//conditions:default": ["@tensorrt//:nvinfer"],
":aarch64_linux": [
"@tensorrt_sbsa//:nvinfer",
"@tensorrt_sbsa//:nvinferplugin",
],
"//conditions:default": [
"@tensorrt//:nvinfer",
"@tensorrt//:nvinferplugin",
],
}),
)
1 change: 1 addition & 0 deletions py/torch-tensorrt-executorch-runtime/native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ target_link_libraries(executorch_backend_tensorrt PRIVATE
executorch::extension_cuda
CUDA::cudart
TensorRT::nvinfer
TensorRT::nvinfer_plugin
Threads::Threads)

# Build to the standard ExecuTorch's own exported configuration asks for. The delegate is compiled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ fi
for entry in $(needed_entries "${dyn}"); do
case "${entry}" in
libexecutorch.so|libexecutorch_extension_cuda.so|libexecutorch_threadpool.so) ;;
libnvinfer.so.*|libcudart.so.*) ;;
libnvinfer.so.*|libnvinfer_plugin.so.*|libcudart.so.*) ;;
libstdc++.so.*|libc.so.*|libm.so.*|libgcc_s.so.*|libdl.so.*|librt.so.*|libpthread.so.*) ;;
ld-linux-*.so.*|libatomic.so.*|libz.so.*) ;;
*) fail "${target} links ${entry}, which is not one of the libraries this wheel may depend on" ;;
Expand Down
Loading
Loading