You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[AUTO] Implement device selection before each inference - #38141
This pull request introduces a new "dynamic device selection" mode to the AUTO plugin, allowing per-inference device selection based on resource-aware properties. The changes add infrastructure to serialize inference requests, queue and dispatch them as devices are re-selected, and ensure models are compiled on-demand for target devices. The implementation also handles device failures and ensures proper resource cleanup.
Details:
Added the m_dynamic_device_selection flag to ScheduleContext, enabling per-inference device selection when resource-aware properties are set.
In AutoSchedule, added members and logic to serialize inference requests, queue them if a device is busy, and re-select or recompile models for each inference as needed.
Modified schedule_to_worker_infer_request to use dynamic scheduling when enabled, ensuring requests are queued and dispatched properly.
In generate_workers, limited the number of worker requests per device to one in dynamic mode to guarantee serialized execution and safe device switching.
Ensured that models are kept alive for on-demand compilation, and added detailed logging for device selection, compilation, and device switching events to aid debugging and profiling.
Adds scheduling exception and execution-slot interfaces.
src/plugins/auto/src/schedule.cpp
Critical (1 vote): retry handling can release the execution gate too early, allowing concurrent execution.
src/plugins/auto/src/plugin.cpp
Critical (3 votes): dynamic switching can produce incorrect stateful-model behavior.
src/plugins/auto/src/common.hpp
Adds dynamic-selection context state.
src/plugins/auto/src/auto_schedule.hpp
Declares dynamic scheduler state and helpers.
src/plugins/auto/src/auto_schedule.cpp
Moderate (2 votes): fallback registrations may remain in the process-wide priority map. Moderate (1 vote): failed devices retain compiled models and worker requests. Nit (1 vote): concurrent queueing is not exercised. Moderate (1 vote): cache publication before worker creation can prevent recovery. Moderate (1 vote): CPU fallback reuse can skip required compilation.
Review details
Suppressed comments (4)
src/plugins/auto/src/auto_schedule.cpp:30
[HIGH] Removing a failed device from m_device_priorities leaves its dynamic compiled model and worker request resident: m_dynamic_compiled_models owns the SoCompiledModel, while m_worker_requests owns the device infer request. Every runtime-failed device therefore keeps its device resources until the whole AUTO compiled model is destroyed, even though it can no longer be selected. Defer safe removal until the current callback has released the worker, then reclaim these entries.
m_context->m_device_priorities.erase(iter);
LOG_WARNING_TAG("[dynamic] inference failed on device:%s, exclude it and retry on another device",
src/plugins/auto/src/auto_schedule.cpp:547
[HIGH] The new queue path is not exercised by the tests: every helper call to infer() waits for completion before starting the next request, so m_gate_pending_tasks is never populated. A regression in concurrent queuing or release_execution_slot() would therefore pass this suite; add a test that starts multiple async requests concurrently and waits for all of them.
m_gate_pending_tasks.emplace_back(std::move(pipeline_task), preferred_device);
LOG_DEBUG_TAG("[dynamic] an inference is still running, request queued, queue size:%ld",
static_cast<long>(m_gate_pending_tasks.size()));
src/plugins/auto/src/auto_schedule.cpp:652
[HIGH] The cache entry is published before generate_workers() succeeds. If creating the first worker throws, dispatch reports the error but leaves this device marked as compiled; later requests skip compilation and call run_pipeline_task() with no idle worker, so the device can never recover. Publish the compiled model only after worker creation succeeds, or erase the entry on the failure path.
[HIGH] The new on-demand compile path can fail to use an available CPU fallback. Dynamic mode explicitly disables m_startup_fallback, leaving m_compile_context[CPU].m_is_enabled false; try_to_compile_model() still performs its CPU-config equality shortcut without checking that the CPU context is enabled/already compiled. If the selected accelerator fails and the candidate CPU has the same (for example, empty) config as that default context, the helper returns before compiling CPU and ensure_device_ready() reports a scheduling error. Guard that reuse shortcut with the CPU context state or provide a dedicated on-demand fallback path.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unresolved critical, blocker, and moderate findings affect device safety, fallback behavior, caching, cleanup, and stateful models.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
src/plugins/auto/src/auto_schedule.cpp:640
[HIGH] The on-demand compile attempt uses a private copy of m_context->m_device_priorities; when a target such as GPU.1 fails compilation, try_to_compile_model removes it only from that temporary list and can fall back to CPU, but the next inference can select GPU.1 again and pay the full compile/fallback cost repeatedly. Record permanently failed compile candidates in the dynamic scheduler's shared selection state (or cache them as unavailable) so device-failure handling does not become per-inference recompilation.
src/plugins/auto/src/auto_schedule.cpp:648
[HIGH] The cleanup below is reached only when context.m_is_load_success is true. If the selected device and every fallback fail to compile, try_to_compile_model can leave its last select_device registration in the process-wide priority map, and this return keeps that reservation alive for the scheduler lifetime; lower-priority AUTO instances can then be prevented from selecting the device. Release every on-demand registration on failure as well, preferably by tracking registration ownership with an RAII guard.
if (!context.m_is_load_success) {
LOG_WARNING_TAG("[dynamic] compiling the model on device:%s failed, %s",
device.device_name.c_str(),
context.m_err_message.c_str());
return false;
src/plugins/auto/src/auto_schedule.cpp:115
[HIGH] This does not retain a model for path-based compilation. In compile_model_impl, m_context->m_model is null when m_model_path is used, so this assignment leaves m_dynamic_model empty and ensure_device_ready() later calls compile_model(m_context->m_model_path, ...) after the original compile has returned. If the caller removes or loses access to the file, the first device switch fails even though the model was compiled successfully; materialize/retain the model for dynamic mode or disable dynamic selection for path-based compilation.
// the model has to stay alive to be compiled on demand once the target device changes
m_dynamic_model = m_context->m_model;
m_dynamic_executor = m_plugin->get_executor_manager()->get_idle_cpu_streams_executor(
ov::threading::IStreamsExecutor::Config{"AutoDynamicSchedule", 1, 0});
Exclude failed dynamic devices from subsequent selection
src/plugins/auto/src/auto_schedule.cpp:655
[HIGH] A compile failure on a dynamically selected device is only removed from the local context.m_meta_devices; m_context->m_device_priorities still contains it. If resource selection picks that device again, this path recompiles the already cached fallback and calls generate_workers again for the same device, leaving duplicate/stale entries in its idle-worker queue and repeating the compile on every such inference. Keep failed compile targets out of subsequent dynamic selection, or reuse an existing cached fallback without regenerating its workers.
[HIGH] When a newly selected device fails compilation, try_to_compile_model() can switch context.m_device_info to a device already present in m_dynamic_compiled_models (for example, a later GPU selection falling back to the initial CPU). This path does not consult that cache: for an initial CPU, the legacy m_compile_context[CPU] is disabled, so the fallback is either reported as unsuccessful or generate_workers() is called again on the existing one-entry idle queue, causing the request to fail instead of reusing the cached worker. After fallback, check the dynamic cache and dispatch its existing worker/model without recompiling or regenerating workers.
[HIGH] This helper calls infer() serially, so no test ever enters schedule_dynamic_task() while m_gate_busy is true. The new queue and release_execution_slot() handoff are the core of concurrent request support; regressions that drop or mis-handle queued requests will pass all of these tests. Add a concurrent start_async() test with a blocked worker and verify that every queued request completes.
Allow fallback selection for out-of-list low-power targets
src/plugins/auto/src/auto_schedule.cpp:59
[HIGH] With THROUGHPUT, priorities GPU.0,CPU, and LOW_POWER_DEVICE=GPU.1, initial selection can choose GPU.1 while dynamic scheduling is disabled. If GPU.1 inference fails, the candidate lookup misses it and returns false without trying the configured runtime fallback devices. Allow fallback selection for this out-of-list target while preserving the protection against repeated callbacks starting another fallback.
Reuse cached fallback models before recompiling
src/plugins/auto/src/auto_schedule.cpp:453
[MEDIUM] If a new target fails compilation and fallback selects an already-cached device, try_to_compile_model() recompiles that device before ensure_device_ready() checks the cache again. This adds compilation cost and can fail the inference despite an available cached model and worker. Reuse the cached model here before compiling again, while preserving the registration cleanup.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Exhausting ordinary candidates blocks subsequent inference even when a healthy low-power device becomes eligible again.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Allow low-power fallback when ordinary candidates are exhausted
src/plugins/auto/src/auto_schedule.cpp:689
[HIGH] With priorities GPU.0 and LOW_POWER_DEVICE=CPU, compiling in low-power mode and then failing GPU.0's first compilation outside that mode removes the sole ordinary candidate. This assertion then rejects every unpinned inference, even after low-power mode resumes with a healthy cached CPU model. Allow the available low-power target to be selected when ordinary candidates are exhausted, without calling select_device() with an empty list, and add a regression test for this transition.
The reason will be displayed to describe this comment to others. Learn more.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Cross-thread scheduling, fallback, and teardown require targeted runtime validation and maintainer review, with a compilation-exclusion issue still unresolved.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
A critical use-after-free remains possible when a failed dispatch completes the request before worker cleanup finishes, requiring scheduler lifetime protection for both dispatch submission paths.
Handle first failure for out-of-list low-power fallback target
src/plugins/auto/src/auto_schedule.cpp:59
With THROUGHPUT, priorities GPU.0,CPU, LOW_POWER_DEVICE=GPU.1, and startup fallback disabled, GPU.1 can be selected initially but its inference failure never tries a fallback despite runtime fallback being enabled. Because GPU.1 is absent from m_device_priorities, the lookup below treats its first failure as an already-handled failure and returns false. Handle the first failure of an out-of-list low-power target as a fallback transition, while preserving the duplicate-failure guard for subsequent worker callbacks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces a new "dynamic device selection" mode to the AUTO plugin, allowing per-inference device selection based on resource-aware properties. The changes add infrastructure to serialize inference requests, queue and dispatch them as devices are re-selected, and ensure models are compiled on-demand for target devices. The implementation also handles device failures and ensures proper resource cleanup.
Details:
m_dynamic_device_selectionflag toScheduleContext, enabling per-inference device selection when resource-aware properties are set.AutoSchedule, added members and logic to serialize inference requests, queue them if a device is busy, and re-select or recompile models for each inference as needed.schedule_to_worker_infer_requestto use dynamic scheduling when enabled, ensuring requests are queued and dispatched properly.generate_workers, limited the number of worker requests per device to one in dynamic mode to guarantee serialized execution and safe device switching.Tickets:
AI Assistance: