Skip to content

[5.1/ N] set_option/get_option API with {backend_name, backend options} only #11865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 24, 2025
37 changes: 37 additions & 0 deletions runtime/backend/interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,42 @@ Result<const char*> get_backend_name(size_t index) {
return registered_backends[index].name;
}

Error set_option(
const char* backend_name,
const executorch::runtime::Span<executorch::runtime::BackendOption>
backend_options) {
auto backend_class = get_backend_class(backend_name);
if (!backend_class) {
return Error::NotFound;
}

BackendOptionContext backend_option_context;
Error result =
backend_class->set_option(backend_option_context, backend_options);
if (result != Error::Ok) {
return result;
}
return Error::Ok;
}

Error get_option(
const char* backend_name,
executorch::runtime::Span<executorch::runtime::BackendOption>
backend_options) {
auto backend_class = get_backend_class(backend_name);
if (!backend_class) {
return Error::NotFound;
}
BackendOptionContext backend_option_context;
executorch::runtime::Span<BackendOption> backend_options_ref(
backend_options.data(), backend_options.size());
auto result =
backend_class->get_option(backend_option_context, backend_options_ref);
if (result != Error::Ok) {
return result;
}
return Error::Ok;
}

} // namespace ET_RUNTIME_NAMESPACE
} // namespace executorch
31 changes: 31 additions & 0 deletions runtime/backend/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,37 @@ size_t get_num_registered_backends();
*/
Result<const char*> get_backend_name(size_t index);


/**
* Sets backend options for a specific backend.
*
* @param backend_name The name of the backend to set options for
* @param backend_options The backend option list containing the options
* to set
* @return Error::Ok on success, Error::NotFound if backend is not found, or
* other error codes on failure
*/
Error set_option(
const char* backend_name,
const executorch::runtime::Span<executorch::runtime::BackendOption>
backend_options);


/**
* Retrieves backend options for a specific backend.
*
* @param backend_name The name of the backend to get options from
* @param backend_options The backend option objects that will be filled with
* the populated values from the backend
* @return Error::Ok on success, Error::NotFound if backend is not found, or
* other error codes on failure
*/
Error get_option(
const char* backend_name,
executorch::runtime::Span<executorch::runtime::BackendOption>
backend_options);


} // namespace ET_RUNTIME_NAMESPACE
} // namespace executorch

Expand Down
118 changes: 117 additions & 1 deletion runtime/backend/test/backend_interface_update_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
*/

#include <executorch/runtime/backend/interface.h>
#include <executorch/runtime/backend/options.h>
#include <executorch/runtime/platform/runtime.h>

#include <gtest/gtest.h>
#include <memory>

using namespace ::testing;
using executorch::runtime::ArrayRef;
Expand Down Expand Up @@ -61,7 +63,8 @@ class MockBackend : public BackendInterface {
int success_update = 0;
for (const auto& backend_option : backend_options) {
if (strcmp(backend_option.key, "Backend") == 0) {
if (std::holds_alternative<std::array<char, 256>>(
if (std::holds_alternative<
std::array<char, executorch::runtime::kMaxOptionValueLength>>(
backend_option.value)) {
// Store the value in our member variable
const auto& arr =
Expand Down Expand Up @@ -285,3 +288,116 @@ TEST_F(BackendInterfaceUpdateTest, UpdateBetweenExecutes) {
ASSERT_TRUE(mock_backend->target_backend.has_value());
EXPECT_STREQ(mock_backend->target_backend.value().c_str(), "NPU");
}

// Mock backend for testing
class StubBackend : public BackendInterface {
public:
~StubBackend() override = default;

bool is_available() const override {
return true;
}

Result<DelegateHandle*> init(
BackendInitContext& context,
FreeableBuffer* processed,
ArrayRef<CompileSpec> compile_specs) const override {
return nullptr;
}

Error execute(
BackendExecutionContext& context,
DelegateHandle* handle,
EValue** args) const override {
return Error::Ok;
}

Error get_option(
BackendOptionContext& context,
executorch::runtime::Span<executorch::runtime::BackendOption>&
backend_options) override {
// For testing purposes, just record that get_option was called
// and verify the input parameters
get_option_called = true;
get_option_call_count++;
last_get_option_size = backend_options.size();

// Verify that the expected option key is present and modify the value
for (size_t i = 0; i < backend_options.size(); ++i) {
if (strcmp(backend_options[i].key, "NumberOfThreads") == 0) {
// Set the value to what was stored by set_option
backend_options[i].value = last_num_threads;
found_expected_key = true;
break;
}
}

return Error::Ok;
}

Error set_option(
BackendOptionContext& context,
const executorch::runtime::Span<executorch::runtime::BackendOption>&
backend_options) override {
// Store the options for verification
last_options_size = backend_options.size();
if (backend_options.size() > 0) {
for (const auto& option : backend_options) {
if (strcmp(option.key, "NumberOfThreads") == 0) {
if (auto* val = std::get_if<int>(&option.value)) {
last_num_threads = *val;
}
}
}
}
return Error::Ok;
}

// Mutable for testing verification
size_t last_options_size = 0;
int last_num_threads = 0;
bool get_option_called = false;
int get_option_call_count = 0;
size_t last_get_option_size = 0;
bool found_expected_key = false;
};

class BackendUpdateTest : public ::testing::Test {
protected:
void SetUp() override {
// Since these tests cause ET_LOG to be called, the PAL must be initialized
// first.
executorch::runtime::runtime_init();

// Register the stub backend
stub_backend = std::make_unique<StubBackend>();
Backend backend_config{"StubBackend", stub_backend.get()};
auto register_result = register_backend(backend_config);
ASSERT_EQ(register_result, Error::Ok);
}

std::unique_ptr<StubBackend> stub_backend;
};

// Test basic string functionality
TEST_F(BackendUpdateTest, TestSetGetOption) {
BackendOptions<1> backend_options;
int new_num_threads = 4;
backend_options.set_option("NumberOfThreads", new_num_threads);

auto status = set_option("StubBackend", backend_options.view());
ASSERT_EQ(status, Error::Ok);

// Set up the default option, which will be populuated by the get_option call
BackendOption ref_backend_option{"NumberOfThreads", 0};
status = get_option("StubBackend", ref_backend_option);

// Verify that the backend actually received the options
ASSERT_TRUE(
std::get<int>(ref_backend_option.value) ==
new_num_threads);

// Verify that the backend actually update the options
ASSERT_EQ(stub_backend->last_options_size, 1);
ASSERT_EQ(stub_backend->last_num_threads, new_num_threads);
}
Loading