Skip to content

Commit f215c0c

Browse files
committed
add torch-tensorrt-executorch-delegate wheel
1 parent 619aaf0 commit f215c0c

15 files changed

Lines changed: 513 additions & 10 deletions

File tree

.github/workflows/executorch-static-linux.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ jobs:
6868
build-matrix: ${{ needs.select-matrix.outputs.matrix }}
6969
pre-script: packaging/pre_build_script.sh
7070
fail-on-empty: false
71+
upload-artifact: torch-tensorrt-executorch-delegate
7172
script: |
7273
set -euo pipefail
7374
BAZELISK_VERSION="1.26.0"
@@ -96,5 +97,13 @@ jobs:
9697
export EXECUTORCH_SOURCE_DIR="$(dirname "${executorch_cmake_location%%:*}")"
9798
export EXECUTORCH_ROOT="${EXECUTORCH_SOURCE_DIR}"
9899
# this is to verify the end user's workflow
99-
python -m pip install pyyaml "executorch>=1.3.1"
100+
python -m pip install pyyaml executorch
101+
102+
# Build the no-compile-for-users Python runtime/delegate wheel.
103+
export TORCH_TENSORRT_EXECUTORCH_DELEGATE_VERSION="$(python -c 'import torch_tensorrt; print(torch_tensorrt.__version__)')"
104+
python -m pip wheel --no-build-isolation --no-deps --wheel-dir dist packaging/executorch_delegate
105+
python -m pip install --no-deps --force-reinstall dist/torch_tensorrt_executorch_delegate-*.whl
106+
python -c 'from torch_tensorrt_executorch_delegate import runtime; assert runtime().backend_registry.is_available("TensorRTBackend")'
107+
python examples/torchtrt_executorch_example/export_static_shape.py --model_path="${RUNNER_TEMP}/torchtrt-python.pte"
108+
python examples/executorch_reference_runner/load_model.py --model_path="${RUNNER_TEMP}/torchtrt-python.pte" --num_runs=1
100109
.github/scripts/verify-executorch-reference-runner.sh

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,6 @@ CLAUDE.md
8686
/compile_commands.json
8787
/external/
8888
build-executorch/
89+
packaging/executorch_delegate/build/
90+
packaging/executorch_delegate/dist/
91+

MODULE.bazel

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ new_git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl"
4242

4343
local_torch = use_repo_rule("//toolchains:local_torch.bzl", "local_torch")
4444

45-
# Pinned to the ExecuTorch release/1.3 branch head.
45+
# Pinned to ExecuTorch main for PyTorch 2.14 native ABI compatibility.
4646
new_git_repository(
4747
name = "executorch",
4848
build_file = "@//third_party/executorch:BUILD",
49-
# latest commit in release/1.3 branch
50-
commit = "6118688a095fd9697224f5cad72ce42db641c9cd",
49+
# PyTorch 2.14-compatible ExecuTorch main commit
50+
commit = "a6d812a082df57898b8608f56c867140cc9da32c",
5151
patch_cmds = [
5252
"find . -mindepth 2 \\( -name BUILD -o -name BUILD.bazel \\) -delete",
5353
"mkdir executorch && find . -mindepth 1 -maxdepth 1 ! -name executorch ! -name BUILD ! -name BUILD.bazel ! -name REPO.bazel -exec cp -a {} executorch/ \\;",

examples/executorch_reference_runner/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ filegroup(
77
srcs = [
88
"CMakeLists.txt",
99
"README.md",
10+
"load_model.py",
1011
"main.cpp",
1112
],
1213
)

examples/executorch_reference_runner/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,28 @@ build-executorch-reference-runner/lib/libexecutorch_trt_backend.a
7979

8080
## Load And Run A `.pte` Model
8181

82+
### Python
83+
84+
Install the complete prebuilt Python runtime and delegate:
85+
86+
```bash
87+
pip install "torch-tensorrt[executorch]"
88+
```
89+
90+
Load and run the model without an ExecuTorch checkout or native build:
91+
92+
```bash
93+
python examples/executorch_reference_runner/load_model.py \
94+
--model_path=model.pte \
95+
--num_runs=1
96+
```
97+
98+
The extra installs `executorch` and the matching
99+
`torch-tensorrt-executorch-delegate` wheel. That wheel contains an ExecuTorch
100+
Python runtime with `TensorRTBackend` linked into its backend registry.
101+
102+
### C++
103+
82104
Run the reference runner against a Torch-TensorRT compiled ExecuTorch model:
83105

84106
```bash
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Load and optionally run a Torch-TensorRT ExecuTorch ``.pte`` model.
2+
3+
The default input matches ``export_static_shape.py``: one CUDA float tensor
4+
with shape ``(2, 3, 4, 4)`` filled with ones.
5+
"""
6+
7+
import argparse
8+
from pathlib import Path
9+
10+
import torch
11+
from torch_tensorrt.executorch.runtime import load
12+
13+
14+
def parse_args() -> argparse.Namespace:
15+
parser = argparse.ArgumentParser()
16+
parser.add_argument("--model_path", type=Path, default=Path("model.pte"))
17+
parser.add_argument("--method", default="forward")
18+
parser.add_argument("--num_runs", type=int, default=1)
19+
parser.add_argument(
20+
"--load_only",
21+
action="store_true",
22+
help="Parse the program and print its methods without loading/executing one",
23+
)
24+
return parser.parse_args()
25+
26+
27+
def main() -> None:
28+
args = parse_args()
29+
if not args.model_path.is_file():
30+
raise FileNotFoundError(f"ExecuTorch model not found: {args.model_path}")
31+
if args.num_runs < 1:
32+
raise ValueError("--num_runs must be at least 1")
33+
34+
program = load(args.model_path)
35+
print(f"Loaded {args.model_path}")
36+
print(f"Program methods: {sorted(program.method_names)}")
37+
38+
if args.load_only:
39+
return
40+
if args.method not in program.method_names:
41+
raise ValueError(
42+
f"Method {args.method!r} is not in the program; "
43+
f"available methods: {sorted(program.method_names)}"
44+
)
45+
inputs = (torch.ones((2, 3, 4, 4), dtype=torch.float32),)
46+
for run in range(args.num_runs):
47+
outputs = program.run(inputs, args.method)
48+
print(f"Run {run + 1} outputs:")
49+
for index, output in enumerate(outputs):
50+
if isinstance(output, torch.Tensor):
51+
print(
52+
f" output[{index}]: shape={tuple(output.shape)}, "
53+
f"dtype={output.dtype}, device={output.device}, "
54+
f"sample={output.flatten()[:8]}"
55+
)
56+
else:
57+
print(f" output[{index}]: {output!r}")
58+
59+
60+
if __name__ == "__main__":
61+
main()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Torch-TensorRT ExecuTorch Delegate Wheel
2+
3+
This directory builds `torch-tensorrt-executorch-delegate`. The Linux wheel
4+
contains an ExecuTorch `_portable_lib` Python runtime with `TensorRTBackend`
5+
force-linked into the same native module that owns the backend registry.
6+
7+
The wheel must use the same Python, PyTorch, ExecuTorch, CUDA, TensorRT, and
8+
C++ ABI as its matching Torch-TensorRT wheel.
9+
10+
## Build
11+
12+
```bash
13+
executorch_cmake_location="$(bazel query \
14+
@executorch//:executorch/CMakeLists.txt --output=location)"
15+
export EXECUTORCH_SOURCE_DIR="$(dirname "${executorch_cmake_location%%:*}")"
16+
export TensorRT_ROOT=/path/to/TensorRT
17+
18+
python -m pip install executorch==1.3.1
19+
python -m pip wheel --no-build-isolation --no-deps \
20+
--wheel-dir dist packaging/executorch_delegate
21+
```
22+
23+
The static ExecuTorch and delegate archives are intermediate build inputs;
24+
users receive the final native Python module and do not compile anything.
25+
26+
## Use
27+
28+
```bash
29+
pip install "torch-tensorrt[executorch]"
30+
```
31+
32+
```python
33+
import torch
34+
from torch_tensorrt.executorch.runtime import load
35+
36+
program = load("model.pte")
37+
outputs = program.forward(torch.ones((2, 3, 4, 4)))
38+
```
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
cmake_minimum_required(VERSION 3.24)
2+
project(torch_tensorrt_executorch_delegate LANGUAGES CXX)
3+
4+
if(NOT EXECUTORCH_SOURCE_DIR OR NOT TORCH_TENSORRT_SOURCE_DIR)
5+
message(FATAL_ERROR "EXECUTORCH_SOURCE_DIR and TORCH_TENSORRT_SOURCE_DIR are required")
6+
endif()
7+
8+
list(APPEND CMAKE_MODULE_PATH "${TORCH_TENSORRT_SOURCE_DIR}/cmake/Modules")
9+
find_package(TensorRT REQUIRED)
10+
find_package(CUDAToolkit REQUIRED)
11+
find_package(Threads REQUIRED)
12+
13+
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
14+
set(EXECUTORCH_BUILD_PYBIND ON CACHE BOOL "" FORCE)
15+
set(EXECUTORCH_BUILD_EXTENSION_MODULE ON CACHE BOOL "" FORCE)
16+
set(EXECUTORCH_BUILD_EXTENSION_DATA_LOADER ON CACHE BOOL "" FORCE)
17+
set(EXECUTORCH_BUILD_EXTENSION_FLAT_TENSOR ON CACHE BOOL "" FORCE)
18+
set(EXECUTORCH_BUILD_EXTENSION_NAMED_DATA_MAP ON CACHE BOOL "" FORCE)
19+
set(EXECUTORCH_BUILD_EXTENSION_TENSOR ON CACHE BOOL "" FORCE)
20+
set(EXECUTORCH_BUILD_KERNELS_OPTIMIZED OFF CACHE BOOL "" FORCE)
21+
set(EXECUTORCH_BUILD_XNNPACK OFF CACHE BOOL "" FORCE)
22+
set(EXECUTORCH_BUILD_TESTS OFF CACHE BOOL "" FORCE)
23+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
24+
25+
add_subdirectory("${EXECUTORCH_SOURCE_DIR}" executorch)
26+
27+
add_library(torch_tensorrt_executorch_backend STATIC
28+
"${TORCH_TENSORRT_SOURCE_DIR}/cpp/src/torch_tensorrt/executorch/TensorRTBackend.cpp"
29+
"${TORCH_TENSORRT_SOURCE_DIR}/cpp/src/torch_tensorrt/executorch/TensorRTBlobHeader.cpp")
30+
target_compile_features(torch_tensorrt_executorch_backend PUBLIC cxx_std_17)
31+
target_compile_definitions(torch_tensorrt_executorch_backend
32+
PRIVATE C10_USING_CUSTOM_GENERATED_MACROS)
33+
target_include_directories(torch_tensorrt_executorch_backend PRIVATE
34+
"${TORCH_TENSORRT_SOURCE_DIR}"
35+
"${TORCH_TENSORRT_SOURCE_DIR}/cpp/include"
36+
"${EXECUTORCH_SOURCE_DIR}/.."
37+
"${EXECUTORCH_SOURCE_DIR}/runtime/core/portable_type/c10")
38+
target_link_libraries(torch_tensorrt_executorch_backend PRIVATE
39+
CUDA::cudart TensorRT::nvinfer Threads::Threads executorch)
40+
41+
if(NOT TARGET portable_lib)
42+
message(FATAL_ERROR "ExecuTorch did not define portable_lib")
43+
endif()
44+
45+
# ExecuTorch portable c10 delegates selected headers to torch/headeronly. The
46+
# pybind bridge must resolve those delegated headers from the exact PyTorch
47+
# wheel it links, not ExecuTorch's vendored snapshot.
48+
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/torch_header_shim/torch")
49+
file(CREATE_LINK
50+
"${TORCH_PRIMARY_INCLUDE_DIR}/torch/headeronly"
51+
"${CMAKE_BINARY_DIR}/torch_header_shim/torch/headeronly"
52+
SYMBOLIC COPY_ON_ERROR)
53+
target_include_directories(util BEFORE PRIVATE "${CMAKE_BINARY_DIR}/torch_header_shim")
54+
target_include_directories(portable_lib BEFORE PRIVATE "${CMAKE_BINARY_DIR}/torch_header_shim")
55+
56+
# TensorRTBackend registers through a static initializer. Force its complete
57+
# archive into the same pybind module that owns ExecuTorch's backend registry.
58+
target_link_libraries(portable_lib PRIVATE
59+
"$<LINK_LIBRARY:WHOLE_ARCHIVE,torch_tensorrt_executorch_backend>"
60+
extension_threadpool
61+
CUDA::cudart TensorRT::nvinfer Threads::Threads)
62+
set_target_properties(portable_lib PROPERTIES
63+
LIBRARY_OUTPUT_DIRECTORY "${DELEGATE_LIBRARY_OUTPUT_DIRECTORY}"
64+
OUTPUT_NAME "_portable_lib")
65+
set_target_properties(data_loader PROPERTIES
66+
LIBRARY_OUTPUT_DIRECTORY "${DELEGATE_LIBRARY_OUTPUT_DIRECTORY}")
67+
68+
add_custom_target(torch_tensorrt_executorch_portable_lib DEPENDS portable_lib data_loader)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[build-system]
2+
requires = [
3+
"setuptools>=68",
4+
"wheel>=0.40",
5+
"torch",
6+
]
7+
build-backend = "setuptools.build_meta"
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""Build the precompiled Torch-TensorRT backend for ExecuTorch Python."""
2+
3+
from __future__ import annotations
4+
5+
import importlib.metadata
6+
import os
7+
import pathlib
8+
import re
9+
import subprocess
10+
import sys
11+
12+
import torch
13+
from torch.utils.cpp_extension import include_paths
14+
from setuptools import Extension, find_packages, setup
15+
from setuptools.command.build_ext import build_ext
16+
17+
HERE = pathlib.Path(__file__).resolve().parent
18+
REPO_ROOT = HERE.parents[1]
19+
20+
21+
def torchtrt_version() -> str:
22+
if value := os.getenv("TORCH_TENSORRT_EXECUTORCH_DELEGATE_VERSION"):
23+
return value
24+
source = (REPO_ROOT / "py/torch_tensorrt/_version.py").read_text()
25+
match = re.search(r'^__version__\s*=\s*["\']([^"\']+)', source, re.MULTILINE)
26+
if not match:
27+
raise RuntimeError("Could not determine the Torch-TensorRT version")
28+
return match.group(1)
29+
30+
31+
class CMakeExtension(Extension):
32+
def __init__(self, name: str) -> None:
33+
super().__init__(name, sources=[])
34+
35+
36+
class CMakeBuild(build_ext):
37+
def build_extension(self, ext: Extension) -> None:
38+
if sys.platform != "linux":
39+
raise RuntimeError("The ExecuTorch TensorRT delegate supports Linux only")
40+
executorch_source = os.getenv("EXECUTORCH_SOURCE_DIR")
41+
if not executorch_source:
42+
raise RuntimeError(
43+
"Set EXECUTORCH_SOURCE_DIR to the pinned ExecuTorch source tree"
44+
)
45+
46+
output = pathlib.Path(self.get_ext_fullpath(ext.name)).resolve()
47+
if output.exists():
48+
return
49+
build_dir = pathlib.Path(self.build_temp) / "executorch_delegate_native"
50+
output.parent.mkdir(parents=True, exist_ok=True)
51+
build_dir.mkdir(parents=True, exist_ok=True)
52+
configure = [
53+
"cmake",
54+
"-S",
55+
str(HERE / "native"),
56+
"-B",
57+
str(build_dir),
58+
f"-DEXECUTORCH_SOURCE_DIR={pathlib.Path(executorch_source).resolve()}",
59+
f"-DTORCH_TENSORRT_SOURCE_DIR={REPO_ROOT}",
60+
f"-DPYTHON_EXECUTABLE={sys.executable}",
61+
f"-DTORCH_PRIMARY_INCLUDE_DIR={include_paths()[0]}",
62+
f"-DDELEGATE_LIBRARY_OUTPUT_DIRECTORY={output.parent}",
63+
f"-DCMAKE_BUILD_TYPE={'Debug' if self.debug else 'Release'}",
64+
]
65+
if value := os.getenv("TensorRT_ROOT"):
66+
configure.append(f"-DTensorRT_ROOT={value}")
67+
configure.extend(os.getenv("CMAKE_ARGS", "").split())
68+
subprocess.run(configure, check=True)
69+
subprocess.run(
70+
[
71+
"cmake",
72+
"--build",
73+
str(build_dir),
74+
"--target",
75+
"torch_tensorrt_executorch_portable_lib",
76+
"--parallel",
77+
str(self.parallel or os.cpu_count() or 1),
78+
],
79+
check=True,
80+
)
81+
library_stem = (
82+
"_portable_lib" if ext.name.endswith("._portable_lib") else "data_loader"
83+
)
84+
built = next(output.parent.glob(f"{library_stem}*.so"), None)
85+
if built is None:
86+
raise RuntimeError(
87+
f"CMake did not produce {library_stem} in {output.parent}"
88+
)
89+
if built != output:
90+
built.replace(output)
91+
92+
93+
executorch_version = importlib.metadata.version("executorch")
94+
setup(
95+
name="torch-tensorrt-executorch-delegate",
96+
version=torchtrt_version(),
97+
description="Torch-TensorRT delegate for the ExecuTorch Python runtime",
98+
packages=find_packages(),
99+
ext_modules=[
100+
CMakeExtension("torch_tensorrt_executorch_delegate._portable_lib"),
101+
CMakeExtension("torch_tensorrt_executorch_delegate.data_loader"),
102+
],
103+
cmdclass={"build_ext": CMakeBuild},
104+
python_requires=">=3.10",
105+
install_requires=[
106+
f"torch=={torch.__version__}",
107+
f"executorch=={executorch_version}",
108+
],
109+
zip_safe=False,
110+
)

0 commit comments

Comments
 (0)