Skip to content

Commit e731449

Browse files
authored
OpenVINO Export Llama Support
Differential Revision: D84544001 Pull Request resolved: #14022
1 parent c539dfc commit e731449

File tree

20 files changed

+889
-157
lines changed

20 files changed

+889
-157
lines changed

backends/openvino/CMakeLists.txt

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,30 +53,6 @@ target_sources(
5353

5454
executorch_target_link_options_shared_lib(openvino_backend)
5555

56-
if(EXECUTORCH_BUILD_OPENVINO_EXECUTOR_RUNNER)
57-
# Build executor runner binary for openvino backend
58-
list(APPEND openvino_executor_runner_libs openvino_backend executorch)
59-
60-
set(_openvino_executor_runner__srcs
61-
${EXECUTORCH_ROOT}/examples/portable/executor_runner/executor_runner.cpp
62-
${EXECUTORCH_ROOT}/extension/data_loader/file_data_loader.cpp
63-
${EXECUTORCH_ROOT}/extension/evalue_util/print_evalue.cpp
64-
${EXECUTORCH_ROOT}/extension/runner_util/inputs.cpp
65-
${EXECUTORCH_ROOT}/extension/runner_util/inputs_portable.cpp
66-
)
67-
add_executable(openvino_executor_runner ${_openvino_executor_runner__srcs})
68-
69-
list(APPEND openvino_executor_runner_libs)
70-
71-
target_link_libraries(
72-
openvino_executor_runner gflags portable_ops_lib
73-
${openvino_executor_runner_libs}
74-
)
75-
target_compile_options(
76-
openvino_executor_runner PUBLIC ${_common_compile_options}
77-
)
78-
endif()
79-
8056
# Install OpenVINO backend library to the lib directory
8157
install(
8258
TARGETS openvino_backend

backends/openvino/README.md

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ For more information on the supported hardware, please refer to [OpenVINO System
1818
executorch
1919
├── backends
2020
│ └── openvino
21+
│ ├── quantizer
22+
│ ├── observers
23+
│ └── nncf_observers.py
24+
│ ├── __init__.py
25+
│ └── quantizer.py
2126
│ ├── runtime
2227
│ ├── OpenvinoBackend.cpp
2328
│ └── OpenvinoBackend.h
@@ -42,11 +47,23 @@ executorch
4247

4348
Before you begin, ensure you have openvino installed and configured on your system.
4449

45-
### Build OpenVINO from Source
50+
### Use OpenVINO from Release Packages
51+
52+
1. Download the OpenVINO release package from [here](https://docs.openvino.ai/2025/get-started/install-openvino.html). Make sure to select your configuration and click on **OpenVINO Archives** under the distribution section to download the appropriate archive for your platform.
53+
54+
2. Extract the release package from the archive and set the environment variables.
55+
56+
```bash
57+
tar -zxf openvino_toolkit_<your_release_configuration>.tgz
58+
cd openvino_toolkit_<your_release_configuration>
59+
source setupvars.sh
60+
```
61+
62+
### (Optional) Build OpenVINO from Source
4663

4764
```bash
4865
git clone https://github.com/openvinotoolkit/openvino.git
49-
cd openvino && git checkout b16b776ac119dafda51f69a80f1e6b7376d02c3b
66+
cd openvino
5067
git submodule update --init --recursive
5168
sudo ./install_build_dependencies.sh
5269
mkdir build && cd build
@@ -59,44 +76,45 @@ cd <your_preferred_install_location>
5976
source setupvars.sh
6077
```
6178

62-
### Use OpenVINO from Release Packages
63-
64-
1. Download the OpenVINO release package from [here](https://docs.openvino.ai/2025/get-started/install-openvino.html). Make sure to select your configuration and click on **OpenVINO Archives** under the distribution section to download the appropriate archive for your platform.
65-
66-
2. Extract the release package from the archive and set the environment variables.
67-
68-
```bash
69-
tar -zxf openvino_toolkit_<your_release_configuration>.tgz
70-
cd openvino_toolkit_<your_release_configuration>
71-
source setupvars.sh
72-
```
73-
7479
For more information about OpenVINO build, refer to the [OpenVINO Build Instructions](https://github.com/openvinotoolkit/openvino/blob/master/docs/dev/build_linux.md).
7580

7681
### Setup
7782

7883
Follow the steps below to setup your build environment:
7984

80-
1. **Setup ExecuTorch Environment**: Refer to the [Environment Setup](https://pytorch.org/executorch/main/getting-started-setup#environment-setup) guide for detailed instructions on setting up the ExecuTorch environment.
8185

82-
2. **Setup OpenVINO Backend Environment**
83-
- Install the dependent libs. Ensure that you are inside `executorch/backends/openvino/` directory
86+
1. **Create a Virtual Environment**
87+
- Create a virtual environment and activate it by executing the commands below.
8488
```bash
85-
pip install -r requirements.txt
89+
python -m venv env
90+
source env/bin/activate
8691
```
87-
Note: To achieve optimal performance with NNCF quantization, you should install the latest development version of NNCF (version 2.16.0.dev0+191b53d9 or higher).
88-
3. Navigate to `scripts/` directory.
89-
90-
4. **Build OpenVINO Backend C++ Libraries and Executor Runner**: Once the prerequisites are in place, run the `openvino_build.sh` script to start the build process. By default, OpenVINO backend will be built under `cmake-out/backends/openvino/` as `libopenvino_backend.a`
91-
92+
2. **Clone ExecuTorch Repository from Github**
93+
- Clone Executorch repository by executing the command below.
9294
```bash
93-
./openvino_build.sh
95+
git clone --recurse-submodules https://github.com/pytorch/executorch.git
9496
```
95-
**Build OpenVINO Backend Python Package with Pybindings**: To build and install the OpenVINO backend Python package with Python bindings, run the `openvino_build.sh` script with the `--enable_python` argument. This will compile and install the ExecuTorch Python package with the OpenVINO backend into your Python environment. This option will also enable python bindings required to execute OpenVINO backend tests and `aot_optimize_and_infer.py` script inside `executorch/examples/openvino` folder.
96-
97+
3. **Build ExecuTorch with OpenVINO Backend**
98+
- Ensure that you are inside `executorch/backends/openvino/scripts` directory. The following command builds and installs ExecuTorch with the OpenVINO backend, also compiles the C++ runtime libraries and binaries into `<executorch_root>/cmake-out` for quick inference testing.
9799
```bash
100+
openvino_build.sh
101+
```
102+
- Optionally, `openvino_build.sh` script can be used to build python package or C++ libraries/binaries seperately.
103+
104+
**Build OpenVINO Backend Python Package with Pybindings**: To build and install the OpenVINO backend Python package with Python bindings, run the `openvino_build.sh` script with the `--enable_python` argument as shown in the below command. This will compile and install the ExecuTorch Python package with the OpenVINO backend into your Python environment. This option will also enable python bindings required to execute OpenVINO backend tests and `aot_optimize_and_infer.py` script inside `executorch/examples/openvino` folder.
105+
```bash
98106
./openvino_build.sh --enable_python
99107
```
108+
**Build C++ Runtime Libraries for OpenVINO Backend**: Run the `openvino_build.sh` script with the `--cpp_runtime` flag to build the C++ runtime libraries as shown in the below command. The compiled libraries files and binaries can be found in the `<executorch_root>/cmake-out` directory. The binary located at `<executorch_root>/cmake-out/executor_runner` can be used to run inference with vision models.
109+
```bash
110+
./openvino_build.sh --cpp_runtime
111+
```
112+
**Build C++ Llama Runner**: First, ensure the C++ runtime libraries are built by following the earlier instructions. Then, run the `openvino_build.sh` script with the `--llama_runner flag` to compile the LlaMA runner as shown the below command, which enables executing inference with models exported using export_llama. The resulting binary is located at: `<executorch_root>/cmake-out/examples/models/llama/llama_main`
113+
```bash
114+
./openvino_build.sh --llama_runner
115+
```
116+
117+
For more information about ExecuTorch environment setup, refer to the [Environment Setup](https://pytorch.org/executorch/main/getting-started-setup#environment-setup) guide.
100118

101119
### Run
102120

backends/openvino/partitioner.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
from torch.fx.passes.operator_support import OperatorSupportBase
2727

2828

29+
class PatternNode:
30+
op_types: dict[str, Optional[list]] = {}
31+
32+
def __init__(self):
33+
self.op_types = {}
34+
35+
2936
class OpenvinoOperatorsSupport(OperatorSupportBase):
3037
extended_support_dict = {
3138
"torch.ops.dim_order_ops._clone_dim_order.default": None,
@@ -36,6 +43,7 @@ def __init__(
3643
self,
3744
op_types_to_skip: Optional[set] = None,
3845
op_names_to_skip: Optional[set] = None,
46+
enabled_ops_by_name: Optional[set] = None,
3947
) -> None:
4048
"""
4149
Initializes the OpenvinoOperatorsSupport class.
@@ -47,9 +55,12 @@ def __init__(
4755
op_types_to_skip = set()
4856
if op_names_to_skip is None:
4957
op_names_to_skip = set()
58+
if enabled_ops_by_name is None:
59+
enabled_ops_by_name = set()
5060

5161
self._op_types_to_skip = op_types_to_skip
5262
self._op_names_to_skip = op_names_to_skip
63+
self._enabled_ops_by_name = enabled_ops_by_name
5364

5465
def is_node_supported(self, _, node: torch.fx.Node) -> bool:
5566
"""
@@ -66,6 +77,10 @@ def is_node_supported(self, _, node: torch.fx.Node) -> bool:
6677
op_type = node.target.__name__
6778
else:
6879
op_type = str(node.target)
80+
81+
if node.name in self._enabled_ops_by_name:
82+
return True
83+
6984
supported_ops = (
7085
OperatorSupport(options)._support_dict | self.extended_support_dict
7186
)
@@ -105,6 +120,7 @@ def __init__(
105120
self.delegation_spec = DelegationSpec(OpenvinoBackend.__name__, compile_spec)
106121
self._op_types_to_skip = op_types_to_skip
107122
self._op_names_to_skip = op_names_to_skip
123+
self._enabled_ops_by_name: set = set()
108124

109125
def ops_to_not_decompose(
110126
self,
@@ -123,19 +139,87 @@ def ops_to_not_decompose(
123139
torch.ops.aten.upsample_bilinear2d.vec,
124140
torch.ops.aten.upsample_nearest2d.default,
125141
torch.ops.aten.upsample_nearest2d.vec,
142+
torch.ops.aten.stack.default,
126143
]
127144
return (ops_not_decompose, None)
128145

146+
def check_pattern(
147+
self, node: torch.fx.Node, pattern: type[PatternNode], enabled_ops: list
148+
) -> bool:
149+
if node.op == "call_function":
150+
if ("call_function" + ":" + str(node.target.__name__)) in pattern.op_types: # type: ignore[union-attr]
151+
pt_input_nodes = node.all_input_nodes
152+
pattern_input_ops = pattern.op_types[
153+
"call_function" + ":" + str(node.target.__name__) # type: ignore[union-attr]
154+
]
155+
if pattern_input_ops is None:
156+
enabled_ops.append(node)
157+
return True
158+
if len(pt_input_nodes) != len(pattern_input_ops):
159+
return False
160+
for i in range(len(pt_input_nodes)):
161+
if not self.check_pattern(
162+
pt_input_nodes[i], pattern_input_ops[i], enabled_ops
163+
):
164+
return False
165+
enabled_ops.append(node)
166+
return True
167+
elif node.op == "get_attr":
168+
if "get_attr" in pattern.op_types:
169+
return True
170+
else:
171+
return False
172+
elif node.op == "placeholder":
173+
if "placeholder" in pattern.op_types:
174+
return True
175+
else:
176+
return False
177+
return False
178+
179+
def capture_nncf_patterns(self, graph_module: torch.fx.GraphModule):
180+
const_node = PatternNode
181+
const_node.op_types["get_attr"] = None
182+
const_node.op_types["placeholder"] = None
183+
bitwise_right_shift_node = PatternNode
184+
bitwise_right_shift_node.op_types[
185+
"call_function:aten.bitwise_right_shift.Tensor_Scalar"
186+
] = [const_node]
187+
bitwise_and_node = PatternNode
188+
bitwise_and_node.op_types["call_function:aten.bitwise_and.Scalar"] = [
189+
const_node
190+
]
191+
stack_node = PatternNode
192+
stack_node.op_types["call_function:aten.stack.default"] = [
193+
bitwise_and_node,
194+
bitwise_right_shift_node,
195+
]
196+
197+
for node in graph_module.graph.nodes:
198+
if (
199+
str(node.op) == "call_function"
200+
and str(node.target.__name__) == "aten.stack.default"
201+
):
202+
enabled_ops: list = []
203+
pattern_match = self.check_pattern(node, stack_node, enabled_ops)
204+
if pattern_match:
205+
for pattern_op in enabled_ops:
206+
self._enabled_ops_by_name.add(pattern_op.name)
207+
129208
def partition(self, exported_program: ExportedProgram) -> PartitionResult:
130209
"""
131210
Partitions an exported program into supported and unsupported segments.
132211
133212
:param exported_program: The exported program.
134213
:return: A PartitionResult containing the partitioned graph and delegation tags.
135214
"""
215+
self.capture_nncf_patterns(exported_program.graph_module)
136216
partitioner = CapabilityBasedPartitioner(
137217
exported_program.graph_module,
138-
OpenvinoOperatorsSupport(self._op_types_to_skip, self._op_names_to_skip),
218+
OpenvinoOperatorsSupport(
219+
self._op_types_to_skip,
220+
self._op_names_to_skip,
221+
self._enabled_ops_by_name,
222+
),
139223
allows_single_node_partition=True,
140224
)
141225
partition_list = partitioner.propose_partitions()
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from .quantizer import OpenVINOQuantizer, quantize_model
1+
from .quantizer import OpenVINOQuantizer, QuantizationMode, quantize_model
22

3-
__all__ = ["OpenVINOQuantizer", "quantize_model"]
3+
__all__ = ["OpenVINOQuantizer", "quantize_model", "QuantizationMode"]

0 commit comments

Comments
 (0)