Skip to content

Commit bf88011

Browse files
committed
Update base for Update on "[4/N] Add backend options map"
This is to manage the backend <-> BackendOptions map. Users will create the bakcend options map, and ET runtime will read the backend name, and dispatch the list of backend options to each backend. Differential Revision: [D76149466](https://our.internmc.facebook.com/intern/diff/D76149466/) [ghstack-poisoned]
1 parent bc09ed6 commit bf88011

File tree

4 files changed

+129
-114
lines changed

4 files changed

+129
-114
lines changed

runtime/backend/backend_options.h

Lines changed: 85 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -6,94 +6,88 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
#pragma once
10-
#include <executorch/runtime/core/error.h>
11-
#include <cstddef>
12-
#include <cstring>
13-
#include <executorch/runtime/core/array_ref.h>
14-
#include <executorch/runtime/core/error.h>
15-
#include <variant>
16-
17-
namespace executorch {
18-
namespace runtime {
19-
20-
// Strongly-typed option key template
21-
template <typename T>
22-
struct OptionKey {
23-
const char* key;
24-
constexpr explicit OptionKey(const char* k) : key(k) {}
25-
};
26-
27-
// Union replaced with std::variant
28-
using OptionValue = std::variant<bool, int, const char*>;
29-
30-
struct BackendOption {
31-
const char* key; // key is the name of the backend option, like num_threads,
32-
// enable_profiling, etc
33-
OptionValue
34-
value; // value is the value of the backend option, like 4, true, etc
35-
};
36-
37-
template <size_t MaxCapacity>
38-
class BackendOptions {
39-
public:
40-
// Initialize with zero options
41-
BackendOptions() : size_(0) {}
42-
43-
// Type-safe setters
44-
template <typename T>
45-
void set_option(OptionKey<T> key, T value) {
46-
const char* k = key.key;
47-
// Update existing if found
48-
for (size_t i = 0; i < size_; ++i) {
49-
if (strcmp(options_[i].key, k) == 0) {
50-
options_[i].value = value;
51-
return;
52-
}
53-
}
54-
// Add new option if space available
55-
if (size_ < MaxCapacity) {
56-
options_[size_++] = BackendOption{k, value};
57-
}
58-
}
59-
60-
// Type-safe getters
61-
template <typename T>
62-
Error get_option(OptionKey<T> key, T& out) const {
63-
const char* k = key.key;
64-
for (size_t i = 0; i < size_; ++i) {
65-
if (strcmp(options_[i].key, k) == 0) {
66-
if (auto* val = std::get_if<T>(&options_[i].value)) {
67-
out = *val;
68-
return Error::Ok;
69-
}
70-
return Error::InvalidArgument;
71-
}
72-
}
73-
return Error::NotFound;
74-
}
75-
executorch::runtime::ArrayRef<BackendOption> view() const {
76-
return executorch::runtime::ArrayRef<BackendOption>(options_, size_);
77-
}
78-
79-
private:
80-
BackendOption options_[MaxCapacity]{}; // Storage for backend options
81-
size_t size_; // Current number of options
82-
};
83-
84-
// Helper functions for creating typed option keys (unchanged)
85-
constexpr OptionKey<bool> BoolKey(const char* k) {
86-
return OptionKey<bool>(k);
87-
}
88-
89-
constexpr OptionKey<int> IntKey(const char* k) {
90-
return OptionKey<int>(k);
91-
}
92-
93-
constexpr OptionKey<const char*> StrKey(const char* k) {
94-
return OptionKey<const char*>(k);
95-
}
96-
97-
} // namespace runtime
98-
} // namespace executorch
99-
9+
#pragma once
10+
#include <executorch/runtime/core/error.h>
11+
#include <cstddef>
12+
#include <cstring>
13+
#include <variant>
14+
15+
namespace executorch {
16+
namespace runtime {
17+
18+
// Strongly-typed option key template
19+
template <typename T>
20+
struct OptionKey {
21+
const char* key;
22+
constexpr explicit OptionKey(const char* k) : key(k) {}
23+
};
24+
25+
// Union replaced with std::variant
26+
using OptionValue = std::variant<bool, int, const char*>;
27+
28+
struct BackendOption {
29+
const char* key; // key is the name of the backend option, like num_threads,
30+
// enable_profiling, etc
31+
OptionValue
32+
value; // value is the value of the backend option, like 4, true, etc
33+
};
34+
35+
template <size_t MaxCapacity>
36+
class BackendOptions {
37+
public:
38+
// Initialize with zero options
39+
BackendOptions() : size_(0) {}
40+
41+
// Type-safe setters
42+
template <typename T>
43+
void set_option(OptionKey<T> key, T value) {
44+
const char* k = key.key;
45+
// Update existing if found
46+
for (size_t i = 0; i < size_; ++i) {
47+
if (strcmp(options_[i].key, k) == 0) {
48+
options_[i].value = value;
49+
return;
50+
}
51+
}
52+
// Add new option if space available
53+
if (size_ < MaxCapacity) {
54+
options_[size_++] = BackendOption{k, value};
55+
}
56+
}
57+
58+
// Type-safe getters
59+
template <typename T>
60+
Error get_option(OptionKey<T> key, T& out) const {
61+
const char* k = key.key;
62+
for (size_t i = 0; i < size_; ++i) {
63+
if (strcmp(options_[i].key, k) == 0) {
64+
if (auto* val = std::get_if<T>(&options_[i].value)) {
65+
out = *val;
66+
return Error::Ok;
67+
}
68+
return Error::InvalidArgument;
69+
}
70+
}
71+
return Error::NotFound;
72+
}
73+
74+
private:
75+
BackendOption options_[MaxCapacity]{}; // Storage for backend options
76+
size_t size_; // Current number of options
77+
};
78+
79+
// Helper functions for creating typed option keys (unchanged)
80+
constexpr OptionKey<bool> BoolKey(const char* k) {
81+
return OptionKey<bool>(k);
82+
}
83+
84+
constexpr OptionKey<int> IntKey(const char* k) {
85+
return OptionKey<int>(k);
86+
}
87+
88+
constexpr OptionKey<const char*> StrKey(const char* k) {
89+
return OptionKey<const char*>(k);
90+
}
91+
92+
} // namespace runtime
93+
} // namespace executorch

runtime/backend/interface.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
#include <executorch/runtime/backend/backend_execution_context.h>
1414
#include <executorch/runtime/backend/backend_init_context.h>
15-
#include <executorch/runtime/backend/backend_update_context.h>
1615
#include <executorch/runtime/backend/backend_options.h>
16+
#include <executorch/runtime/backend/backend_update_context.h>
1717
#include <executorch/runtime/core/array_ref.h>
1818
#include <executorch/runtime/core/error.h>
1919
#include <executorch/runtime/core/evalue.h>
@@ -102,18 +102,35 @@ class BackendInterface {
102102
EValue** args) const = 0;
103103

104104
/**
105-
* Responsible update the backend status, if any. The backend options are passed in
106-
* by users, and the backend can update its internal status based on the options.
105+
* Responsible update the backend status, if any. The backend options are
106+
* passed in by users, and the backend can update its internal status based on
107+
* the options.
107108
*
108109
* @param[in] context Runtime context if any. Currently it's not used.
109110
* @param[in] args A list of BackendOptions passed in by users.
110111
* @retval Error::Ok if successful.
111112
*/
112-
ET_NODISCARD virtual Error update(
113-
BackendUpdateContext& context,
114-
const executorch::runtime::ArrayRef<BackendOption>& backend_options) const {
115-
return Error::Ok;
116-
};
113+
ET_NODISCARD virtual Error set_option(
114+
BackendUpdateContext& context,
115+
const executorch::runtime::Span<BackendOption>& backend_options) {
116+
return Error::Ok;
117+
};
118+
119+
/**
120+
* Responsible update the backend status, if any. The backend options are
121+
* passed in by users, and the backend can update its internal status based on
122+
* the options.
123+
*
124+
* @param[in] context Runtime context if any. Currently it's not used.
125+
* @param[in] args A list of BackendOptions passed in by users, that will be
126+
* filled by the backend
127+
* @retval Error::Ok if successful.
128+
*/
129+
ET_NODISCARD virtual Error get_option(
130+
BackendUpdateContext& context,
131+
executorch::runtime::Span<BackendOption>& backend_options) {
132+
return Error::Ok;
133+
};
117134

118135
/**
119136
* Responsible for destroying a handle, if it's required for some backend.

runtime/backend/test/backend_interface_update_test.cpp

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ class MockBackend : public BackendInterface {
5454
return Error::Ok;
5555
}
5656

57-
Error update(
57+
Error set_option(
5858
BackendUpdateContext& context,
59-
const executorch::runtime::ArrayRef<BackendOption>& backend_options) const override {
60-
update_count++;
59+
const executorch::runtime::Span<BackendOption>& backend_options) override {
60+
set_option_count++;
6161
int sucess_update = 0;
6262
for (const auto& backend_option : backend_options) {
6363
if (strcmp(backend_option.key, "Backend") == 0) {
@@ -92,7 +92,7 @@ class MockBackend : public BackendInterface {
9292
// State tracking
9393
mutable bool init_called = false;
9494
mutable int execute_count = 0;
95-
mutable int update_count = 0;
95+
mutable int set_option_count = 0;
9696
};
9797

9898
class BackendInterfaceUpdateTest : public ::testing::Test {
@@ -119,7 +119,8 @@ TEST_F(BackendInterfaceUpdateTest, HandlesInvalidOption) {
119119
"None"
120120
};
121121

122-
Error err = mock_backend->update(context, invalid_option);
122+
// Create a span from the single option
123+
Error err = mock_backend->set_option(context, invalid_option);
123124
EXPECT_EQ(err, Error::InvalidArgument);
124125

125126
}
@@ -132,7 +133,7 @@ TEST_F(BackendInterfaceUpdateTest, HandlesInvalidOption) {
132133
EXPECT_EQ(mock_backend->target_backend, std::nullopt);
133134

134135
// Test successful update
135-
Error err = mock_backend->update(context, options.view());
136+
Error err = mock_backend->set_option(context, options.view());
136137
EXPECT_EQ(err, Error::Ok);
137138

138139
EXPECT_EQ(mock_backend->target_backend, "GPU");
@@ -150,7 +151,7 @@ TEST_F(BackendInterfaceUpdateTest, HandlesIntOption) {
150151
options.set_option(IntKey("NumberOfThreads"), expected_num_threads);
151152

152153
// Test successful update
153-
Error err = mock_backend->update(context, options.view());
154+
Error err = mock_backend->set_option(context, options.view());
154155
EXPECT_EQ(err, Error::Ok);
155156
EXPECT_EQ(mock_backend->num_threads, expected_num_threads);
156157
}
@@ -164,7 +165,7 @@ TEST_F(BackendInterfaceUpdateTest, HandlesBoolOption) {
164165
options.set_option(BoolKey("Debug"), true);
165166

166167
// Test successful update
167-
Error err = mock_backend->update(context, options.view());
168+
Error err = mock_backend->set_option(context, options.view());
168169
EXPECT_EQ(err, Error::Ok);
169170

170171
EXPECT_EQ(mock_backend->debug, true);
@@ -181,7 +182,7 @@ TEST_F(BackendInterfaceUpdateTest, HandlesMultipleOptions) {
181182
options.set_option(StrKey("Backend"), "GPU");
182183

183184
// Test successful update
184-
Error err = mock_backend->update(context, options.view());
185+
Error err = mock_backend->set_option(context, options.view());
185186
EXPECT_EQ(err, Error::Ok);
186187

187188
EXPECT_EQ(mock_backend->debug, true);
@@ -199,7 +200,7 @@ TEST_F(BackendInterfaceUpdateTest, UpdateBeforeInit) {
199200
options.set_option(StrKey("Backend"), "GPU");
200201

201202
// Update before init
202-
Error err = mock_backend->update(update_context, options.view());
203+
Error err = mock_backend->set_option(update_context, options.view());
203204
EXPECT_EQ(err, Error::Ok);
204205

205206
// Now call init
@@ -210,7 +211,7 @@ TEST_F(BackendInterfaceUpdateTest, UpdateBeforeInit) {
210211

211212
// Verify state
212213
EXPECT_TRUE(mock_backend->init_called);
213-
EXPECT_EQ(mock_backend->update_count, 1);
214+
EXPECT_EQ(mock_backend->set_option_count, 1);
214215
EXPECT_EQ(mock_backend->execute_count, 0);
215216
ASSERT_TRUE(mock_backend->target_backend.has_value());
216217
EXPECT_STREQ(mock_backend->target_backend.value().c_str(), "GPU");
@@ -234,7 +235,7 @@ TEST_F(BackendInterfaceUpdateTest, UpdateAfterInitBeforeExecute) {
234235

235236
// Now update
236237
options.set_option(StrKey("Backend"), "CPU");
237-
Error err = mock_backend->update(update_context, options.view());
238+
Error err = mock_backend->set_option(update_context, options.view());
238239
EXPECT_EQ(err, Error::Ok);
239240

240241
// Now execute
@@ -244,7 +245,7 @@ TEST_F(BackendInterfaceUpdateTest, UpdateAfterInitBeforeExecute) {
244245
EXPECT_EQ(err, Error::Ok);
245246

246247
// Verify state
247-
EXPECT_EQ(mock_backend->update_count, 1);
248+
EXPECT_EQ(mock_backend->set_option_count, 1);
248249
EXPECT_EQ(mock_backend->execute_count, 1);
249250
ASSERT_TRUE(mock_backend->target_backend.has_value());
250251
EXPECT_STREQ(mock_backend->target_backend.value().c_str(), "CPU");
@@ -270,15 +271,15 @@ TEST_F(BackendInterfaceUpdateTest, UpdateBetweenExecutes) {
270271

271272
// Update between executes
272273
options.set_option(StrKey("Backend"), "NPU");
273-
err = mock_backend->update(update_context, options.view());
274+
err = mock_backend->set_option(update_context, options.view());
274275
EXPECT_EQ(err, Error::Ok);
275276

276277
// Second execute
277278
err = mock_backend->execute(execute_context, handle, args);
278279
EXPECT_EQ(err, Error::Ok);
279280

280281
// Verify state
281-
EXPECT_EQ(mock_backend->update_count, 1);
282+
EXPECT_EQ(mock_backend->set_option_count, 1);
282283
EXPECT_EQ(mock_backend->execute_count, 2);
283284
ASSERT_TRUE(mock_backend->target_backend.has_value());
284285
EXPECT_STREQ(mock_backend->target_backend.value().c_str(), "NPU");

runtime/core/span.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class Span final {
5555
template <size_t N>
5656
/* implicit */ constexpr Span(T (&Arr)[N]) : data_(Arr), length_(N) {}
5757

58+
/// Construct a Span from a single element reference.
59+
/* implicit */ constexpr Span(T& single_element) : data_(&single_element), length_(1) {}
60+
5861
/// @returns a pointer to the start of the underlying element buffer.
5962
iterator begin() const noexcept {
6063
return data_;

0 commit comments

Comments
 (0)