Skip to content
Merged
32 changes: 32 additions & 0 deletions include/onnxruntime/core/session/onnxruntime_ep_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -2965,6 +2965,38 @@ struct OrtEpFactory {
*/
ORT_API2_STATUS(DeinitGraphicsInterop, _In_ OrtEpFactory* this_ptr,
_In_ const OrtEpDevice* ep_device);

/** \brief Select the best compiled model compatibility info from candidate strings.
*
* Evaluates each candidate compatibility string against the given hardware devices and returns the selected index.
*
* Context about having this function:
* The existing ValidateCompiledModelCompatibilityInfo() alone is not sufficient for some EPs to determine the best
* compatible model when there are multiple candidates. For example, an EP may support multiple compilation modes
Comment thread
skottmckay marked this conversation as resolved.
* (e.g., "speed optimized" vs "memory optimized") that produce different compatibility strings. The EP can implement
* this function to evaluate the candidate strings and select the best compatible one based on its own criteria and
* the target devices.
*
* If all candidates are unsupported, this function succeeds and sets `selected_index` to SIZE_MAX.
*
* \param[in] this_ptr The OrtEpFactory instance.
* \param[in] devices Array of OrtHardwareDevice pointers that the EP would run on. All must map to this EP.
* \param[in] num_devices Number of entries in `devices`.
* \param[in] compatibility_infos Array of candidate compatibility strings.
* \param[in] num_compatibility_infos Number of entries in `compatibility_infos`.
* \param[out] selected_index Index of selected candidate, or SIZE_MAX if all candidates are unsupported.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
* \since Version 1.27.
*/
ORT_API2_STATUS(SelectBestCompiledModelCompatibilityInfo,
_In_ OrtEpFactory* this_ptr,
_In_reads_(num_devices) const OrtHardwareDevice* const* devices,
_In_ size_t num_devices,
_In_reads_(num_compatibility_infos) const char* const* compatibility_infos,
_In_ size_t num_compatibility_infos,
_Out_ size_t* selected_index);
Comment thread
chilo-ms marked this conversation as resolved.
Outdated
};

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions onnxruntime/core/session/plugin_ep/ep_factory_internal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ EpFactoryInternal::EpFactoryInternal(std::unique_ptr<EpFactoryInternalImpl> impl
OrtEpFactory::GetHardwareDeviceIncompatibilityDetails = Forward::GetHardwareDeviceIncompatibilityDetails;
OrtEpFactory::InitGraphicsInterop = Forward::InitGraphicsInterop;
OrtEpFactory::DeinitGraphicsInterop = Forward::DeinitGraphicsInterop;
OrtEpFactory::SelectBestCompiledModelCompatibilityInfo = Forward::SelectBestCompiledModelCompatibilityInfo;
}

InternalExecutionProviderFactory::InternalExecutionProviderFactory(EpFactoryInternal& ep_factory,
Expand Down
10 changes: 10 additions & 0 deletions onnxruntime/core/session/plugin_ep/ep_factory_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ class EpFactoryInternal : public OrtEpFactory {
return impl_->DeinitGraphicsInterop(ep_device);
}

OrtStatus* SelectBestCompiledModelCompatibilityInfo(_In_reads_(num_devices) const OrtHardwareDevice* const* devices,
_In_ size_t num_devices,
_In_reads_(num_compatibility_infos) const char* const* compatibility_infos,
_In_ size_t num_compatibility_infos,
_Out_ size_t* selected_index) noexcept {
return impl_->SelectBestCompiledModelCompatibilityInfo(devices, num_devices,
compatibility_infos, num_compatibility_infos,
selected_index);
}

// Function ORT calls to release an EP instance.
void ReleaseEp(OrtEp* /*ep*/) noexcept {
// we never create an OrtEp so we should never be trying to release one
Expand Down
18 changes: 18 additions & 0 deletions onnxruntime/core/session/plugin_ep/ep_factory_internal_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,24 @@ class EpFactoryInternalImpl {
return nullptr;
}

virtual OrtStatus* SelectBestCompiledModelCompatibilityInfo(
_In_reads_(num_devices) const OrtHardwareDevice* const* devices,
_In_ size_t num_devices,
_In_reads_(num_compatibility_infos) const char* const* compatibility_infos,
_In_ size_t num_compatibility_infos,
_Out_ size_t* selected_index) noexcept {
ORT_UNUSED_PARAMETER(devices);
ORT_UNUSED_PARAMETER(num_devices);
if (compatibility_infos == nullptr || num_compatibility_infos == 0 || selected_index == nullptr) {
return OrtApis::CreateStatus(ORT_INVALID_ARGUMENT,
"Invalid arguments to SelectBestCompiledModelCompatibilityInfo.");
}

// Default implementation: all candidates are unsupported, sets `selected_index` to SIZE_MAX.
*selected_index = SIZE_MAX;
return nullptr;
}

// Function ORT calls to release an EP instance.
void ReleaseEp(OrtEp* ep);

Expand Down
11 changes: 11 additions & 0 deletions onnxruntime/core/session/plugin_ep/forward_to_factory_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ struct ForwardToFactoryImpl {
return static_cast<TFactory*>(this_ptr)->DeinitGraphicsInterop(ep_device);
}

static OrtStatus* ORT_API_CALL SelectBestCompiledModelCompatibilityInfo(
OrtEpFactory* this_ptr,
_In_reads_(num_devices) const OrtHardwareDevice* const* devices,
size_t num_devices,
_In_reads_(num_compatibility_infos) const char* const* compatibility_infos,
size_t num_compatibility_infos,
size_t* selected_index) noexcept {
return static_cast<TFactory*>(this_ptr)->SelectBestCompiledModelCompatibilityInfo(
devices, num_devices, compatibility_infos, num_compatibility_infos, selected_index);
}

static void ORT_API_CALL ReleaseEp(OrtEpFactory* this_ptr, OrtEp* ep) noexcept {
static_cast<TFactory*>(this_ptr)->ReleaseEp(ep);
}
Expand Down
71 changes: 71 additions & 0 deletions onnxruntime/test/autoep/library/example_plugin_ep/ep_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "ep_factory.h"

#include <cassert>
#include <limits>

#include "ep.h"
#include "ep_allocator.h"
Expand All @@ -13,6 +14,22 @@

#include "core/session/onnxruntime_session_options_config_keys.h"

namespace {
int CompatibilityRank(OrtCompiledModelCompatibility c) {
switch (c) {
case OrtCompiledModelCompatibility_EP_SUPPORTED_OPTIMAL:
return 3;
case OrtCompiledModelCompatibility_EP_SUPPORTED_PREFER_RECOMPILATION:
return 2;
case OrtCompiledModelCompatibility_EP_NOT_APPLICABLE:
return 1;
case OrtCompiledModelCompatibility_EP_UNSUPPORTED:
default:
return 0;
}
}
} // namespace

ExampleEpFactory::ExampleEpFactory(const char* ep_name, ApiPtrs apis, const OrtLogger& default_logger)
: OrtEpFactory{},
ApiPtrs(apis),
Expand Down Expand Up @@ -45,6 +62,7 @@ ExampleEpFactory::ExampleEpFactory(const char* ep_name, ApiPtrs apis, const OrtL
GetNumCustomOpDomains = GetNumCustomOpDomainsImpl;
GetCustomOpDomains = GetCustomOpDomainsImpl;
ValidateCompiledModelCompatibilityInfo = ValidateCompiledModelCompatibilityInfoImpl;
SelectBestCompiledModelCompatibilityInfo = SelectBestCompiledModelCompatibilityInfoImpl;

// setup the OrtMemoryInfo instances required by the EP.
// We pretend the device the EP is running on is GPU.
Expand Down Expand Up @@ -514,3 +532,56 @@ OrtStatus* ORT_API_CALL ExampleEpFactory::ValidateCompiledModelCompatibilityInfo
*model_compatibility = OrtCompiledModelCompatibility_EP_SUPPORTED_OPTIMAL;
return nullptr;
}

OrtStatus* ORT_API_CALL ExampleEpFactory::SelectBestCompiledModelCompatibilityInfoImpl(
OrtEpFactory* this_ptr,
const OrtHardwareDevice* const* devices,
size_t num_devices,
const char* const* compatibility_infos,
size_t num_compatibility_infos,
size_t* selected_index) noexcept {
auto& factory = *static_cast<ExampleEpFactory*>(this_ptr);

if (selected_index == nullptr) {
return factory.ort_api.CreateStatus(ORT_INVALID_ARGUMENT, "selected_index cannot be nullptr");
}

*selected_index = std::numeric_limits<size_t>::max();

if (compatibility_infos == nullptr || num_compatibility_infos == 0) {
return factory.ort_api.CreateStatus(ORT_INVALID_ARGUMENT,
"compatibility_infos cannot be nullptr or empty");
}

int best_rank = -1;
size_t best_idx = std::numeric_limits<size_t>::max();

for (size_t i = 0; i < num_compatibility_infos; ++i) {
if (compatibility_infos[i] == nullptr) {
return factory.ort_api.CreateStatus(ORT_INVALID_ARGUMENT,
"compatibility_infos contains a nullptr entry");
}

OrtCompiledModelCompatibility compatibility = OrtCompiledModelCompatibility_EP_UNSUPPORTED;
OrtStatus* status = ValidateCompiledModelCompatibilityInfoImpl(
this_ptr, devices, num_devices, compatibility_infos[i], &compatibility);
if (status != nullptr) {
return status;
}

const int rank = CompatibilityRank(compatibility);
if (rank > best_rank) {
best_rank = rank;
best_idx = i;
}
}

// all unsupported => SIZE_MAX
if (best_rank <= CompatibilityRank(OrtCompiledModelCompatibility_EP_UNSUPPORTED)) {
*selected_index = std::numeric_limits<size_t>::max();
} else {
*selected_index = best_idx;
}

return nullptr;
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ class ExampleEpFactory : public OrtEpFactory, public ApiPtrs {
const char* compatibility_info,
OrtCompiledModelCompatibility* model_compatibility) noexcept;

static OrtStatus* ORT_API_CALL SelectBestCompiledModelCompatibilityInfoImpl(
OrtEpFactory* this_ptr,
const OrtHardwareDevice* const* devices,
size_t num_devices,
const char* const* compatibility_infos,
size_t num_compatibility_infos,
size_t* selected_index) noexcept;

const std::string ep_name_; // EP name
const std::string vendor_{"Contoso"}; // EP vendor name
const uint32_t vendor_id_{0xB357}; // EP vendor ID
Expand Down
67 changes: 67 additions & 0 deletions onnxruntime/test/autoep/test_ep_compatibility.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include <limits>
#include <string>

#include "gtest/gtest.h"

#include "core/session/onnxruntime_c_api.h"
#include "core/session/onnxruntime_cxx_api.h"
#include "core/session/abi_devices.h"
#include "test/autoep/test_autoep_utils.h"

using namespace onnxruntime;

TEST(EpCompatibilitySelectBestTest, SelectBestCompiledModelCompatibilityInfo_UsesHardwareDevices) {
Ort::Env env{ORT_LOGGING_LEVEL_WARNING, "EpCompatSelectBestExampleEp"};

onnxruntime::test::RegisteredEpDeviceUniquePtr example_ep;
onnxruntime::test::Utils::RegisterAndGetExampleEp(
env, onnxruntime::test::Utils::example_ep_info, example_ep);

ASSERT_NE(example_ep.get(), nullptr);

const OrtEpDevice* ep_device = example_ep.get();
OrtEpFactory* factory = ep_device->GetMutableFactory();
ASSERT_NE(factory, nullptr);
ASSERT_NE(factory->SelectBestCompiledModelCompatibilityInfo, nullptr);

// API takes OrtHardwareDevice[].
const OrtHardwareDevice* devices[] = {ep_device->device};

// Match ExampleEpFactory::ValidateCompiledModelCompatibilityInfoImpl format:
// "<ep_name>;version=X;ort_api_version=Y;hardware_architecture=Z"
const std::string ep_name = factory->GetName(factory);

const std::string optimal =
ep_name + ";version=0.1.0;ort_api_version=" + std::to_string(ORT_API_VERSION) + ";hardware_architecture=arch1";

const std::string prefer_recompile =
ep_name + ";version=9.9.9;ort_api_version=" + std::to_string(ORT_API_VERSION) + ";hardware_architecture=arch1";

const std::string unsupported =
"SomeOtherEp;version=0.1.0;ort_api_version=" + std::to_string(ORT_API_VERSION) + ";hardware_architecture=arch1";

const char* compatibility_infos[] = {
unsupported.c_str(),
prefer_recompile.c_str(),
optimal.c_str(),
};

size_t selected_index = std::numeric_limits<size_t>::max();
OrtStatus* st = factory->SelectBestCompiledModelCompatibilityInfo(
factory,
devices,
1,
compatibility_infos,
3,
&selected_index);

const OrtApi* api = OrtGetApiBase()->GetApi(ORT_API_VERSION);
ASSERT_NE(api, nullptr);
ASSERT_EQ(st, nullptr) << (st ? api->GetErrorMessage(st) : "");

// Best candidate should be the fully optimal one.
EXPECT_EQ(selected_index, 2u);
}
Loading