Skip to content

Commit 48541fc

Browse files
shoumikhinfacebook-github-bot
authored andcommitted
Add OpenVINO backend to pip wheel for Linux x86_64 (#18309)
Summary: Rewrite the OpenVINO backend runtime to use dlopen/dlsym with the OpenVINO C API, eliminating the build-time dependency on the OpenVINO SDK. This follows the QNN backend's proven pattern: the backend links statically into _portable_lib.so with zero external library dependencies and loads libopenvino_c.so at runtime via dlopen when the user has the openvino pip package installed. Key design decisions: - Use OpenVINO C API (openvino/c/openvino.h) instead of C++ API to enable dlopen with dlsym function pointers. The C++ class-based API (ov::Core, ov::InferRequest) cannot be resolved via dlsym. - Forward-declare OpenVINO C types in OpenvinoApi.h instead of including headers, so there is no build-time SDK dependency at all. - Thread-safe lazy loading via std::call_once in ensure_loaded(). - OPENVINO_LIB_PATH env var for explicit library path override. - Backend always registers via static initializer (zero cost); actual OpenVINO loading deferred to first is_available()/init() call. - No changes to portable_lib.py needed — no RTLD_GLOBAL, no separate .so, no auditwheel workarounds. The backend is statically linked into _portable_lib.so like XNNPACK and has no NEEDED entries for any OpenVINO library. - Enable build on Linux x86_64 unconditionally in setup.py since there is no build-time dependency. User experience: pip install executorch # XNNPACK+QNN+OpenVINO registered pip install executorch[openvino] # + openvino runtime, backend activates Differential Revision: D97202714
1 parent 569cf41 commit 48541fc

File tree

12 files changed

+506
-129
lines changed

12 files changed

+506
-129
lines changed

.ci/scripts/wheel/pre_build_script.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,10 @@ if [[ "$(uname -s)" == "Linux" && "$(uname -m)" == "x86_64" ]]; then
6262
echo "QNN_SDK_ROOT=${QNN_SDK_ROOT}" >> "${GITHUB_ENV}"
6363
echo "QNN SDK downloaded to ${QNN_SDK_ROOT}"
6464
fi
65+
66+
# Install OpenVINO Python package on Linux x86_64 for wheel testing.
67+
# The backend itself has no build-time dependency (uses dlopen at runtime).
68+
if [[ "$(uname -s)" == "Linux" && "$(uname -m)" == "x86_64" ]]; then
69+
echo "Installing OpenVINO runtime for testing..."
70+
pip install "openvino>=2025.1.0,<2026.0.0"
71+
fi

.ci/scripts/wheel/test_linux.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
), f"QnnBackend not found in registered backends: {registered}"
2525
print("✓ QnnBackend is registered")
2626

27+
assert (
28+
"OpenvinoBackend" in registered
29+
), f"OpenvinoBackend not found in registered backends: {registered}"
30+
print("✓ OpenvinoBackend is registered")
31+
2732
test_base.run_tests(
2833
model_tests=[
2934
test_base.ModelTest(

README-wheel.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ to run ExecuTorch `.pte` files, with some restrictions:
1414
* Only the [XNNPACK backend delegate](docs/source/backends/xnnpack/xnnpack-overview.md) is linked into the prebuilt module.
1515
* \[macOS only] [Core ML](docs/source/backends/coreml/coreml-overview.md) and [MPS](docs/source/backends/mps/mps-overview.md) backend
1616
are also linked into the prebuilt module.
17+
* \[Linux x86_64] [QNN](docs/source/backends-qualcomm.md) and
18+
[OpenVINO](docs/source/build-run-openvino.md) backends are also linked into the
19+
prebuilt module. OpenVINO requires the runtime to be installed separately:
20+
`pip install executorch[openvino]`
1721

1822
Please visit the [ExecuTorch website](https://pytorch.org/executorch) for
1923
tutorials and documentation. Here are some starting points:

backends/openvino/CMakeLists.txt

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,32 @@ set(COMMON_INCLUDE_DIRS ${EXECUTORCH_ROOT}/..)
2828
# Include utility CMake scripts from ExecuteTorch
2929
include(${EXECUTORCH_ROOT}/tools/cmake/Utils.cmake)
3030

31-
# Find OpenVINO libraries
32-
find_package(OpenVINO REQUIRED)
31+
# The backend resolves OpenVINO C API symbols via dlopen/dlsym at runtime, so
32+
# there is no build-time dependency on the OpenVINO SDK.
3333

3434
# Define OpenVINO backend as a static library
35-
add_library(openvino_backend STATIC .)
35+
add_library(openvino_backend STATIC)
3636

3737
# Enable exceptions and RTTI for OpenVINO backend
3838
target_compile_options(openvino_backend PRIVATE -frtti -fexceptions)
3939

40-
# Include Executorch directories
41-
target_include_directories(openvino_backend PRIVATE ${COMMON_INCLUDE_DIRS})
42-
43-
# Link OpenVINO and ExecuteTorch core libraries
44-
target_link_libraries(
45-
openvino_backend PRIVATE openvino::runtime executorch_core
46-
)
47-
4840
# Add source files for OpenVINO backend
4941
target_sources(
5042
openvino_backend
5143
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/runtime/OpenvinoBackend.cpp
5244
)
5345

46+
# Include Executorch directories
47+
target_include_directories(openvino_backend PRIVATE ${COMMON_INCLUDE_DIRS})
48+
49+
# Link ExecuteTorch core and dynamic loading libraries
50+
target_link_libraries(openvino_backend PRIVATE executorch_core ${CMAKE_DL_LIBS})
51+
5452
executorch_target_link_options_shared_lib(openvino_backend)
5553

5654
# Install OpenVINO backend library to the lib directory
5755
install(
5856
TARGETS openvino_backend
5957
EXPORT ExecuTorchTargets
6058
DESTINATION ${CMAKE_INSTALL_LIBDIR}
61-
INCLUDES
62-
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
6359
)

backends/openvino/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,36 @@ OpenVINO backend supports the following hardware:
1212

1313
For more information on the supported hardware, please refer to [OpenVINO System Requirements](https://docs.openvino.ai/2025/about-openvino/release-notes-openvino/system-requirements.html) page.
1414

15+
## Quick Start (pip wheel)
16+
17+
On Linux x86_64, the OpenVINO backend is included in the ExecuTorch pip wheel. Install the OpenVINO runtime to activate it:
18+
19+
```bash
20+
pip install executorch[openvino]
21+
```
22+
23+
Set the library path so the backend can find the OpenVINO runtime:
24+
25+
```bash
26+
export LD_LIBRARY_PATH="$(python3 -c "import openvino, os; print(os.path.join(os.path.dirname(openvino.__file__), 'libs'))")${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
27+
```
28+
29+
Or point to the library directly:
30+
31+
```bash
32+
export OPENVINO_LIB_PATH=$(python3 -c "import openvino, os; print(os.path.join(os.path.dirname(openvino.__file__), 'libs', 'libopenvino_c.so'))")
33+
```
34+
35+
Verify the backend is available:
36+
37+
```python
38+
from executorch.extension.pybindings.portable_lib import (
39+
_get_registered_backend_names,
40+
)
41+
print(_get_registered_backend_names())
42+
# Should include 'OpenvinoBackend'
43+
```
44+
1545
## Directory Structure
1646

1747
```
@@ -24,6 +54,7 @@ executorch
2454
│ ├── __init__.py
2555
│ └── quantizer.py
2656
│ ├── runtime
57+
│ ├── OpenvinoApi.h
2758
│ ├── OpenvinoBackend.cpp
2859
│ └── OpenvinoBackend.h
2960
│ ├── scripts
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright (c) Intel Corporation
3+
*
4+
* Licensed under the BSD License (the "License"); you may not use this file
5+
* except in compliance with the License. See the license file found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <dlfcn.h>
12+
#include <cstddef>
13+
#include <cstdint>
14+
#include <memory>
15+
16+
namespace executorch {
17+
namespace backends {
18+
namespace openvino {
19+
20+
// Forward declarations matching the OpenVINO C API opaque types.
21+
// Only pointer types are used so struct layout is irrelevant.
22+
typedef struct ov_core ov_core_t;
23+
typedef struct ov_compiled_model ov_compiled_model_t;
24+
typedef struct ov_infer_request ov_infer_request_t;
25+
typedef struct ov_tensor ov_tensor_t;
26+
27+
// Value types reproduced from openvino/c/ov_shape.h and ov_common.h.
28+
// These are stable C ABI — pinned via version constraint in pyproject.toml.
29+
typedef struct {
30+
int64_t rank;
31+
int64_t* dims;
32+
} ov_shape_t;
33+
34+
typedef struct {
35+
char** devices;
36+
size_t size;
37+
} ov_available_devices_t;
38+
39+
// Intentionally partial — only OV_STATUS_OK is needed for success checks.
40+
// The full enum is defined in openvino/c/ov_common.h.
41+
typedef enum {
42+
OV_STATUS_OK = 0,
43+
OV_STATUS_GENERAL_ERROR = -1,
44+
} ov_status_e;
45+
46+
// Values aligned with ov::element::Type_t (sequential enum).
47+
typedef enum {
48+
OV_ELEMENT_UNDEFINED = 0,
49+
OV_ELEMENT_BOOLEAN = 1,
50+
OV_ELEMENT_BF16 = 2,
51+
OV_ELEMENT_F16 = 3,
52+
OV_ELEMENT_F32 = 4,
53+
OV_ELEMENT_F64 = 5,
54+
OV_ELEMENT_I4 = 6,
55+
OV_ELEMENT_I8 = 7,
56+
OV_ELEMENT_I16 = 8,
57+
OV_ELEMENT_I32 = 9,
58+
OV_ELEMENT_I64 = 10,
59+
OV_ELEMENT_U1 = 11,
60+
OV_ELEMENT_U2 = 12,
61+
OV_ELEMENT_U3 = 13,
62+
OV_ELEMENT_U4 = 14,
63+
OV_ELEMENT_U6 = 15,
64+
OV_ELEMENT_U8 = 16,
65+
} ov_element_type_e;
66+
67+
// Function pointer types for each OpenVINO C API function we use.
68+
using ov_core_create_fn = ov_status_e (*)(ov_core_t**);
69+
using ov_core_free_fn = void (*)(ov_core_t*);
70+
using ov_core_get_available_devices_fn =
71+
ov_status_e (*)(const ov_core_t*, ov_available_devices_t*);
72+
using ov_available_devices_free_fn = void (*)(ov_available_devices_t*);
73+
using ov_core_import_model_fn = ov_status_e (*)(
74+
const ov_core_t*,
75+
const char*,
76+
size_t,
77+
const char*,
78+
ov_compiled_model_t**);
79+
using ov_compiled_model_create_infer_request_fn =
80+
ov_status_e (*)(const ov_compiled_model_t*, ov_infer_request_t**);
81+
using ov_compiled_model_inputs_size_fn =
82+
ov_status_e (*)(const ov_compiled_model_t*, size_t*);
83+
using ov_compiled_model_outputs_size_fn =
84+
ov_status_e (*)(const ov_compiled_model_t*, size_t*);
85+
using ov_compiled_model_free_fn = void (*)(ov_compiled_model_t*);
86+
using ov_infer_request_set_input_tensor_by_index_fn =
87+
ov_status_e (*)(ov_infer_request_t*, size_t, const ov_tensor_t*);
88+
using ov_infer_request_set_output_tensor_by_index_fn =
89+
ov_status_e (*)(ov_infer_request_t*, size_t, const ov_tensor_t*);
90+
using ov_infer_request_infer_fn = ov_status_e (*)(ov_infer_request_t*);
91+
using ov_infer_request_free_fn = void (*)(ov_infer_request_t*);
92+
using ov_tensor_create_from_host_ptr_fn =
93+
ov_status_e (*)(ov_element_type_e, ov_shape_t, void*, ov_tensor_t**);
94+
using ov_tensor_free_fn = void (*)(ov_tensor_t*);
95+
using ov_shape_create_fn =
96+
ov_status_e (*)(int64_t, const int64_t*, ov_shape_t*);
97+
using ov_shape_free_fn = ov_status_e (*)(ov_shape_t*);
98+
99+
struct DlCloser {
100+
void operator()(void* handle) {
101+
if (handle) {
102+
dlclose(handle);
103+
}
104+
}
105+
};
106+
using DlHandle = std::unique_ptr<void, DlCloser>;
107+
108+
struct OpenvinoFunctions {
109+
ov_core_create_fn core_create = nullptr;
110+
ov_core_free_fn core_free = nullptr;
111+
ov_core_get_available_devices_fn core_get_available_devices = nullptr;
112+
ov_available_devices_free_fn available_devices_free = nullptr;
113+
ov_core_import_model_fn core_import_model = nullptr;
114+
ov_compiled_model_create_infer_request_fn
115+
compiled_model_create_infer_request = nullptr;
116+
ov_compiled_model_inputs_size_fn compiled_model_inputs_size = nullptr;
117+
ov_compiled_model_outputs_size_fn compiled_model_outputs_size = nullptr;
118+
ov_compiled_model_free_fn compiled_model_free = nullptr;
119+
ov_infer_request_set_input_tensor_by_index_fn
120+
infer_request_set_input_tensor_by_index = nullptr;
121+
ov_infer_request_set_output_tensor_by_index_fn
122+
infer_request_set_output_tensor_by_index = nullptr;
123+
ov_infer_request_infer_fn infer_request_infer = nullptr;
124+
ov_infer_request_free_fn infer_request_free = nullptr;
125+
ov_tensor_create_from_host_ptr_fn tensor_create_from_host_ptr = nullptr;
126+
ov_tensor_free_fn tensor_free = nullptr;
127+
ov_shape_create_fn shape_create = nullptr;
128+
ov_shape_free_fn shape_free = nullptr;
129+
};
130+
131+
} // namespace openvino
132+
} // namespace backends
133+
} // namespace executorch

0 commit comments

Comments
 (0)