Skip to content

Commit 52b3fc8

Browse files
metascroyfacebook-github-bot
authored andcommitted
Add BackendOption loading to swift bindings (#18855)
Summary: Add backend options support to the ExecuTorch Swift/ObjC bindings. The C++ Module class supports LoadBackendOptionsMap for passing per-delegate configuration (e.g. compute unit, thread count, cache directory) at model load time, but this was not exposed through the Swift/ObjC layer. This diff adds: A new ExecuTorchBackendOption ObjC class (BackendOption in Swift) representing a single key-value configuration option with support for boolean, integer, and string value types. New load and loadMethod overloads on ExecuTorchModule that accept a dictionary mapping backend identifiers to arrays of backend options. Swift extensions providing idiomatic load(backendOptions:verification:) and load(_:backendOptions:) APIs on Module. ``` let module = Module(filePath: "model.pte") try module.load(backendOptions: [ "CoreMLBackend": [ BackendOption("compute_unit", "cpu_and_gpu"), BackendOption("num_threads", 4), BackendOption("use_cache", true), ] ]) ``` Differential Revision: D100710833
1 parent 875f7c8 commit 52b3fc8

7 files changed

Lines changed: 400 additions & 0 deletions

File tree

extension/apple/ExecuTorch/Exported/ExecuTorch+Module.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,36 @@ public extension MethodMetadata {
4747
}
4848
}
4949

50+
public extension Module {
51+
/// Loads the module's program with per-delegate backend options.
52+
///
53+
/// - Parameters:
54+
/// - backendOptions: A dictionary mapping backend identifiers (e.g. "CoreMLBackend")
55+
/// to arrays of `BackendOption` objects configuring that backend.
56+
/// - verification: The verification level to apply when loading the program.
57+
/// - Throws: An error if loading fails.
58+
func load(
59+
backendOptions: [String: [BackendOption]],
60+
verification: ModuleVerification = .minimal
61+
) throws {
62+
try __loadWithBackendOptions(backendOptions, verification: verification)
63+
}
64+
65+
/// Loads a specific method from the program with per-delegate backend options.
66+
///
67+
/// - Parameters:
68+
/// - method: The name of the method to load.
69+
/// - backendOptions: A dictionary mapping backend identifiers (e.g. "CoreMLBackend")
70+
/// to arrays of `BackendOption` objects configuring that backend.
71+
/// - Throws: An error if loading fails.
72+
func load(
73+
_ method: String,
74+
backendOptions: [String: [BackendOption]]
75+
) throws {
76+
try __loadMethod(method, backendOptions: backendOptions)
77+
}
78+
}
79+
5080
public extension Module {
5181
/// Executes a specific method with the provided input values.
5282
/// The method is loaded on demand if not already loaded.

extension/apple/ExecuTorch/Exported/ExecuTorch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9+
#import "ExecuTorchBackendOption.h"
910
#import "ExecuTorchError.h"
1011
#import "ExecuTorchLog.h"
1112
#import "ExecuTorchModule.h"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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+
#import <Foundation/Foundation.h>
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
/**
14+
* Enum to define the type of a backend option value.
15+
*/
16+
typedef NS_ENUM(NSInteger, ExecuTorchBackendOptionType) {
17+
ExecuTorchBackendOptionTypeBoolean,
18+
ExecuTorchBackendOptionTypeInteger,
19+
ExecuTorchBackendOptionTypeString,
20+
} NS_SWIFT_NAME(BackendOptionType);
21+
22+
/**
23+
* Represents a single key-value configuration option for a backend.
24+
*
25+
* Backend options are used to pass per-delegate configuration (e.g., compute
26+
* unit, thread count, cache directory) when loading a module. Each option has
27+
* a string key and a typed value (boolean, integer, or string).
28+
*/
29+
NS_SWIFT_NAME(BackendOption)
30+
__attribute__((objc_subclassing_restricted))
31+
@interface ExecuTorchBackendOption : NSObject
32+
33+
/** The option key name (e.g. "compute_unit", "num_threads"). */
34+
@property (nonatomic, readonly) NSString *key;
35+
36+
/** The type of the option value. */
37+
@property (nonatomic, readonly) ExecuTorchBackendOptionType type;
38+
39+
/** The boolean value. Only valid when type is Boolean. */
40+
@property (nonatomic, readonly) BOOL boolValue;
41+
42+
/** The integer value. Only valid when type is Integer. */
43+
@property (nonatomic, readonly) NSInteger intValue;
44+
45+
/** The string value. Only valid when type is String. */
46+
@property (nullable, nonatomic, readonly) NSString *stringValue;
47+
48+
/**
49+
* Creates a backend option with a boolean value.
50+
*
51+
* @param key The option key.
52+
* @param value The boolean value.
53+
* @return A new ExecuTorchBackendOption instance.
54+
*/
55+
+ (instancetype)optionWithKey:(NSString *)key
56+
booleanValue:(BOOL)value
57+
NS_SWIFT_NAME(init(_:_:))
58+
NS_RETURNS_RETAINED;
59+
60+
/**
61+
* Creates a backend option with an integer value.
62+
*
63+
* @param key The option key.
64+
* @param value The integer value.
65+
* @return A new ExecuTorchBackendOption instance.
66+
*/
67+
+ (instancetype)optionWithKey:(NSString *)key
68+
integerValue:(NSInteger)value
69+
NS_SWIFT_NAME(init(_:_:))
70+
NS_RETURNS_RETAINED;
71+
72+
/**
73+
* Creates a backend option with a string value.
74+
*
75+
* @param key The option key.
76+
* @param value The string value.
77+
* @return A new ExecuTorchBackendOption instance.
78+
*/
79+
+ (instancetype)optionWithKey:(NSString *)key
80+
stringValue:(NSString *)value
81+
NS_SWIFT_NAME(init(_:_:))
82+
NS_RETURNS_RETAINED;
83+
84+
+ (instancetype)new NS_UNAVAILABLE;
85+
- (instancetype)init NS_UNAVAILABLE;
86+
87+
@end
88+
89+
NS_ASSUME_NONNULL_END
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
#import "ExecuTorchBackendOption.h"
10+
11+
@implementation ExecuTorchBackendOption {
12+
NSString *_key;
13+
ExecuTorchBackendOptionType _type;
14+
BOOL _boolValue;
15+
NSInteger _intValue;
16+
NSString *_stringValue;
17+
}
18+
19+
- (instancetype)initWithKey:(NSString *)key
20+
booleanValue:(BOOL)value {
21+
self = [super init];
22+
if (self) {
23+
_key = [key copy];
24+
_type = ExecuTorchBackendOptionTypeBoolean;
25+
_boolValue = value;
26+
}
27+
return self;
28+
}
29+
30+
- (instancetype)initWithKey:(NSString *)key
31+
integerValue:(NSInteger)value {
32+
self = [super init];
33+
if (self) {
34+
_key = [key copy];
35+
_type = ExecuTorchBackendOptionTypeInteger;
36+
_intValue = value;
37+
}
38+
return self;
39+
}
40+
41+
- (instancetype)initWithKey:(NSString *)key
42+
stringValue:(NSString *)value {
43+
self = [super init];
44+
if (self) {
45+
_key = [key copy];
46+
_type = ExecuTorchBackendOptionTypeString;
47+
_stringValue = [value copy];
48+
}
49+
return self;
50+
}
51+
52+
+ (instancetype)optionWithKey:(NSString *)key
53+
booleanValue:(BOOL)value {
54+
return [[self alloc] initWithKey:key booleanValue:value];
55+
}
56+
57+
+ (instancetype)optionWithKey:(NSString *)key
58+
integerValue:(NSInteger)value {
59+
return [[self alloc] initWithKey:key integerValue:value];
60+
}
61+
62+
+ (instancetype)optionWithKey:(NSString *)key
63+
stringValue:(NSString *)value {
64+
return [[self alloc] initWithKey:key stringValue:value];
65+
}
66+
67+
@end

extension/apple/ExecuTorch/Exported/ExecuTorchModule.h

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

9+
#import "ExecuTorchBackendOption.h"
910
#import "ExecuTorchValue.h"
1011

1112
NS_ASSUME_NONNULL_BEGIN
@@ -186,6 +187,30 @@ NS_SWIFT_NAME(Module)
186187
*/
187188
- (BOOL)load:(NSError **)error;
188189

190+
/**
191+
* Loads the module's program with per-delegate backend options.
192+
*
193+
* @param backendOptions A dictionary mapping backend identifiers (e.g. "CoreMLBackend")
194+
* to arrays of ExecuTorchBackendOption objects configuring that backend.
195+
* @param verification The verification level to apply when loading the program.
196+
* @param error A pointer to an NSError pointer that will be set if an error occurs.
197+
* @return YES if the program was successfully loaded; otherwise, NO.
198+
*/
199+
- (BOOL)loadWithBackendOptions:(NSDictionary<NSString *, NSArray<ExecuTorchBackendOption *> *> *)backendOptions
200+
verification:(ExecuTorchVerification)verification
201+
error:(NSError **)error NS_REFINED_FOR_SWIFT;
202+
203+
/**
204+
* Loads the module's program with per-delegate backend options using minimal verification.
205+
*
206+
* @param backendOptions A dictionary mapping backend identifiers (e.g. "CoreMLBackend")
207+
* to arrays of ExecuTorchBackendOption objects configuring that backend.
208+
* @param error A pointer to an NSError pointer that will be set if an error occurs.
209+
* @return YES if the program was successfully loaded; otherwise, NO.
210+
*/
211+
- (BOOL)loadWithBackendOptions:(NSDictionary<NSString *, NSArray<ExecuTorchBackendOption *> *> *)backendOptions
212+
error:(NSError **)error NS_REFINED_FOR_SWIFT;
213+
189214
/**
190215
* Checks if the module is loaded.
191216
*
@@ -203,6 +228,19 @@ NS_SWIFT_NAME(Module)
203228
- (BOOL)loadMethod:(NSString *)methodName
204229
error:(NSError **)error NS_SWIFT_NAME(load(_:));
205230

231+
/**
232+
* Loads a specific method from the program with per-delegate backend options.
233+
*
234+
* @param methodName A string representing the name of the method to load.
235+
* @param backendOptions A dictionary mapping backend identifiers (e.g. "CoreMLBackend")
236+
* to arrays of ExecuTorchBackendOption objects configuring that backend.
237+
* @param error A pointer to an NSError pointer that is set if an error occurs.
238+
* @return YES if the method was successfully loaded; otherwise, NO.
239+
*/
240+
- (BOOL)loadMethod:(NSString *)methodName
241+
backendOptions:(NSDictionary<NSString *, NSArray<ExecuTorchBackendOption *> *> *)backendOptions
242+
error:(NSError **)error NS_REFINED_FOR_SWIFT;
243+
206244
/**
207245
* Checks if a specific method is loaded.
208246
*

extension/apple/ExecuTorch/Exported/ExecuTorchModule.mm

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88

99
#import "ExecuTorchModule.h"
1010

11+
#import "ExecuTorchBackendOption.h"
1112
#import "ExecuTorchError.h"
1213
#import "ExecuTorchUtils.h"
1314

1415
#import <executorch/extension/module/module.h>
1516
#import <executorch/extension/tensor/tensor.h>
17+
#import <executorch/runtime/backend/backend_options_map.h>
18+
#import <executorch/runtime/backend/options.h>
1619

1720
using namespace executorch::extension;
1821
using namespace executorch::runtime;
@@ -63,6 +66,49 @@ static inline EValue toEValue(ExecuTorchValue *value) {
6366
return [ExecuTorchValue new];
6467
}
6568

69+
static Error buildBackendOptionsMap(
70+
NSDictionary<NSString *, NSArray<ExecuTorchBackendOption *> *> *backendOptions,
71+
std::vector<std::vector<BackendOption>> &allOptions,
72+
LoadBackendOptionsMap &map) {
73+
allOptions.reserve(backendOptions.count);
74+
for (NSString *backendId in backendOptions) {
75+
NSArray<ExecuTorchBackendOption *> *options = backendOptions[backendId];
76+
std::vector<BackendOption> opts;
77+
opts.reserve(options.count);
78+
for (ExecuTorchBackendOption *opt in options) {
79+
BackendOption bo;
80+
strncpy(bo.key, opt.key.UTF8String, kMaxOptionKeyLength - 1);
81+
bo.key[kMaxOptionKeyLength - 1] = '\0';
82+
switch (opt.type) {
83+
case ExecuTorchBackendOptionTypeBoolean:
84+
bo.value = (bool)opt.boolValue;
85+
break;
86+
case ExecuTorchBackendOptionTypeInteger:
87+
if (opt.intValue < INT_MIN || opt.intValue > INT_MAX) {
88+
return Error::InvalidArgument;
89+
}
90+
bo.value = (int)opt.intValue;
91+
break;
92+
case ExecuTorchBackendOptionTypeString: {
93+
std::array<char, kMaxOptionValueLength> arr{};
94+
strncpy(arr.data(), opt.stringValue.UTF8String, kMaxOptionValueLength - 1);
95+
arr[kMaxOptionValueLength - 1] = '\0';
96+
bo.value = arr;
97+
break;
98+
}
99+
}
100+
opts.push_back(bo);
101+
}
102+
allOptions.push_back(std::move(opts));
103+
auto &backOpts = allOptions.back();
104+
const auto err = map.set_options(backendId.UTF8String, Span<BackendOption>(backOpts.data(), backOpts.size()));
105+
if (err != Error::Ok) {
106+
return err;
107+
}
108+
}
109+
return Error::Ok;
110+
}
111+
66112
@interface ExecuTorchTensorMetadata ()
67113

68114
- (instancetype)initWithTensorMetadata:(const TensorInfo &)tensorInfo
@@ -324,6 +370,57 @@ - (BOOL)loadMethod:(NSString *)methodName
324370
return YES;
325371
}
326372

373+
- (BOOL)loadWithBackendOptions:(NSDictionary<NSString *, NSArray<ExecuTorchBackendOption *> *> *)backendOptions
374+
verification:(ExecuTorchVerification)verification
375+
error:(NSError **)error {
376+
std::vector<std::vector<BackendOption>> allOptions;
377+
LoadBackendOptionsMap map;
378+
const auto buildError = buildBackendOptionsMap(backendOptions, allOptions, map);
379+
if (buildError != Error::Ok) {
380+
if (error) {
381+
*error = ExecuTorchErrorWithCode((ExecuTorchErrorCode)buildError);
382+
}
383+
return NO;
384+
}
385+
const auto errorCode = _module->load(map, static_cast<Program::Verification>(verification));
386+
if (errorCode != Error::Ok) {
387+
if (error) {
388+
*error = ExecuTorchErrorWithCode((ExecuTorchErrorCode)errorCode);
389+
}
390+
return NO;
391+
}
392+
return YES;
393+
}
394+
395+
- (BOOL)loadWithBackendOptions:(NSDictionary<NSString *, NSArray<ExecuTorchBackendOption *> *> *)backendOptions
396+
error:(NSError **)error {
397+
return [self loadWithBackendOptions:backendOptions
398+
verification:ExecuTorchVerificationMinimal
399+
error:error];
400+
}
401+
402+
- (BOOL)loadMethod:(NSString *)methodName
403+
backendOptions:(NSDictionary<NSString *, NSArray<ExecuTorchBackendOption *> *> *)backendOptions
404+
error:(NSError **)error {
405+
std::vector<std::vector<BackendOption>> allOptions;
406+
LoadBackendOptionsMap map;
407+
const auto buildError = buildBackendOptionsMap(backendOptions, allOptions, map);
408+
if (buildError != Error::Ok) {
409+
if (error) {
410+
*error = ExecuTorchErrorWithCode((ExecuTorchErrorCode)buildError);
411+
}
412+
return NO;
413+
}
414+
const auto errorCode = _module->load_method(methodName.UTF8String, nullptr, nullptr, &map);
415+
if (errorCode != Error::Ok) {
416+
if (error) {
417+
*error = ExecuTorchErrorWithCode((ExecuTorchErrorCode)errorCode);
418+
}
419+
return NO;
420+
}
421+
return YES;
422+
}
423+
327424
- (BOOL)isMethodLoaded:(NSString *)methodName {
328425
return _module->is_method_loaded(methodName.UTF8String);
329426
}

0 commit comments

Comments
 (0)