Con Windows ML, alcuni provider di esecuzione (EP) vengono scaricati, installati e condivisi in modo dinamico a livello di sistema tramite le API di Windows ML ExecutionProviderCatalog e vengono aggiornati automaticamente. Per informazioni sui provider di esecuzione disponibili, vedere Provider di esecuzione supportati.
Questa pagina illustra come installare gli EP su un dispositivo dell'utente. Dopo l'installazione, è necessario registrare i provider di esecuzione con ONNX Runtime prima di usarli.
Installare tutti gli EPS compatibili
Per lo sviluppo iniziale, risulta utile semplicemente chiamare EnsureAndRegisterCertifiedAsync(), che scaricherà e installerà tutti gli EP disponibili per il dispositivo dell'utente e quindi registra tutti gli EP con il runtime ONNX in una singola chiamata. Si noti che alla prima esecuzione questo metodo può richiedere più secondi o anche minuti a seconda della velocità di rete e degli EPS che devono essere scaricati.
// Get the default ExecutionProviderCatalog
var catalog = ExecutionProviderCatalog.GetDefault();
// Ensure execution providers compatible with device are present (downloads if necessary)
// and then registers all present execution providers with ONNX Runtime
await catalog.EnsureAndRegisterCertifiedAsync();
// Get the default ExecutionProviderCatalog
winrt::Microsoft::Windows::AI::MachineLearning::ExecutionProviderCatalog catalog =
winrt::Microsoft::Windows::AI::MachineLearning::ExecutionProviderCatalog::GetDefault();
// Ensure execution providers compatible with device are present (downloads if necessary)
// and then registers all present execution providers with ONNX Runtime
catalog.EnsureAndRegisterCertifiedAsync().get();
Le API C non supportano una singola EnsureAndRegisterCertifiedAsync() chiamata. Seguire invece le istruzioni per scaricare e registrare un EP specifico.
# Please DO NOT use this API. It won't register EPs to the python ort env.
Trova tutti gli EPs compatibili
È possibile visualizzare gli EPS (inclusi gli EPS non installati) disponibili per il dispositivo dell'utente chiamando il FindAllProviders() metodo .
ExecutionProviderCatalog catalog = ExecutionProviderCatalog.GetDefault();
// Find all available EPs (including non-installed EPs)
ExecutionProvider[] providers = catalog.FindAllProviders();
foreach (var provider in providers)
{
Console.WriteLine($"{provider.Name}: {provider.ReadyState}");
}
auto catalog = ExecutionProviderCatalog::GetDefault();
// Find all available EPs (including non-installed EPs)
auto providers = catalog.FindAllProviders();
for (auto const& provider : providers)
{
std::wcout << provider.Name() << L": " << static_cast<int>(provider.ReadyState()) << std::endl;
}
#include <WinMLEpCatalog.h>
// Context structure to track enumeration state
struct EnumContext
{
bool needsDownload;
};
// Callback to check if any providers need downloading
BOOL CALLBACK CheckDownloadNeededCallback(
WinMLEpHandle ep,
const WinMLEpInfo* info,
void* context)
{
if (info == nullptr) return TRUE; // Skip invalid entries
EnumContext* ctx = static_cast<EnumContext*>(context);
if (info->readyState == WinMLEpReadyState_NotPresent)
{
ctx->needsDownload = true;
return FALSE; // Stop enumeration early
}
return TRUE; // Continue enumeration
}
void DiscoverProviders()
{
WinMLEpCatalogHandle catalog = nullptr;
HRESULT hr = WinMLEpCatalogCreate(&catalog);
if (FAILED(hr)) return;
EnumContext ctx = { false };
// Check if any providers need to be downloaded
WinMLEpCatalogEnumProviders(catalog, CheckDownloadNeededCallback, &ctx);
if (ctx.needsDownload)
{
// There are new EPs available; decide how your app wants to handle that.
// See "Install a specific EP" below for download and registration.
}
WinMLEpCatalogRelease(catalog);
}
# winml: winui3.microsoft.windows.ai.machinelearning
catalog = winml.ExecutionProviderCatalog.get_default()
# Find all available EPs (including non-installed EPs)
providers = catalog.find_all_providers()
for provider in providers:
print(f"{provider.name}: {provider.ready_state}")
I provider di esecuzione restituiti variano in base al dispositivo dell'utente e ai provider di esecuzione disponibili. In un dispositivo Qualcomm compatibile senza provider di esecuzione attualmente installati, il codice precedente restituisce quanto segue...
QNNExecutionProvider: NotPresent
Ogni ExecutionProvider ha una proprietà ReadyState che indica lo stato corrente nel dispositivo. Comprendere questi stati consente di determinare quali azioni devono essere eseguite dall'app.
| ReadyState |
Definizione |
Passaggi successivi |
NotPresent |
L'EP non è installato nel dispositivo client. |
Chiamare EnsureReadyAsync() per scaricare e installare l'EP e aggiungerlo al grafico delle dipendenze di runtime dell'app. |
NotReady |
L'EP è installato nel dispositivo client, ma non è stato aggiunto al grafico delle dipendenze di runtime dell'app. |
Chiamare EnsureReadyAsync() per aggiungere l'EP al grafico delle dipendenze di runtime dell'app. |
Ready |
Il modulo EP viene installato nel dispositivo client ed è stato aggiunto al grafico delle dipendenze di runtime dell'applicazione. |
Chiamare TryRegister() per registrare l'EP con ONNX Runtime. |
Installare un EP specifico
Se è presente un executionProvider specifico che l'app vuole usare e il relativo ReadyState è NotPresent, è possibile scaricarlo e installarlo chiamando EnsureReadyAsync().
Si userà FindAllProviders() prima di tutto per ottenere tutti gli EPS compatibili e quindi è possibile chiamare EnsureReadyAsync() su un determinato ExecutionProvider per scaricare il provider di esecuzione specifico e chiamare TryRegister() per registrare il provider di esecuzione specifico.
// Download and install a NotPresent EP
var result = await provider.EnsureReadyAsync();
// Check that the download and install was successful
bool installed = result.Status == ExecutionProviderReadyResultState.Success;
Si userà FindAllProviders() prima di tutto per ottenere tutti gli EPS compatibili e quindi è possibile chiamare EnsureReadyAsync() su un determinato ExecutionProvider per scaricare il provider di esecuzione specifico e chiamare TryRegister() per registrare il provider di esecuzione specifico.
// Download and install a NotPresent EP
auto result = provider.EnsureReadyAsync().get();
// Check that the download and install was successful
bool installed = result.Status() == ExecutionProviderReadyResultState::Success;
Si utilizzerà il metodo WinMLEpCatalogFindProvider per richiedere un provider di esecuzione specifico. Successivamente, è possibile chiamare WinMLEpEnsureReady passando WinMLEpHandle per scaricare il provider di esecuzione specifico. Quindi, utilizzare WinMLEpGetLibraryPathSize e WinMLEpGetLibraryPath per ottenere il percorso del provider di esecuzione e registrarlo con ONNX Runtime utilizzando RegisterExecutionProviderLibrary.
#include <WinMLEpCatalog.h>
#include <onnxruntime_cxx_api.h>
#include <filesystem>
#include <string>
// Assumes an Ort::Env has already been created
// Ort::Env env(ORT_LOGGING_LEVEL_ERROR, "MyApp");
WinMLEpCatalogHandle catalog = nullptr;
HRESULT hr = WinMLEpCatalogCreate(&catalog);
if (FAILED(hr)) return;
// Find the QNN provider
WinMLEpHandle qnnProvider = nullptr;
hr = WinMLEpCatalogFindProvider(catalog, "QNN", nullptr, &qnnProvider);
if (SUCCEEDED(hr) && qnnProvider != nullptr)
{
// Ensure it's ready (download if necessary)
hr = WinMLEpEnsureReady(qnnProvider);
if (SUCCEEDED(hr))
{
// Get the library path for registration
size_t pathSize = 0;
WinMLEpGetLibraryPathSize(qnnProvider, &pathSize);
std::string libraryPathUtf8(pathSize, '\0');
WinMLEpGetLibraryPath(qnnProvider, pathSize, libraryPathUtf8.data(), nullptr);
// Register with ONNX Runtime
std::filesystem::path libraryPath(libraryPathUtf8);
env.RegisterExecutionProviderLibrary("QNN", libraryPath.wstring());
}
}
WinMLEpCatalogRelease(catalog);
Si userà find_all_providers() prima di tutto per ottenere tutti gli EPS compatibili e quindi è possibile chiamare ensure_ready_async() su un determinato ExecutionProvider per scaricare il provider di esecuzione specifico e usare ONNX Runtime per register_execution_provider_library registrare il provider di esecuzione specifico.
# Download and install a NotPresent EP
result = provider.ensure_ready_async().get()
# Check that the download and install was successful
installed = result.status == winml.ExecutionProviderReadyResultState.SUCCESS
Installazione con stato di avanzamento
Le API per scaricare e installare gli EP includono callback che forniscono aggiornamenti sullo stato del progresso, in modo da poter visualizzare indicatori di avanzamento per mantenere gli utenti informati.
// Start the download and install of a NotPresent EP
var operation = provider.EnsureReadyAsync();
// Listen to progress callback
operation.Progress = (asyncInfo, progressInfo) =>
{
// Dispatch to UI thread (varies based on UI platform)
_dispatcherQueue.TryEnqueue(() =>
{
// progressInfo is out of 100, convert to 0-1 range
double normalizedProgress = progressInfo / 100.0;
// Display the progress to the user
Progress = normalizedProgress;
};
};
// Await for the download and install to complete
var result = await operation;
// Check that the download and install was successful
bool installed = result.Status == ExecutionProviderReadyResultState.Success;
// Start the download and install of a NotPresent EP
auto operation = provider.EnsureReadyAsync();
// Listen to progress callback
operation.Progress([this](auto const& asyncInfo, double progressInfo)
{
// Dispatch to UI thread (varies based on UI platform)
dispatcherQueue.TryEnqueue([this, progressInfo]()
{
// progressInfo is out of 100, convert to 0-1 range
double normalizedProgress = progressInfo / 100.0;
// Display the progress to the user
Progress(normalizedProgress);
});
});
// Await for the download and install to complete
auto result = operation.get();
// Check that the download and install was successful
bool installed = result.Status() == ExecutionProviderReadyResultState::Success;
#include <WinMLEpCatalog.h>
#include <WinMLAsync.h>
#include <iostream>
#include <format>
// Progress callback — called periodically during download
void CALLBACK OnProgress(WinMLAsyncBlock* async, double progress)
{
// progress is out of 100, convert to 0-1 range
double normalizedProgress = progress / 100.0;
// Display the progress to the user
std::cout << std::format("Progress: {:.0f}%\n", normalizedProgress * 100);
}
// Completion callback — called when the download finishes
void CALLBACK OnComplete(WinMLAsyncBlock* async)
{
HRESULT hr = WinMLAsyncGetStatus(async, FALSE);
if (SUCCEEDED(hr))
{
std::cout << "Download complete!\n";
}
else
{
std::cout << std::format("Download failed: 0x{:08X}\n", static_cast<uint32_t>(hr));
}
}
// Start the async download with progress
WinMLAsyncBlock async = {};
async.callback = OnComplete;
async.progress = OnProgress;
HRESULT hr = WinMLEpEnsureReadyAsync(ep, &async);
if (SUCCEEDED(hr))
{
// Wait for the async operation to complete
WinMLAsyncGetStatus(&async, TRUE);
}
WinMLAsyncClose(&async);
# Start the download and install of a NotPresent EP
operation = provider.ensure_ready_async()
# Listen to progress callback
def on_progress(async_info, progress_info):
# progress_info is out of 100, convert to 0-1 range
normalized_progress = progress_info / 100.0
# Display the progress to the user
print(f"Progress: {normalized_progress:.0%}")
operation.progress = on_progress
# Await for the download and install to complete
result = operation.get()
# Check that the download and install was successful
installed = result.status == winml.ExecutionProviderReadyResultState.SUCCESS
Passaggi successivi
Dopo aver installato i provider di esecuzione, vedere Registrare i provider di esecuzione per informazioni su come registrarli per l'utilizzo con ONNX Runtime.
Esempio di app di produzione
Per le applicazioni di produzione, di seguito è riportato un esempio di ciò che l'app potrebbe voler fare per offrire a se stessi e agli utenti il controllo su quando si verificano i download. È possibile verificare se i nuovi provider di esecuzione sono disponibili e scaricarli in modo condizionale prima della registrazione:
using Microsoft.Windows.AI.MachineLearning;
var catalog = ExecutionProviderCatalog.GetDefault();
// Filter to the EPs our app supports/uses
var providers = catalog.FindAllProviders().Where(p =>
p.Name == "MIGraphXExecutionProvider" ||
p.Name == "VitisAIExecutionProvider" ||
p.Name == "OpenVINOExecutionProvider" ||
p.Name == "QNNExecutionProvider" ||
p.Name == "NvTensorRtRtxExecutionProvider"
);
if (providers.Any(p => p.ReadyState == ExecutionProviderReadyState.NotPresent))
{
// Show UI to user asking if they want to download new execution providers
bool userWantsToDownload = await ShowDownloadDialogAsync();
if (userWantsToDownload)
{
// Download all EPs
foreach (var p in providers)
{
if (p.ReadyState == ExecutionProviderReadyState.NotPresent)
{
// Ignore result handling here; production code could inspect status
await p.EnsureReadyAsync();
}
}
// And register all EPs
await catalog.RegisterCertifiedAsync();
}
else
{
// Register only already-present EPs
await catalog.RegisterCertifiedAsync();
}
}
using namespace winrt::Microsoft::Windows::AI::MachineLearning;
auto catalog = ExecutionProviderCatalog::GetDefault();
auto allProviders = catalog.FindAllProviders();
// Filter to the EPs our app supports/uses
std::vector<ExecutionProvider> targetProviders;
for (auto const& p : allProviders)
{
auto name = p.Name();
if (name == L"VitisAIExecutionProvider" ||
name == L"OpenVINOExecutionProvider" ||
name == L"QNNExecutionProvider" ||
name == L"NvTensorRtRtxExecutionProvider")
{
targetProviders.push_back(p);
}
}
bool needsDownload = false;
for (auto const& p : targetProviders)
{
if (p.ReadyState() == ExecutionProviderReadyState::NotPresent)
{
needsDownload = true;
break;
}
}
if (needsDownload)
{
// Show UI to user or check application settings to confirm download
bool userWantsToDownload = ShowDownloadDialog();
if (userWantsToDownload)
{
// Download only the missing target providers
for (auto const& p : targetProviders)
{
if (p.ReadyState() == ExecutionProviderReadyState::NotPresent)
{
// Ignore result handling here; production code could inspect status
p.EnsureReadyAsync().get();
}
}
// Register all (both previously present and newly downloaded) providers
catalog.RegisterCertifiedAsync().get();
}
else
{
// User deferred download; register only already-present providers
catalog.RegisterCertifiedAsync().get();
}
}
else
{
// All target EPs already present
catalog.RegisterCertifiedAsync().get();
}
#include <WinMLEpCatalog.h>
#include <onnxruntime_cxx_api.h>
#include <filesystem>
#include <string>
#include <cstring>
// List of provider names our app supports
const char* targetProviderNames[] = {
"VitisAIExecutionProvider",
"OpenVINOExecutionProvider",
"QNNExecutionProvider",
"NvTensorRtRtxExecutionProvider"
};
const size_t targetProviderCount = sizeof(targetProviderNames) / sizeof(targetProviderNames[0]);
bool IsTargetProvider(const char* name)
{
for (size_t i = 0; i < targetProviderCount; i++)
{
if (strcmp(name, targetProviderNames[i]) == 0)
return true;
}
return false;
}
// Context for enumeration callbacks
struct ProductionContext
{
bool needsDownload;
bool userWantsToDownload;
Ort::Env* env;
};
// Check if any target providers need downloading
BOOL CALLBACK CheckTargetProvidersCallback(
WinMLEpHandle ep,
const WinMLEpInfo* info,
void* context)
{
if (info == nullptr || info->name == nullptr) return TRUE; // Skip invalid entries
ProductionContext* ctx = static_cast<ProductionContext*>(context);
if (IsTargetProvider(info->name) && info->readyState == WinMLEpReadyState_NotPresent)
{
ctx->needsDownload = true;
}
return TRUE; // Continue to check all providers
}
// Download missing and register ready target providers
BOOL CALLBACK ProcessTargetProvidersCallback(
WinMLEpHandle ep,
const WinMLEpInfo* info,
void* context)
{
if (info == nullptr || info->name == nullptr) return TRUE; // Skip invalid entries
ProductionContext* ctx = static_cast<ProductionContext*>(context);
if (!IsTargetProvider(info->name))
return TRUE; // Skip non-target providers
// Download if user agreed and provider is not present
if (ctx->userWantsToDownload && info->readyState == WinMLEpReadyState_NotPresent)
{
WinMLEpEnsureReady(ep);
}
// Re-check state and register if ready
WinMLEpReadyState currentState;
WinMLEpGetReadyState(ep, ¤tState);
if (currentState == WinMLEpReadyState_Ready)
{
// Get the library path
size_t pathSize = 0;
WinMLEpGetLibraryPathSize(ep, &pathSize);
std::string libraryPathUtf8(pathSize, '\0');
WinMLEpGetLibraryPath(ep, pathSize, libraryPathUtf8.data(), nullptr);
// Register with ONNX Runtime
std::filesystem::path libraryPath(libraryPathUtf8);
ctx->env->RegisterExecutionProviderLibrary(info->name, libraryPath.wstring());
}
return TRUE;
}
void ProductionAppExample(Ort::Env& env, bool userWantsToDownload)
{
WinMLEpCatalogHandle catalog = nullptr;
HRESULT hr = WinMLEpCatalogCreate(&catalog);
if (FAILED(hr)) return;
ProductionContext ctx = { false, userWantsToDownload, &env };
// First pass: check if any target providers need downloading
WinMLEpCatalogEnumProviders(catalog, CheckTargetProvidersCallback, &ctx);
if (ctx.needsDownload && !userWantsToDownload)
{
// TODO: Show UI to user asking if they want to download
// ctx.userWantsToDownload = ShowDownloadDialog();
}
// Second pass: download (if requested) and register target providers
WinMLEpCatalogEnumProviders(catalog, ProcessTargetProvidersCallback, &ctx);
WinMLEpCatalogRelease(catalog);
}
# remove the msvcp140.dll from the winrt-runtime package.
# So it does not cause issues with other libraries.
from pathlib import Path
from importlib import metadata
site_packages_path = Path(str(metadata.distribution('winrt-runtime').locate_file('')))
dll_path = site_packages_path / 'winrt' / 'msvcp140.dll'
if dll_path.exists():
dll_path.unlink()
from winui3.microsoft.windows.applicationmodel.dynamicdependency.bootstrap import (
InitializeOptions,
initialize
)
import winui3.microsoft.windows.ai.machinelearning as winml
import onnxruntime as ort
with initialize(options=InitializeOptions.ON_NO_MATCH_SHOW_UI):
catalog = winml.ExecutionProviderCatalog.get_default()
# Filter EPs that the app supports
providers = [provider for provider in catalog.find_all_providers() if provider.name in [
'VitisAIExecutionProvider',
'OpenVINOExecutionProvider',
'QNNExecutionProvider',
'NvTensorRtRtxExecutionProvider'
]]
# Download and make ready missing EPs if the user wants to
if any(provider.ready_state == winml.ExecutionProviderReadyState.NOT_PRESENT for provider in providers):
# Ask the user if they want to download the missing packages
if user_wants_to_download:
for provider in [provider for provider in providers if provider.ready_state == winml.ExecutionProviderReadyState.NOT_PRESENT]:
provider.ensure_ready_async().get()
# Make ready the existing EPs
for provider in [provider for provider in providers if provider.ready_state == winml.ExecutionProviderReadyState.NOT_READY]:
provider.ensure_ready_async().get()
# Register all ready EPs
for provider in [provider for provider in providers if provider.ready_state == winml.ExecutionProviderReadyState.READY]:
ort.register_execution_provider_library(provider.name, provider.library_path)
Vedere anche