Skip to content

[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

Open
wants to merge 7 commits into
base: gh/cccclai/21/base
Choose a base branch
from
Open

Conversation

cccclai
Copy link
Contributor

@cccclai cccclai commented Jun 5, 2025

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

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]
Copy link

pytorch-bot bot commented Jun 5, 2025

🔗 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.

✅ No Failures

As of commit 2fda6a1 with merge base 57e0765 (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

cccclai added a commit that referenced this pull request Jun 5, 2025
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
@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 5, 2025
@facebook-github-bot
Copy link
Contributor

This pull request was exported from Phabricator. Differential Revision: D75993712

Copy link

github-actions bot commented Jun 5, 2025

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@cccclai
Copy link
Contributor Author

cccclai commented Jun 5, 2025

Previous comment in the old PR: #11288

From @JacobSzwejbka :

use int64_t for int type.

@cccclai
Copy link
Contributor Author

cccclai commented Jun 5, 2025

From @mergennachin:

I don't think we need to define a generic BackendOption that takes arbitrary key value. Each backend owners could define their options instead. Vulkan will define its own config structure, Core ML will define its own config structure. Wdyt?

@JacobSzwejbka, @mcr229, @kimishpatel

Something like this:

BackendInterface { // parent interface
  virtual Error setOption(const BackendOptions& options) = 0;
}

struct BackendOptions {  // Empty parent struct
  virtual ~BackendOptions() = default;
};

Each individual backends define their own configs

struct VulkanBackendOptions : public BackendOptions {
  const char* metadata[20] = "";
  int thread_count = 1;
  // ... other Vulkan-specific options
};

VulkanBackend extends BackendInterface {
  Error setOption(const BackendOptions& options) override {
    auto* vulkan_opts = dynamic_cast<const VulkanBackendOptions*>(&options);
  }
}

// User code

VulkanBackendOptions opts;
opts.thread_count = 5;
auto backend = std::make_unique<VulkanBackend>();
backend->setOption(opts);  // 

@cccclai
Copy link
Contributor Author

cccclai commented Jun 5, 2025

My response:

I thought about this idea at the beginning, but it means that users code needs to link to a specific backend, otherwise the runner code will fail to build. Currently the same user code can be backend agnostic, meaning the same code can still run with different backends with/without linking the backend. As the example, the portable runner can work with/without xnnpack https://github.com/pytorch/executorch/blob/main/examples/xnnpack/executor_runner/targets.bzl.

It also exposes the backend specific symbol to users, and the contract interface is a bit looser. Previously, The contract is like backend <-> ET <-> backend, and if we change it to this, it can be user <-> backend. And users can pass arbitrary things to backend. Users can run backend->init, backend->execute in their own code too, and it will be out of our control.

With the proposed solution (#10216), the user code can still be backend agnostic, and the backend option is still optional. We are still on the hook of the contract.

@kimishpatel
Copy link
Contributor

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 update (I would call set_backend_options) method that will set all the options. But part that is not clear to me is how would user indicate which backend they setting this option for. So somehow we need a string identifer for the backends that can be used to figure out which backend's update method be called. @mergennachin in this case we cannot quite have what you proposed. Imagine you have a pointer to an instance of VulkanBackendOptions. You pass this to runtime's update method which accepts BackendOptions*. Eventually you want to call VulkanBackend's update options method so you need to cast BackendOptions* to VulkanBackendOptions*. You have two choices either do static_cast or dynamic_cast. The later requires rtti which we dont allow. For static_cast, it has to be safe and you need to know that BackendOption* is actually pointing to VulkanBackendOption. To do that you need to have BackendOptions store tag containing what is the type of the derived class. If we do that, then what you suggest can work.

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]
@facebook-github-bot
Copy link
Contributor

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]
@facebook-github-bot
Copy link
Contributor

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]
@facebook-github-bot
Copy link
Contributor

This pull request was exported from Phabricator. Differential Revision: D75993712

namespace runtime {

// Strongly-typed option key template
template <typename T>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this class doesnt need to be templated. No use of T

Comment on lines 31 to 32
OptionValue
value; // value is the value of the backend option, like 4, true, etc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format?

using OptionValue = std::variant<bool, int, const char*>;

struct BackendOption {
const char* key; // key is the name of the backend option, like num_threads,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit concerned about storing pointer to the key. Users may do things like

std::string my_key("my_key");
BackendOption().set_option<int>(my_key.c_str(), 10);

When that goes out of scope we have a problem. I suggest instead use something like const char[kMaxKeyLength] key

// enable_profiling, etc
OptionValue
value; // value is the value of the backend option, like 4, true, etc
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other thing I realize is why do we need BackendOption as a separate struct?

Can we not just have BackendOptions class instantiate array of keys and values? The interface there is set_option with key name and value, get_option using key name. User never really needs to get BackendOption

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users don't need get backend option, but the dispatcher needs it and dispatch to the actual backend.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually you're right. The backend options map is for dispatching. and each backend will get an array ref of backend options.


// Type-safe setters
template <typename T>
void set_option(OptionKey<T> key, T value) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bassed on suggestion above

Suggested change
void set_option(OptionKey<T> key, T value) {
void set_option(const char [kMaxKeyLength]& key, T value) {

Comment on lines 53 to 55
if (size_ < MaxCapacity) {
options_[size_++] = BackendOption{k, value};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set error if no capacity

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good point. Will update

Comment on lines 79 to 90
// Helper functions for creating typed option keys (unchanged)
constexpr OptionKey<bool> BoolKey(const char* k) {
return OptionKey<bool>(k);
}

constexpr OptionKey<int> IntKey(const char* k) {
return OptionKey<int>(k);
}

constexpr OptionKey<const char*> StrKey(const char* k) {
return OptionKey<const char*>(k);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont think you need these

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just helper functions to simplify users' code.

Copy link
Contributor

@kimishpatel kimishpatel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made some suggestions

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]
@facebook-github-bot
Copy link
Contributor

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]
@facebook-github-bot
Copy link
Contributor

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]
@facebook-github-bot
Copy link
Contributor

This pull request was exported from Phabricator. Differential Revision: D75993712

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. fb-exported
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants