Repository navigation
Build and ship the OpenVINO backend in the Windows wheels - #23997
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/23997
Note: Links to docs will display an error until the docs builds have been completed. ❌ You can merge normally! (5 Unrelated Failures), 1 Unclassified FailureAs of commit f23eaf3 with merge base e0692c7 ( UNCLASSIFIED FAILURE - DrCI could not classify the following job because the workflow did not run on the merge base. The failure may be pre-existing on trunk or introduced by this PR:
BROKEN TRUNK - The following jobs failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
@claude review this PR plz |
|
Claude finished @Gasoonjia's task in 3m 54s —— View job This PR builds and ships the OpenVINO delegate in the Windows wheel, and I found nothing blocking. The wheel build, the packaging, the pybind linking and the symbol check all look correctly wired for Windows. Three non-blocking points: the model-execution check never actually runs in CI, the Windows loader uses ANSI-only APIs, and its error messages show only a number. Detailed reviewPR Review: #23997SummaryThis PR adds a Windows branch to the OpenVINO delegate's runtime loader and turns the delegate on in the Windows wheel. The changes cover the build flags, the wheel packaging, the
The remaining findings are minor. Testing
Code Quality
RecommendationApprove None of these block the PR. The test gap is the most worthwhile fix, because without it CI cannot catch a Windows build where the delegate registers but fails at inference. If you leave the test as is, update the PR description so it doesn't claim end-to-end execution on Windows. |
8c11101 to
e5305bb
Compare
e5305bb to
b6f111f
Compare
The OpenVINO delegate lets ExecuTorch run models on Intel CPUs, GPUs and NPUs. Until now the prebuilt delegate shipped only in the Linux wheels. The Windows wheels left it out, even though Intel publishes an OpenVINO runtime for Windows x86-64, so Windows users had to build ExecuTorch from source. This change builds the delegate into the Windows wheels, CPU and CUDA, the same way the Linux wheels carry it. setup.py enables EXECUTORCH_BUILD_OPENVINO on Windows and packages the delegate's import library (.lib) beside the other Windows DLLs. The build file passes /EHsc and /GR when CMake reports an MSVC style compiler, which includes the clang-cl the wheels use, and -frtti and -fexceptions otherwise. The delegate loads the OpenVINO C library at run time, so the build needs no OpenVINO SDK. That loader used dlopen, with libopenvino_c.so as the default name. It is now split by platform. On Windows it reads OPENVINO_LIB_PATH with GetEnvironmentVariableW and loads with LoadLibraryExW, so a path outside the active code page still works, and it reports load errors as the system's message text. A bare name such as openvino_c.dll is searched with LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, which covers the program's own folder, System32 and folders added with AddDllDirectory, but not the current folder or PATH. So an openvino_c.dll planted in the current folder is not loaded. A path is made absolute and also searches its own folder, so the runtime's dependencies, openvino.dll and tbb12.dll, load from beside it. The Python package already finds the pip installed runtime and passes its full path; it now looks for openvino_c.dll on Windows. The delegate is a global, so on Windows its destructor keeps the runtime loaded rather than calling FreeLibrary inside DllMain. On Linux the load error now also names the path it tried. pip install "executorch[openvino]" now installs the runtime on Windows x86-64 too. The OpenVINO versions the extra allows publish no build for Python 3.15, or for Linux aarch64 on Python 3.14, so there the extra now installs nothing. Before, the command failed on those Linux setups. The docs say so. The Windows wheel smoke tests, CPU and CUDA, check that the delegate registers. They then install the openvino extra and the quantizer the partitioner needs. The extra is pinned to the installed version, so pip cannot swap the wheel under test. In a new process, they require that the delegate loads the runtime and that a model it runs matches eager. From a folder holding a copy of the runtime, they also check that the delegate loads the copy by its full path but not by its bare name. On Python 3.15 they only check that the extra installs without a runtime. The shared model check now asserts that the program is really delegated, and loads it from memory, because Windows does not delete a file the loaded program keeps open. The shared library check requires the delegate on Linux and Windows wheels, and finds it by its is_available member rather than its class name, because the reader of Windows exports matches decorated names, which spell a member and never a class alone. The docs, the README and the PyPI description list Windows. The backend README explains where Windows looks for the runtime's dependencies, and its Linux OPENVINO_LIB_PATH example now finds the versioned library the pip package ships. Test plan: - Built the loader for Windows with a MinGW cross compiler and ran it under Wine with the OpenVINO 2025.4.1 Windows DLLs. Full, forward slash, relative and non-ASCII paths load the runtime and find the CPU device, and so does a path set with SetEnvironmentVariableW after startup, or changed while it is read. A bare name does not load a copy planted in the current folder. Errors read as text with their code. Unloading the delegate's DLL leaves the runtime loaded. - Ran the bare name check against that build under Wine. It passes with the real DLLs. It fails when the copied runtime cannot load, and when the loader searches the current folder. - On Python 3.15 with pip, the extra installs without a runtime, and the check fails if the python_version bound is removed, also when an older release is available to pip. A resolver run shows the extra resolves on Linux aarch64 with Python 3.14 only with the new bound. - On Linux, built the delegate against the wheel's C++ SDK and ran the Windows OpenVINO check with OpenVINO 2025.4.1 and nncf 3.1.0. The runtime loads and the model matches eager exactly, and the check fails when the runtime cannot load. - Ran the repository's linters (black, usort, flake8, clang-format and others) on the changed files. - On native Windows, the CPU and CUDA wheel jobs (Python 3.11) run the new checks. The Python 3.15 branch first runs in the nightly build.
b6f111f to
f23eaf3
Compare
OpenVINO is Intel's runtime for fast inference on Intel CPUs, GPUs and NPUs. The ExecuTorch OpenVINO delegate ships only in the Linux wheels today. Intel publishes OpenVINO for Windows x64, but Windows users must build ExecuTorch from source to use the delegate.
What this change does
The Windows wheels, CPU and CUDA, now build and ship the delegate, like the Linux wheels.
pip install "executorch[openvino]"installs the runtime on Windows x64 too. On Python 3.15, and on Linux aarch64 with Python 3.14, the extra installs nothing, because the OpenVINO versions it allows have no build there. Before, it failed there on Linux.The delegate loads the OpenVINO C library at run time, so the build needs no OpenVINO SDK. On Windows it reads
OPENVINO_LIB_PATHwithGetEnvironmentVariableWand loads withLoadLibraryExW, using wide paths. A bare name is searched withLOAD_LIBRARY_SEARCH_DEFAULT_DIRS, which leaves out the current folder andPATH, so anopenvino_c.dllplanted in the current folder is not loaded. A path is made absolute and also searches its own folder, soopenvino.dllandtbb12.dllload from beside it. At unload, the delegate keeps the runtime loaded instead of callingFreeLibraryinsideDllMain.The Windows smoke tests check that the delegate registers, then install the openvino extra and nncf. In a new process, they check that the runtime loads, the model is really delegated, and its output matches eager. From a folder holding a copy of the runtime, they check that its full path loads and its bare name does not. On Python 3.15 they only check that the extra installs without a runtime. The model check loads the program from memory.
The docs and the PyPI description list Windows, note the Python limits, and explain the Windows search rules.
Testing
SetEnvironmentVariableWafter startup. The bare name check passes, and fails if the copy cannot load or the loader searches the current folder.