Skip to content

Commit 4a34bc4

Browse files
authored
Add a backend option + runtime spec for weight cache, refactor option handling
Differential Revision: D96879876 Pull Request resolved: pytorch#18253
1 parent bb8318d commit 4a34bc4

11 files changed

Lines changed: 472 additions & 81 deletions

File tree

backends/xnnpack/runtime/XNNExecutor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ class XNNExecutor {
5353
return packed_data_names_;
5454
}
5555

56+
inline bool uses_weight_cache() const {
57+
return !packed_data_names_.empty();
58+
}
59+
5660
inline std::shared_ptr<XNNWorkspace> get_workspace() {
5761
return workspace_;
5862
}

backends/xnnpack/runtime/XNNPACKBackend.cpp

Lines changed: 35 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include <executorch/backends/xnnpack/runtime/XNNPACKBackend.h>
1111
#include <executorch/backends/xnnpack/runtime/XNNWeightsCache.h>
1212
#include <executorch/backends/xnnpack/runtime/XNNWorkspace.h>
13-
#include <executorch/backends/xnnpack/runtime/XNNWorkspaceManager.h>
13+
#include <executorch/backends/xnnpack/runtime/XnnpackBackendOptions.h>
1414
#include <executorch/runtime/backend/interface.h>
1515
#include <executorch/runtime/core/error.h>
1616
#include <executorch/runtime/core/evalue.h>
@@ -24,7 +24,6 @@
2424
namespace executorch {
2525
namespace backends {
2626

27-
using executorch::backends::xnnpack::WorkspaceSharingMode;
2827
using executorch::backends::xnnpack::XNNWorkspace;
2928
using executorch::backends::xnnpack::delegate::XNNWeightsCache;
3029
using executorch::ET_RUNTIME_NAMESPACE::Backend;
@@ -57,9 +56,6 @@ class XnnpackBackend final
5756
(unsigned int)status);
5857
return;
5958
}
60-
61-
// Workspace manager is initialized with the appropriate default mode in its
62-
// constructor
6359
}
6460

6561
bool is_available() const override {
@@ -82,17 +78,24 @@ class XnnpackBackend final
8278

8379
auto program_id =
8480
reinterpret_cast<uintptr_t>(context.get_runtime_allocator());
85-
auto workspace_result = get_or_create_workspace(program_id);
81+
auto sharing_mode_result = options_.resolve_sharing_mode(context);
82+
if (!sharing_mode_result.ok()) {
83+
return sharing_mode_result.error();
84+
}
85+
auto workspace_result =
86+
options_.workspace_manager().get_or_create_workspace(
87+
program_id, sharing_mode_result.get());
8688
if (!workspace_result.ok()) {
8789
return workspace_result.error();
8890
}
8991
auto workspace = workspace_result.get();
9092

91-
#ifdef ENABLE_XNNPACK_WEIGHTS_CACHE
92-
const std::lock_guard<std::mutex> lock_weight_cache(weights_cache_mutex_);
93-
weights_cache_->initialize_for_runtime(
94-
context.get_runtime_allocator(), named_data_map);
95-
#endif
93+
bool use_weight_cache = options_.resolve_weight_cache(context);
94+
if (use_weight_cache) {
95+
const std::lock_guard<std::mutex> lock_weight_cache(weights_cache_mutex_);
96+
weights_cache_->initialize_for_runtime(
97+
context.get_runtime_allocator(), named_data_map);
98+
}
9699

97100
auto [workspace_lock, workspace_ptr] = workspace->acquire();
98101

@@ -129,9 +132,11 @@ class XnnpackBackend final
129132
Span<EValue*> args) const override {
130133
auto executor = static_cast<xnnpack::delegate::XNNExecutor*>(handle);
131134

132-
#ifdef ENABLE_XNNPACK_WEIGHTS_CACHE
133-
const std::lock_guard<std::mutex> lock_weights_cache(weights_cache_mutex_);
134-
#endif
135+
std::unique_lock<std::mutex> lock_weights_cache(
136+
weights_cache_mutex_, std::defer_lock);
137+
if (executor->uses_weight_cache()) {
138+
lock_weights_cache.lock();
139+
}
135140

136141
auto [raii_lock, _] = executor->get_workspace()->acquire();
137142

@@ -161,11 +166,11 @@ class XnnpackBackend final
161166
executor->print_avg_op_timings();
162167
#endif
163168

164-
#ifdef ENABLE_XNNPACK_WEIGHTS_CACHE
165-
const std::lock_guard<std::mutex> lock_weights_cache(
166-
weights_cache_mutex_);
167-
weights_cache_->delete_packed_data(executor->get_packed_data_names());
168-
#endif
169+
if (executor->uses_weight_cache()) {
170+
const std::lock_guard<std::mutex> lock_weights_cache(
171+
weights_cache_mutex_);
172+
weights_cache_->delete_packed_data(executor->get_packed_data_names());
173+
}
169174

170175
// This is needed to serialize access to xnn_delete_runtime which is not
171176
// thread safe. This can heppen when multiple threads call destroy() on
@@ -181,72 +186,32 @@ class XnnpackBackend final
181186
}
182187
}
183188

184-
Error get_option_internal(
189+
Error get_option(
185190
BackendOptionContext& context,
186-
executorch::runtime::Span<executorch::runtime::BackendOption>&
187-
backend_options) const {
188-
// Intentionally not locking here as it is not required.
189-
190-
// Verify that the expected option key is present and modify the value
191+
Span<BackendOption>& backend_options) override {
191192
for (size_t i = 0; i < backend_options.size(); ++i) {
192-
if (strcmp(
193-
backend_options[i].key,
194-
xnnpack::workspace_sharing_mode_option_key) == 0) {
195-
// Set the value to what was stored by set_option
196-
backend_options[i].value =
197-
static_cast<int>(workspace_manager_.get_sharing_mode());
193+
Error err = options_.get_option(backend_options[i]);
194+
if (err != Error::Ok) {
195+
return err;
198196
}
199197
}
200-
201198
return Error::Ok;
202199
}
203200

204-
Error get_option(
205-
BackendOptionContext& context,
206-
executorch::runtime::Span<executorch::runtime::BackendOption>&
207-
backend_options) override {
208-
return get_option_internal(context, backend_options);
209-
}
210-
211201
Error set_option(
212202
BackendOptionContext& context,
213-
const executorch::runtime::Span<executorch::runtime::BackendOption>&
214-
backend_options) override {
215-
if (backend_options.size() > 0) {
216-
for (const auto& option : backend_options) {
217-
if (strcmp(option.key, xnnpack::workspace_sharing_mode_option_key) ==
218-
0) {
219-
if (auto* val = std::get_if<int>(&option.value)) {
220-
if (*val < 0 ||
221-
*val > static_cast<int>(WorkspaceSharingMode::Count)) {
222-
ET_LOG(
223-
Error,
224-
"XNNPACK workspace sharing mode must be between 0 and %d, inclusive, but was %d.",
225-
static_cast<int>(WorkspaceSharingMode::Count),
226-
*val);
227-
return Error::InvalidArgument;
228-
}
229-
230-
ET_LOG(
231-
Debug, "Setting XNNPACK workspace sharing mode to %d.", *val);
232-
auto status = workspace_manager_.set_sharing_mode(
233-
static_cast<WorkspaceSharingMode>(*val));
234-
if (status != Error::Ok) {
235-
return status;
236-
}
237-
} else {
238-
ET_LOG(Error, "XNNPACK workspace sharing mode must be an integer.");
239-
return Error::InvalidArgument;
240-
}
241-
}
203+
const Span<BackendOption>& backend_options) override {
204+
for (const auto& option : backend_options) {
205+
Error err = options_.set_option(option);
206+
if (err != Error::Ok) {
207+
return err;
242208
}
243209
}
244210
return Error::Ok;
245211
}
246212

247213
private:
248-
// Workspace manager for handling workspace sharing modes
249-
mutable xnnpack::XNNWorkspaceManager workspace_manager_;
214+
mutable xnnpack::XnnpackBackendOptions options_;
250215

251216
// Weights cache is global to all delegate instances.
252217
mutable std::mutex weights_cache_mutex_;
@@ -257,13 +222,6 @@ class XnnpackBackend final
257222
// weights_cache_mutex_
258223
// workspace_meta_mutex_
259224
// workspace_mutex_ (owned by executor)
260-
261-
// Retrieve a workspace for the given method ID, depending on the sharing
262-
// mode.
263-
Result<std::shared_ptr<XNNWorkspace>> get_or_create_workspace(
264-
uintptr_t program_id) const {
265-
return workspace_manager_.get_or_create_workspace(program_id);
266-
}
267225
};
268226

269227
namespace {

backends/xnnpack/runtime/XNNPACKBackend.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ const char xnnpack_backend_key[] = "XnnpackBackend";
99
/// for a description of the associated functionality.
1010
const char workspace_sharing_mode_option_key[] = "workspace_sharing_mode";
1111

12+
/// The key for the weight cache option. When enabled, packed weights are shared
13+
// across delegate instances. Changes only affect subsequently loaded models.
14+
const char weight_cache_option_key[] = "weight_cache_enabled";
15+
1216
/// Workspace sharing mode. This is a backend option that can be set via the
1317
/// set_option API to control memory sharing between CALL_DELEGATE instances.
1418
/// This is useful for reducing memory consumption.

backends/xnnpack/runtime/XNNWorkspaceManager.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,14 @@ WorkspaceSharingMode XNNWorkspaceManager::get_sharing_mode() const {
4646

4747
Result<std::shared_ptr<XNNWorkspace>>
4848
XNNWorkspaceManager::get_or_create_workspace(uintptr_t program_id) const {
49-
auto mode = sharing_mode_.load();
49+
return get_or_create_workspace(program_id, sharing_mode_.load());
50+
}
5051

51-
// Get or create the workspace according to the current sharing mode.
52+
Result<std::shared_ptr<XNNWorkspace>>
53+
XNNWorkspaceManager::get_or_create_workspace(
54+
uintptr_t program_id,
55+
WorkspaceSharingMode mode) const {
56+
// Get or create the workspace according to the specified sharing mode.
5257
if (mode == WorkspaceSharingMode::Disabled) {
5358
ET_LOG(Debug, "Instantiating workspace.");
5459
auto create_result = XNNWorkspace::create();
@@ -121,8 +126,7 @@ XNNWorkspaceManager::get_or_create_model_workspace(uintptr_t program_id) const {
121126
"Created workspace %p for program %" PRIuPTR ".",
122127
workspace->unsafe_get_workspace(),
123128
program_id);
124-
model_workspaces_.insert(
125-
{program_id, std::weak_ptr<XNNWorkspace>(workspace)});
129+
model_workspaces_[program_id] = workspace;
126130
}
127131

128132
return workspace;

backends/xnnpack/runtime/XNNWorkspaceManager.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ class XNNWorkspaceManager {
5858
runtime::Result<std::shared_ptr<XNNWorkspace>> get_or_create_workspace(
5959
uintptr_t program_id) const;
6060

61+
/**
62+
* Retrieve a workspace for the given program ID, using the specified sharing
63+
* mode instead of the stored mode. A workspace will be created if needed.
64+
*
65+
* @param program_id The ID of the program requesting a workspace.
66+
* @param mode The workspace sharing mode to use.
67+
* @return A Result containing a shared_ptr to the workspace, or an error.
68+
*/
69+
runtime::Result<std::shared_ptr<XNNWorkspace>> get_or_create_workspace(
70+
uintptr_t program_id,
71+
WorkspaceSharingMode mode) const;
72+
6173
private:
6274
// The active sharing mode. Changes to this affect only models loaded after
6375
// the change.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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/backends/xnnpack/runtime/XnnpackBackendOptions.h>
10+
11+
#include <cstring>
12+
13+
namespace executorch::backends::xnnpack {
14+
15+
using executorch::runtime::BackendOption;
16+
using executorch::runtime::Error;
17+
18+
namespace {
19+
20+
// Resolve an option value, preferring the runtime spec override if present.
21+
template <typename T>
22+
T resolve_option(
23+
const ET_RUNTIME_NAMESPACE::BackendInitContext& context,
24+
const char* key,
25+
T global_default) {
26+
auto spec = context.get_runtime_spec<T>(key);
27+
if (spec.ok()) {
28+
return spec.get();
29+
}
30+
return global_default;
31+
}
32+
33+
} // namespace
34+
35+
Error XnnpackBackendOptions::get_option(BackendOption& option) const {
36+
if (strcmp(option.key, workspace_sharing_mode_option_key) == 0) {
37+
option.value = static_cast<int>(sharing_mode_.load());
38+
} else if (strcmp(option.key, weight_cache_option_key) == 0) {
39+
option.value = weight_cache_enabled_.load();
40+
}
41+
return Error::Ok;
42+
}
43+
44+
Error XnnpackBackendOptions::set_option(const BackendOption& option) {
45+
if (strcmp(option.key, workspace_sharing_mode_option_key) == 0) {
46+
auto* val = std::get_if<int>(&option.value);
47+
if (!val) {
48+
ET_LOG(Error, "XNNPACK workspace sharing mode must be an integer.");
49+
return Error::InvalidArgument;
50+
}
51+
if (*val < 0 || *val >= static_cast<int>(WorkspaceSharingMode::Count)) {
52+
ET_LOG(
53+
Error,
54+
"XNNPACK workspace sharing mode must be between 0 and %d, inclusive, but was %d.",
55+
static_cast<int>(WorkspaceSharingMode::Count) - 1,
56+
*val);
57+
return Error::InvalidArgument;
58+
}
59+
ET_LOG(Debug, "Setting XNNPACK workspace sharing mode to %d.", *val);
60+
sharing_mode_.store(static_cast<WorkspaceSharingMode>(*val));
61+
} else if (strcmp(option.key, weight_cache_option_key) == 0) {
62+
auto* val = std::get_if<bool>(&option.value);
63+
if (!val) {
64+
ET_LOG(Error, "XNNPACK weight cache enabled must be a bool.");
65+
return Error::InvalidArgument;
66+
}
67+
ET_LOG(Debug, "Setting XNNPACK weight cache enabled to %d.", *val);
68+
weight_cache_enabled_.store(*val);
69+
}
70+
return Error::Ok;
71+
}
72+
73+
bool XnnpackBackendOptions::resolve_weight_cache(
74+
const ET_RUNTIME_NAMESPACE::BackendInitContext& context) const {
75+
return resolve_option<bool>(
76+
context, weight_cache_option_key, weight_cache_enabled_.load());
77+
}
78+
79+
runtime::Result<WorkspaceSharingMode>
80+
XnnpackBackendOptions::resolve_sharing_mode(
81+
const ET_RUNTIME_NAMESPACE::BackendInitContext& context) const {
82+
auto global_mode = sharing_mode_.load();
83+
int raw_mode = resolve_option<int>(
84+
context,
85+
workspace_sharing_mode_option_key,
86+
static_cast<int>(global_mode));
87+
if (raw_mode < 0 ||
88+
raw_mode >= static_cast<int>(WorkspaceSharingMode::Count)) {
89+
ET_LOG(
90+
Error,
91+
"XNNPACK workspace sharing mode must be between 0 and %d, inclusive, but was %d.",
92+
static_cast<int>(WorkspaceSharingMode::Count) - 1,
93+
raw_mode);
94+
return runtime::Error::InvalidArgument;
95+
}
96+
return static_cast<WorkspaceSharingMode>(raw_mode);
97+
}
98+
99+
WorkspaceSharingMode XnnpackBackendOptions::get_sharing_mode() const {
100+
return sharing_mode_.load();
101+
}
102+
103+
XNNWorkspaceManager& XnnpackBackendOptions::workspace_manager() {
104+
return workspace_manager_;
105+
}
106+
107+
const XNNWorkspaceManager& XnnpackBackendOptions::workspace_manager() const {
108+
return workspace_manager_;
109+
}
110+
111+
} // namespace executorch::backends::xnnpack

0 commit comments

Comments
 (0)