-
Notifications
You must be signed in to change notification settings - Fork 602
[1/N] Add BackendOptions class #11389
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
base: gh/cccclai/21/base
Are you sure you want to change the base?
Conversation
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) [ghstack-poisoned]
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/11389
Note: Links to docs will display an error until the docs builds have been completed. ✅ You can merge normally! (3 Unrelated Failures)As of commit 943fe96 with merge base 608a745 ( FLAKY - The following jobs failed but were likely due to flakiness present on trunk:
BROKEN TRUNK - The following job failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) ghstack-source-id: 288273758 Pull Request resolved: #11389
This pull request was exported from Phabricator. Differential Revision: D75993712 |
This PR needs a
|
Previous comment in the old PR: #11288 From @JacobSzwejbka :
|
From @mergennachin:
|
My response:
|
I think what @mergennachin suggested makes sense. If user code is setting backend specific option they already have to know what backend they are talking about, unless some options are backend agnostic. Other piece of the puzzle is how the user will update this. And from #10216 it seems that we will pipe |
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) [ghstack-poisoned]
This pull request was exported from Phabricator. Differential Revision: D75993712 |
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) [ghstack-poisoned]
This pull request was exported from Phabricator. Differential Revision: D75993712 |
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) [ghstack-poisoned]
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712) [ghstack-poisoned]
This pull request was exported from Phabricator. Differential Revision: D75993712 |
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712) [ghstack-poisoned]
This pull request was exported from Phabricator. Differential Revision: D75993712 |
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712) [ghstack-poisoned]
This pull request was exported from Phabricator. Differential Revision: D75993712 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See inline
|
||
// Union replaced with std::variant | ||
using OptionValue = std::variant<bool, int, const char*>; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document the safety and lifecycle of string types
All string keys and values must have static storage duration (string literals, static const char arrays, or global constants). The BackendOptions class does NOT take ownership of strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the actual value itself, I think we should do this where std::variant itself holds array of chars rather than pointer to char. Like this https://godbolt.org/z/odTc9ex96
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
runtime/backend/options.h
Outdated
constexpr OptionKey<const char*> StrKey(const char* k) { | ||
return OptionKey<const char*>(k); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you force to accept only string literals with this?
template <size_t N>
constexpr OptionKey<const char*> StrKey(const char (&literal)[N]) {
return OptionKey<const char*>(literal);
}
runtime/backend/options.h
Outdated
|
||
// Type-safe setters | ||
template <typename T> | ||
void set_option(OptionKey<T> key, T value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename this to set_option_impl and make it a private function. And add type safe public APIs to enforce key is always safe string... And value is also safe in the case of string.
// String options - only accepts string literals/arrays
template <size_t KeyN, size_t ValueN>
Error set_option(
const char (&key)[KeyN],
const char (&value)[ValueN]
) noexcept {
return set_option_impl(key, static_cast<const char*>(value));
}
// Non-string options
template <size_t N>
Error set_option(const char (&key)[N], bool value) noexcept {
return set_option_impl(key, value);
}
template <size_t N>
Error set_option(const char (&key)[N], int value) noexcept {
return set_option_impl(key, value);
}
runtime/backend/options.h
Outdated
|
||
// Strongly-typed option key template | ||
template <typename T> | ||
struct OptionKey { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think we need OptionKey as a separate class. Just use const char *
in the BackendOption
class.
However, the thing that worries me the most is by owning const char*
we are giving users a footgun. They can allocate pointer to the backend option name on stack, pass the pointer around and it will go out-of-scope. If this results in segfault that might be your best option. Worst can lead to undefined behaviors.
I would suggest that we just store const char[kMaxLegthOfBackendOptionName]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You resolved this comment but I dont see it being addressed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you addressed but kept OptionKey
. please remove
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712) [ghstack-poisoned]
This pull request was exported from Phabricator. Differential Revision: D75993712 |
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712) [ghstack-poisoned]
@kimishpatel @mergennachin thanks for the feedback! I've addressed most of them. If there is anything I miss, let me know |
This pull request was exported from Phabricator. Differential Revision: D75993712 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See inline, approving but inline comments should be addressed
runtime/backend/options.h
Outdated
*/ | ||
template <size_t N> | ||
Error set_option(const char (&key)[N], bool value) noexcept { | ||
ET_CHECK_MSG(N <= kMaxOptionKeyLength, "Option key is too long"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static_assert(N <= kMaxOptionKeyLength, "Option key is too long");
for compile time check, as opposed to runtime check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is there KeyLen here? Can it not be just max length? Like why are we templating this on keylength? If it is to allow for users to construct const char[10]
vs. const char [5]
, I would say just make recommended use via const char[kMaxOptionKeyLength]
? Feels like that is simler
runtime/backend/options.h
Outdated
*/ | ||
template <size_t N> | ||
Error set_option(const char (&key)[N], int value) noexcept { | ||
ET_CHECK_MSG(N <= kMaxOptionKeyLength, "Option key is too long"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.
runtime/backend/options.h
Outdated
*/ | ||
template <size_t N> | ||
Error set_option(const char (&key)[N], const char* value) noexcept { | ||
ET_CHECK_MSG(N <= kMaxOptionKeyLength, "Option key is too long"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.
runtime/backend/options.h
Outdated
strncpy(new_option.key, key, kMaxOptionKeyLength - 1); | ||
new_option.key[kMaxOptionKeyLength - 1] = '\0'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const size_t key_len = std::strlen(key);
const size_t copy_len = std::min(key_len, kMaxOptionKeyLength - 1);
std::memcpy(new_option.key, key, copy_len);
new_option.key[copy_len] = '\0';
runtime/backend/options.h
Outdated
* | ||
* @return A const Span containing all BackendOption entries | ||
*/ | ||
executorch::runtime::Span<BackendOption> view() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
executorch::runtime::Span<const BackendOption> view() const
* Default constructor - initializes with zero options. | ||
*/ | ||
BackendOptions() : size_(0) {} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potentially have this
/**
* Copy constructor
*/
BackendOptions(const BackendOptions& other) : size_(other.size_) {
for (size_t i = 0; i < size_; ++i) {
options_[i] = other.options_[i];
}
}
/**
* Copy assignment operator
*/
BackendOptions& operator=(const BackendOptions& other) {
if (this != &other) {
size_ = other.size_;
for (size_t i = 0; i < size_; ++i) {
options_[i] = other.options_[i];
}
}
return *this;
}
@mergennachin I am concerned about lifecycle and potential footgun it introduces when setting string values. So I would like to resolve that before we land |
Can we just always require string value to be a string literals, static const char arrays, or global constants in the API like this? |
runtime/backend/options.h
Outdated
* | ||
* @return A mutable Span containing all BackendOption entries | ||
*/ | ||
executorch::runtime::Span<BackendOption> view() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe make it explicit that this is a mutable view.
* exist, Error::InvalidArgument if type doesn't match | ||
*/ | ||
template <typename T, size_t KeyLen> | ||
Error get_option(const char (&key)[KeyLen], T& out) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as before on keylen
// Integer get should succeed with correct value | ||
EXPECT_EQ(options.get_option("flag", ival), Error::Ok); | ||
EXPECT_EQ(ival, 123); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add tests for mutability of the options now that you have mutable view
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should make option itself have char array instead of char*
Yeah those are fine options. I just want API to make it clear, like what you suggested in the comment (although I think length specifier, for option and value, being template param seems a bit weird. Just have a max longest length as some const) |
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. Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712/) Differential Revision: [D75993712](https://our.internmc.facebook.com/intern/diff/D75993712) [ghstack-poisoned]
This pull request was exported from Phabricator. Differential Revision: D75993712 |
New comments are addressed, let me know your thoughts |
* @return Error::Ok on success, Error::InvalidArgument if storage is full | ||
*/ | ||
template <size_t N> | ||
Error set_option(const char (&key)[N], bool value) noexcept { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need N? It will just create N different methods for many possible lengths. My suggestion is to remove N as template param and expect callsites to be something like
char my_option_name[kMaxOptionKeyLength] = "use_gpu";
...set_option(my_option_name, True)
// Create a fixed-size array and copy the string | ||
std::array<char, kMaxOptionValueLength> arr; | ||
strncpy(arr.data(), value, kMaxOptionValueLength - 1); | ||
arr[kMaxOptionValueLength - 1] = '\0'; // Ensure null termination |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the suggestion I mentioned above will also not require you to do this. ALthough its not a bit deal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I still think we should not need N or KeyLen template params. So I would suggest to fix that.
Stack from ghstack (oldest at bottom):
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.
Differential Revision: D75993712
Differential Revision: D75993712