From 049f0268969e561a0165eaed459d16a47c46194f Mon Sep 17 00:00:00 2001 From: Yue Sun Date: Fri, 31 Jul 2026 15:11:25 +0800 Subject: [PATCH] fix(perf): avoid native stderr redirect for VitisAI --- src/winml/modelkit/commands/perf.py | 12 +++++++-- tests/unit/commands/test_perf_cli.py | 38 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/winml/modelkit/commands/perf.py b/src/winml/modelkit/commands/perf.py index b23a13842..917b9273b 100644 --- a/src/winml/modelkit/commands/perf.py +++ b/src/winml/modelkit/commands/perf.py @@ -1051,8 +1051,16 @@ def _load_model(self) -> None: ), } + # VitisAI's compiler can hang during InferenceSession creation when + # native stderr points to a pipe, even when that pipe is drained + # concurrently. Keep its native handle untouched; other EPs retain the + # default warning filtering. + filter_native_warnings = ( + self._ep_device.device.ep_name != "VitisAIExecutionProvider" + ) + if is_onnx: - with suppress_native_warnings(enabled=True): + with suppress_native_warnings(enabled=filter_native_warnings): self._model = WinMLAutoModel.from_onnx( onnx_path=model_path, skip_build=self.config.skip_build, @@ -1060,7 +1068,7 @@ def _load_model(self) -> None: **common_kwargs, ) else: - with suppress_native_warnings(enabled=True): + with suppress_native_warnings(enabled=filter_native_warnings): self._model = WinMLAutoModel.from_pretrained( model_id, **common_kwargs, diff --git a/tests/unit/commands/test_perf_cli.py b/tests/unit/commands/test_perf_cli.py index ace26947d..62eed01ef 100644 --- a/tests/unit/commands/test_perf_cli.py +++ b/tests/unit/commands/test_perf_cli.py @@ -512,6 +512,44 @@ def resolve_ep_device(self: PerfBenchmark) -> None: assert all(import_observed) assert load_observed == [True] + def test_load_model_does_not_redirect_native_stderr_for_vitisai( + self, monkeypatch: pytest.MonkeyPatch + ) -> None: + """VitisAI session creation keeps the process native stderr handle.""" + suppression_enabled: list[bool] = [] + + @contextmanager + def record_native_suppression(*_args: object, **kwargs: object): + suppression_enabled.append(bool(kwargs.get("enabled"))) + yield + + class FakeWinMLAutoModel: + @staticmethod + def from_pretrained(*args: object, **kwargs: object) -> MagicMock: + return MagicMock() + + def resolve_ep_device(self: PerfBenchmark) -> None: + self._ep_device = MagicMock() + self._ep_device.device.ep_name = "VitisAIExecutionProvider" + self._resolved_device = "npu" + self._resolved_ep = "VitisAIExecutionProvider" + + fake_models_pkg = ModuleType("winml.modelkit.models") + fake_models_pkg.WinMLAutoModel = FakeWinMLAutoModel + monkeypatch.setitem(sys.modules, "winml.modelkit.models", fake_models_pkg) + monkeypatch.setattr( + "winml.modelkit.commands.perf.suppress_native_warnings", + record_native_suppression, + ) + monkeypatch.setattr(PerfBenchmark, "_resolve_device_ep", resolve_ep_device) + + benchmark = PerfBenchmark( + BenchmarkConfig(model_id="microsoft/resnet-50", task="image-classification") + ) + benchmark._load_model() + + assert suppression_enabled == [True, False] + def test_resolve_device_ep_filters_native_warnings_and_preserves_errors( self, monkeypatch: pytest.MonkeyPatch,