Skip to content

Commit 7140cc9

Browse files
committed
[1/N] Add BackendOptions class
Pull Request resolved: #11389 Introduce backend option as discussed in #10216 Step 1: Introducd Backend Option class In later stage, it will be plugged in with the rest of the stack. BackendOptions is pretty much a list of BackendOption, and backend option is a key value pair. The key is a string, and the value can be 3 different types, including bool, string and int. ghstack-source-id: 290059228 Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/)
1 parent 496cb05 commit 7140cc9

File tree

4 files changed

+294
-1
lines changed

4 files changed

+294
-1
lines changed

runtime/backend/options.h

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
#include <executorch/runtime/core/error.h>
11+
#include <executorch/runtime/core/span.h>
12+
#include <cstddef>
13+
#include <cstring>
14+
#include <variant>
15+
16+
namespace executorch {
17+
namespace runtime {
18+
19+
// Strongly-typed option key template
20+
template <typename T>
21+
struct OptionKey {
22+
using value_type = T;
23+
const char* key;
24+
constexpr explicit OptionKey(const char* k) : key(k) {}
25+
};
26+
27+
// All string keys and values must have static storage duration (string
28+
// literals, static const char arrays, or global constants). The BackendOptions
29+
// class does NOT take ownership of strings.
30+
using OptionValue = std::variant<bool, int, const char*>;
31+
static constexpr size_t kMaxOptionKeyLength = 64;
32+
33+
struct BackendOption {
34+
// key is the name of the backend option, like num_threads, enable_profiling,
35+
// etc
36+
char key[kMaxOptionKeyLength];
37+
// value is the value of the backend option, like 4, true, etc
38+
OptionValue value;
39+
};
40+
41+
/**
42+
* A template class for storing and managing backend-specific configuration
43+
* options.
44+
*
45+
* This class provides a type-safe way to store key-value pairs for backend
46+
* configuration, with compile-time capacity limits and runtime type checking.
47+
* It supports bool, int, and const char* value types.
48+
*
49+
* @tparam MaxCapacity The maximum number of options that can be stored
50+
*/
51+
template <size_t MaxCapacity>
52+
class BackendOptions {
53+
public:
54+
/**
55+
* Default constructor - initializes with zero options.
56+
*/
57+
BackendOptions() : size_(0) {}
58+
59+
/**
60+
* Returns a const view of all stored options as a Span.
61+
*
62+
* @return A const Span containing all BackendOption entries
63+
*/
64+
executorch::runtime::Span<BackendOption> view() const {
65+
return executorch::runtime::Span<BackendOption>(options_, size_);
66+
}
67+
68+
/**
69+
* Returns a mutable view of all stored options as a Span.
70+
*
71+
* @return A mutable Span containing all BackendOption entries
72+
*/
73+
executorch::runtime::Span<BackendOption> view() {
74+
return executorch::runtime::Span<BackendOption>(options_, size_);
75+
}
76+
77+
/**
78+
* Sets a boolean option value for the given key.
79+
* If the key already exists, updates its value. Otherwise, adds a new option.
80+
*
81+
* @tparam N The length of the key string (automatically deduced)
82+
* @param key The option key (must be a string literal or array)
83+
* @param value The boolean value to set
84+
* @return Error::Ok on success, Error::InvalidArgument if storage is full
85+
*/
86+
template <size_t N>
87+
Error set_option(const char (&key)[N], bool value) noexcept {
88+
ET_CHECK_MSG(N <= kMaxOptionKeyLength, "Option key is too long");
89+
return set_option_impl(key, value);
90+
}
91+
92+
/**
93+
* Sets an integer option value for the given key.
94+
* If the key already exists, updates its value. Otherwise, adds a new option.
95+
*
96+
* @tparam N The length of the key string (automatically deduced)
97+
* @param key The option key (must be a string literal or array)
98+
* @param value The integer value to set
99+
* @return Error::Ok on success, Error::InvalidArgument if storage is full
100+
*/
101+
template <size_t N>
102+
Error set_option(const char (&key)[N], int value) noexcept {
103+
ET_CHECK_MSG(N <= kMaxOptionKeyLength, "Option key is too long");
104+
return set_option_impl(key, value);
105+
}
106+
107+
/**
108+
* Sets a string option value for the given key.
109+
* If the key already exists, updates its value. Otherwise, adds a new option.
110+
*
111+
* Note: The string value must have static storage duration. This class does
112+
* NOT take ownership of the string - it only stores the pointer.
113+
*
114+
* @tparam N The length of the key string (automatically deduced)
115+
* @param key The option key (must be a string literal or array)
116+
* @param value The string value to set (must have static storage duration)
117+
* @return Error::Ok on success, Error::InvalidArgument if storage is full
118+
*/
119+
template <size_t N>
120+
Error set_option(const char (&key)[N], const char* value) noexcept {
121+
ET_CHECK_MSG(N <= kMaxOptionKeyLength, "Option key is too long");
122+
return set_option_impl(key, value);
123+
}
124+
125+
/**
126+
* Retrieves an option value by key and type.
127+
*
128+
* @tparam T The expected type of the option value (bool, int, or const char*)
129+
* @tparam KeyLen The length of the key string (automatically deduced)
130+
* @param key The option key to look up
131+
* @param out Reference to store the retrieved value
132+
* @return Error::Ok if found and type matches, Error::NotFound if key doesn't
133+
* exist, Error::InvalidArgument if type doesn't match
134+
*/
135+
template <typename T, size_t KeyLen>
136+
Error get_option(const char (&key)[KeyLen], T& out) const {
137+
ET_CHECK_MSG(KeyLen <= kMaxOptionKeyLength, "Option key is too long");
138+
139+
for (size_t i = 0; i < size_; ++i) {
140+
if (std::strcmp(options_[i].key, key) == 0) {
141+
if (auto* val = std::get_if<T>(&options_[i].value)) {
142+
out = *val;
143+
return Error::Ok;
144+
}
145+
return Error::InvalidArgument;
146+
}
147+
}
148+
return Error::NotFound;
149+
}
150+
151+
private:
152+
BackendOption options_[MaxCapacity]{}; // Storage for backend options
153+
size_t size_; // Current number of options
154+
155+
/**
156+
* Internal implementation for setting option values.
157+
* Handles both updating existing options and adding new ones.
158+
*
159+
* @tparam T The type of the value (bool, int, or const char*)
160+
* @param key The option key
161+
* @param value The value to set
162+
* @return Error::Ok on success, Error::InvalidArgument if storage is full
163+
*/
164+
template <typename T>
165+
Error set_option_impl(const char* key, T value) {
166+
// Update existing if found
167+
for (size_t i = 0; i < size_; ++i) {
168+
if (strcmp(options_[i].key, key) == 0) {
169+
options_[i].value = value;
170+
return Error::Ok;
171+
}
172+
}
173+
// Add new option if space available
174+
if (size_ < MaxCapacity) {
175+
BackendOption new_option;
176+
strncpy(new_option.key, key, kMaxOptionKeyLength - 1);
177+
new_option.key[kMaxOptionKeyLength - 1] = '\0';
178+
new_option.value = value;
179+
options_[size_++] = new_option;
180+
return Error::Ok;
181+
}
182+
// Return error when full
183+
return Error::InvalidArgument;
184+
}
185+
};
186+
187+
} // namespace runtime
188+
} // namespace executorch

runtime/backend/targets.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def define_common_targets():
1717
exported_headers = [
1818
"backend_execution_context.h",
1919
"backend_init_context.h",
20+
"options.h",
2021
"interface.h",
2122
],
2223
preprocessor_flags = ["-DUSE_ATEN_LIB"] if aten_mode else [],
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <executorch/runtime/backend/options.h>
10+
#include <executorch/runtime/platform/runtime.h>
11+
12+
#include <gtest/gtest.h>
13+
14+
using namespace ::testing;
15+
using executorch::runtime::BackendOptions;
16+
using executorch::runtime::Error;
17+
18+
class BackendOptionsTest : public ::testing::Test {
19+
protected:
20+
void SetUp() override {
21+
// Since these tests cause ET_LOG to be called, the PAL must be initialized
22+
// first.
23+
executorch::runtime::runtime_init();
24+
}
25+
BackendOptions<5> options; // Capacity of 5 for testing limits
26+
};
27+
28+
// Test basic string functionality
29+
TEST_F(BackendOptionsTest, HandlesStringOptions) {
30+
// Set and retrieve valid string
31+
options.set_option("backend_type", "GPU");
32+
const char* result = nullptr;
33+
EXPECT_EQ(options.get_option("backend_type", result), Error::Ok);
34+
EXPECT_STREQ(result, "GPU");
35+
36+
// Update existing key
37+
options.set_option("backend_type", "CPU");
38+
EXPECT_EQ(options.get_option("backend_type", result), Error::Ok);
39+
EXPECT_STREQ(result, "CPU");
40+
}
41+
42+
// Test boolean options
43+
TEST_F(BackendOptionsTest, HandlesBoolOptions) {
44+
options.set_option("debug", true);
45+
bool debug = false;
46+
EXPECT_EQ(options.get_option("debug", debug), Error::Ok);
47+
EXPECT_TRUE(debug);
48+
49+
// Test false value
50+
options.set_option("verbose", false);
51+
EXPECT_EQ(options.get_option("verbose", debug), Error::Ok);
52+
EXPECT_FALSE(debug);
53+
}
54+
55+
// Test integer options
56+
TEST_F(BackendOptionsTest, HandlesIntOptions) {
57+
options.set_option("num_threads", 256);
58+
int num_threads = 0;
59+
EXPECT_EQ(options.get_option("num_threads", num_threads), Error::Ok);
60+
EXPECT_EQ(num_threads, 256);
61+
}
62+
63+
// Test error conditions
64+
TEST_F(BackendOptionsTest, HandlesErrors) {
65+
// Non-existent key
66+
bool dummy_bool;
67+
EXPECT_EQ(options.get_option("missing", dummy_bool), Error::NotFound);
68+
69+
// Type mismatch
70+
options.set_option("threshold", 100);
71+
const char* dummy_str = nullptr;
72+
EXPECT_EQ(options.get_option("threshold", dummy_str), Error::InvalidArgument);
73+
74+
// Null value handling
75+
options.set_option("nullable", static_cast<const char*>(nullptr));
76+
EXPECT_EQ(options.get_option("nullable", dummy_str), Error::Ok);
77+
EXPECT_EQ(dummy_str, nullptr);
78+
}
79+
80+
// Test type-specific keys
81+
TEST_F(BackendOptionsTest, EnforcesKeyTypes) {
82+
// Same key name - later set operations overwrite earlier ones
83+
options.set_option("flag", true);
84+
options.set_option("flag", 123); // Overwrites the boolean entry
85+
86+
bool bval;
87+
int ival;
88+
89+
// Boolean get should fail - type was overwritten to INT
90+
EXPECT_EQ(options.get_option("flag", bval), Error::InvalidArgument);
91+
92+
// Integer get should succeed with correct value
93+
EXPECT_EQ(options.get_option("flag", ival), Error::Ok);
94+
EXPECT_EQ(ival, 123);
95+
}

runtime/backend/test/targets.bzl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1+
load("@fbsource//xplat/executorch/build:runtime_wrapper.bzl", "runtime")
2+
13
def define_common_targets():
24
"""Defines targets that should be shared between fbcode and xplat.
35
46
The directory containing this targets.bzl file should also contain both
57
TARGETS and BUCK files that call this function.
68
"""
7-
pass
9+
runtime.cxx_test(
10+
name = "backend_options_test",
11+
srcs = ["backend_options_test.cpp"],
12+
deps = [
13+
"//executorch/runtime/core:core",
14+
"//executorch/runtime/backend:interface",
15+
],
16+
)

0 commit comments

Comments
 (0)