При использовании Windows ML некоторые поставщики выполнения динамически загружаются, устанавливаются и распределяются по всей системе через Windows ML ExecutionProviderCatalog API, и обновляются автоматически. Сведения о доступных провайдерах выполнения см. в разделе Поддерживаемые провайдеры выполнения.
На этой странице описывается установка EPS на устройство пользователя. После установки необходимо зарегистрировать поставщиков выполнения в onNX Runtime перед их использованием.
Установка всех совместимых EPs
Для первоначальной разработки можно просто вызвать EnsureAndRegisterCertifiedAsync(), который загрузит и установит все доступные на устройстве пользователя модули EP, а затем зарегистрирует все модули EP в среде выполнения ONNX одним вызовом. Обратите внимание, что при первом запуске этот метод может занять несколько секунд или даже минут в зависимости от скорости сети и ip-адресов, которые необходимо скачать.
// 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();
API C не поддерживают один EnsureAndRegisterCertifiedAsync() вызов. Вместо этого следуйте инструкциям, чтобы скачать и зарегистрировать конкретный EP.
# Please DO NOT use this API. It won't register EPs to the python ort env.
Поиск всех совместимых EPs
Вы можете увидеть, какие EP (включая неустановленные EP) доступны устройству пользователя, вызвав метод FindAllProviders().
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}")
Возвращаемые поставщики выполнения зависят от устройства пользователя и доступных поставщиков выполнения. На совместимом устройстве Qualcomm без установленных поставщиков выполнения приведенный выше код выводит следующий код...
QNNExecutionProvider: NotPresent
Каждый ExecutionProvider имеет свойство ReadyState , указывающее его текущее состояние на устройстве. Понимание этих состояний помогает определить, какие действия необходимо предпринять приложению.
| ReadyState |
Определение |
Дальнейшие шаги |
NotPresent |
EP не установлен на клиентском устройстве. |
Вызов EnsureReadyAsync() для скачивания и установки EP и добавления его в граф зависимостей среды выполнения приложения. |
NotReady |
EP устанавливается на клиентском устройстве, но не добавлен в граф зависимостей среды выполнения приложения. |
Вызовите EnsureReadyAsync(), чтобы добавить EP в граф зависимостей времени выполнения вашего приложения. |
Ready |
EP устанавливается на клиентском устройстве и добавлен в граф зависимостей среды выполнения приложения. |
Вызовите TryRegister(), чтобы зарегистрировать EP в ONNX Runtime. |
Установка определенного EP
Если в вашем приложении есть конкретный ExecutionProvider, и его ReadyStateNotPresent, вы можете скачать и установить его, вызвав EnsureReadyAsync().
Сначала вы будете использовать FindAllProviders() для получения всех совместимых EPs, а затем можно вызвать EnsureReadyAsync() на конкретном поставщике выполнения, чтобы скачать конкретного поставщика выполнения, и вызвать TryRegister(), чтобы зарегистрировать конкретного поставщика выполнения.
// 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;
Сначала вы будете использовать FindAllProviders() для получения всех совместимых EPs, а затем можно вызвать EnsureReadyAsync() на конкретном поставщике выполнения, чтобы скачать конкретного поставщика выполнения, и вызвать TryRegister(), чтобы зарегистрировать конкретного поставщика выполнения.
// 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;
Вы будете использовать метод WinMLEpCatalogFindProvider для запроса конкретного поставщика выполнения, а затем можно вызвать WinMLEpEnsureReady, передав в WinMLEpHandle, чтобы загрузить конкретного поставщика выполнения, а затем использовать WinMLEpGetLibraryPathSize и WinMLEpGetLibraryPath, чтобы получить путь к поставщику выполнения и зарегистрировать его в ONNX Runtime с помощью 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);
Сначала вы будете использовать find_all_providers() для получения всех совместимых EP, а затем можно вызвать ensure_ready_async() на конкретном поставщике выполнения, чтобы скачать этого поставщика выполнения и использовать register_execution_provider_library среды выполнения ONNX для его регистрации.
# 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
Установка с отображением прогресса
API-интерфейсы для скачивания и установки EPs включают обратные вызовы, которые предоставляют обновления хода выполнения. Это позволяет отображать индикаторы прогресса и информировать пользователей.
// 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
Дальнейшие шаги
Теперь, когда вы установили поставщиков выполнения, ознакомьтесь с разделом "Регистрация поставщиков выполнения ", чтобы узнать, как зарегистрировать их для использования в onNX Runtime.
Пример рабочего приложения
Для рабочих приложений ниже приведен пример того, что может потребоваться сделать ваше приложение, чтобы предоставить себе и пользователям контроль над моментом загрузки. Вы можете проверить, доступны ли новые провайдеры выполнения и загружать их по необходимости перед регистрацией:
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)
См. также