Skip to content

Commit 13da62b

Browse files
authored
[CoreML] Add support for running statefule model. (#5143)
1 parent 3268da2 commit 13da62b

File tree

8 files changed

+96
-52
lines changed

8 files changed

+96
-52
lines changed

backends/apple/coreml/runtime/delegate/ETCoreMLDefaultModelExecutor.mm

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ - (instancetype)initWithModel:(ETCoreMLModel *)model {
2929
if (self.ignoreOutputBackings) {
3030
predictionOptions.outputBackings = @{};
3131
}
32-
id<MLFeatureProvider> outputs = [self.model.mlModel predictionFromFeatures:inputs
33-
options:predictionOptions
34-
error:error];
32+
33+
id<MLFeatureProvider> outputs = [self.model predictionFromFeatures:inputs
34+
options:predictionOptions
35+
error:error];
3536
if (!outputs) {
3637
return nil;
3738
}

backends/apple/coreml/runtime/delegate/ETCoreMLModel.h

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
#import <CoreML/CoreML.h>
99
#import <vector>
1010

11+
#if !defined(MODEL_STATE_IS_SUPPORTED) && __has_include(<CoreML/MLModel+MLState.h>)
12+
#define MODEL_STATE_IS_SUPPORTED 1
13+
#endif
14+
1115
NS_ASSUME_NONNULL_BEGIN
1216

1317
@class ETCoreMLAsset;
@@ -37,15 +41,12 @@ __attribute__((objc_subclassing_restricted))
3741
orderedOutputNames:(NSOrderedSet<NSString*>*)orderedOutputNames
3842
error:(NSError* __autoreleasing*)error NS_DESIGNATED_INITIALIZER;
3943

40-
- (nullable NSArray<MLMultiArray*>*)prepareInputs:(const std::vector<executorchcoreml::MultiArray>&)inputs
41-
error:(NSError* __autoreleasing*)error;
42-
43-
- (nullable NSArray<MLMultiArray*>*)prepareOutputBackings:(const std::vector<executorchcoreml::MultiArray>&)outputs
44-
error:(NSError* __autoreleasing*)error;
45-
4644
/// The underlying MLModel.
4745
@property (strong, readonly, nonatomic) MLModel* mlModel;
4846

47+
/// The model state.
48+
@property (strong, readonly, nonatomic) id state API_AVAILABLE(macos(15.0), ios(18.0), tvos(18.0), watchos(11.0));
49+
4950
/// The asset from which the model is loaded.
5051
@property (strong, readonly, nonatomic) ETCoreMLAsset* asset;
5152

@@ -58,6 +59,19 @@ __attribute__((objc_subclassing_restricted))
5859
/// The ordered output names of the model.
5960
@property (copy, readonly, nonatomic) NSOrderedSet<NSString*>* orderedOutputNames;
6061

62+
63+
- (nullable id<MLFeatureProvider>)predictionFromFeatures:(id<MLFeatureProvider>)input
64+
options:(MLPredictionOptions*)options
65+
error:(NSError* __autoreleasing*)error;
66+
67+
- (nullable NSArray<MLMultiArray*>*)prepareInputs:(const std::vector<executorchcoreml::MultiArray>&)inputs
68+
error:(NSError* __autoreleasing*)error;
69+
70+
- (nullable NSArray<MLMultiArray*>*)prepareOutputBackings:(const std::vector<executorchcoreml::MultiArray>&)outputs
71+
error:(NSError* __autoreleasing*)error;
72+
73+
- (BOOL)prewarmAndReturnError:(NSError* __autoreleasing*)error;
74+
6175
@end
6276

6377
NS_ASSUME_NONNULL_END

backends/apple/coreml/runtime/delegate/ETCoreMLModel.mm

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77

88
#import <ETCoreMLModel.h>
99

10-
#import <ETCoreMLAsset.h>
10+
#import "ETCoreMLAsset.h"
11+
#import "ETCoreMLLogging.h"
12+
#import "multiarray.h"
13+
#import "objc_array_util.h"
14+
#import "MLModel_Prewarm.h"
1115
#import <functional>
12-
#import <objc_array_util.h>
13-
#import <multiarray.h>
1416
#import <numeric>
1517

1618
#pragma mark - ETCoreMLMultiArrayDescriptor
@@ -194,6 +196,11 @@ - (nullable instancetype)initWithAsset:(ETCoreMLAsset *)asset
194196
_cache = [[NSCache alloc] init];
195197
_inputConstraintsByName = get_multi_array_input_constraints_by_name(mlModel.modelDescription);
196198
_outputConstraintsByName = get_multi_array_output_constraints_by_name(mlModel.modelDescription);
199+
#if MODEL_STATE_IS_SUPPORTED
200+
if (@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *)) {
201+
_state = mlModel.modelDescription.stateDescriptionsByName.count > 0 ? [_mlModel newState] : nil;
202+
}
203+
#endif
197204
}
198205

199206
return self;
@@ -272,4 +279,48 @@ MultiArray buffer(mutableBytes, MultiArray::MemoryLayout(to_multiarray_data_type
272279

273280
}
274281

282+
- (nullable id<MLFeatureProvider>)predictionFromFeatures:(id<MLFeatureProvider>)input
283+
options:(MLPredictionOptions *)options
284+
error:(NSError **)error {
285+
286+
#if MODEL_STATE_IS_SUPPORTED
287+
if (@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *)) {
288+
if (self.state != nil) {
289+
return [self.mlModel predictionFromFeatures:input
290+
usingState:(MLState *)self.state
291+
options:options
292+
error:error];
293+
}
294+
}
295+
#endif
296+
297+
return [self.mlModel predictionFromFeatures:input
298+
options:options
299+
error:error];
300+
}
301+
302+
- (BOOL)prewarmAndReturnError:(NSError* __autoreleasing*)error {
303+
BOOL prewarm = YES;
304+
#if MODEL_STATE_IS_SUPPORTED
305+
if (@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *)) {
306+
prewarm = (self.mlModel.modelDescription.stateDescriptionsByName.count == 0);
307+
}
308+
#endif
309+
310+
NSError *localError = nil;
311+
BOOL result = prewarm ? [self.mlModel prewarmAndReturnError:&localError] : NO;
312+
if (!result) {
313+
ETCoreMLLogError(localError,
314+
"%@: Failed to prewarm model with identifier = %@",
315+
NSStringFromClass(self.class),
316+
self.identifier);
317+
}
318+
319+
if (error) {
320+
*error = localError;
321+
}
322+
323+
return result;
324+
}
325+
275326
@end

backends/apple/coreml/runtime/delegate/ETCoreMLModelManager.mm

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -598,21 +598,8 @@ - (BOOL)prewarmModelWithHandle:(ModelHandle *)handle
598598
if (!model) {
599599
return NO;
600600
}
601-
602-
NSError *localError = nil;
603-
BOOL result = [model.mlModel prewarmAndReturnError:&localError];
604-
if (!result) {
605-
ETCoreMLLogError(localError,
606-
"%@: Failed to prewarm model with identifier = %@",
607-
NSStringFromClass(self.assetManager.class),
608-
model.identifier);
609-
}
610-
611-
if (error) {
612-
*error = localError;
613-
}
614-
615-
return result;
601+
602+
return [model prewarmAndReturnError:error];
616603
}
617604

618605
- (void)prewarmRecentlyUsedAssetsWithMaxCount:(NSUInteger)maxCount {

backends/apple/coreml/runtime/delegate/MLModel_Prewarm.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ - (BOOL)prewarmAndReturnError:(NSError * __autoreleasing *)error {
7777
if (!inputs) {
7878
return NO;
7979
}
80-
80+
8181
id<MLFeatureProvider> outputs = [self predictionFromFeatures:inputs error:error];
8282
return outputs != nil;
8383
}

backends/apple/coreml/runtime/sdk/ETCoreMLModelAnalyzer.mm

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,9 @@ - (nullable instancetype)initWithCompiledModelAsset:(ETCoreMLAsset *)compiledMod
8888
eventLogger:(const executorchcoreml::ModelEventLogger *)eventLogger
8989
error:(NSError * __autoreleasing *)error {
9090
if (self.profiler == nil) {
91-
ETCoreMLModelProfiler *profiler = [[ETCoreMLModelProfiler alloc] initWithCompiledModelAsset:self.model.asset
92-
outputNames:self.model.orderedOutputNames
93-
configuration:self.configuration
94-
error:error];
91+
ETCoreMLModelProfiler *profiler = [[ETCoreMLModelProfiler alloc] initWithModel:self.model
92+
configuration:self.configuration
93+
error:error];
9594
self.profiler = profiler;
9695
}
9796

backends/apple/coreml/runtime/sdk/ETCoreMLModelProfiler.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,12 @@ __attribute__((objc_subclassing_restricted))
3131

3232
/// Constructs an `ETCoreMLModelProfiler` instance.
3333
///
34-
/// @param compiledModelAsset The compiled model asset (mlmodelc).
35-
/// @param outputNames The model output names.
34+
/// @param model The model.
3635
/// @param configuration The model configuration.
3736
/// @param error On failure, error is filled with the failure information.
38-
- (nullable instancetype)initWithCompiledModelAsset:(ETCoreMLAsset*)compiledModelAsset
39-
outputNames:(NSOrderedSet<NSString*>*)outputNames
40-
configuration:(MLModelConfiguration*)configuration
41-
error:(NSError* __autoreleasing*)error NS_DESIGNATED_INITIALIZER;
37+
- (nullable instancetype)initWithModel:(ETCoreMLModel*)model
38+
configuration:(MLModelConfiguration*)configuration
39+
error:(NSError* __autoreleasing*)error NS_DESIGNATED_INITIALIZER;
4240

4341
/// Returns profiling info of operations at the specified paths.
4442
///

backends/apple/coreml/runtime/sdk/ETCoreMLModelProfiler.mm

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#import "ETCoreMLModelProfiler.h"
99

1010
#import "ETCoreMLAsset.h"
11+
#import "ETCoreMLModel.h"
1112
#import "ETCoreMLLogging.h"
1213
#import "ETCoreMLModelStructurePath.h"
1314
#import "ETCoreMLOperationProfilingInfo.h"
@@ -221,8 +222,8 @@ void set_model_outputs(id<MLFeatureProvider> output_features,
221222
}
222223

223224
@interface ETCoreMLModelProfiler ()
224-
/// The CoreML model.
225-
@property (readonly, strong, nonatomic) MLModel *model;
225+
/// The model.
226+
@property (readonly, strong, nonatomic) ETCoreMLModel *model;
226227
/// The model output names.
227228
@property (readonly, copy, nonatomic) NSOrderedSet<NSString *> *outputNames;
228229
#if MODEL_PROFILING_IS_AVAILABLE
@@ -240,25 +241,19 @@ @interface ETCoreMLModelProfiler ()
240241

241242
@implementation ETCoreMLModelProfiler
242243

243-
- (nullable instancetype)initWithCompiledModelAsset:(ETCoreMLAsset *)compiledModelAsset
244-
outputNames:(NSOrderedSet<NSString *> *)outputNames
245-
configuration:(MLModelConfiguration *)configuration
246-
error:(NSError * __autoreleasing *)error {
244+
- (nullable instancetype)initWithModel:(ETCoreMLModel *)model
245+
configuration:(MLModelConfiguration *)configuration
246+
error:(NSError * __autoreleasing *)error {
247247
#if MODEL_PROFILING_IS_AVAILABLE
248248
if (@available(macOS 14.4, iOS 17.4, tvOS 17.4, watchOS 10.4, *)) {
249-
NSURL *compiledModelURL = compiledModelAsset.contentURL;
249+
NSURL *compiledModelURL = model.asset.contentURL;
250250
MLComputePlan *computePlan = get_compute_plan_of_model_at_url(compiledModelURL,
251251
configuration,
252252
error);
253253
if (!computePlan) {
254254
return nil;
255255
}
256-
257-
MLModel *model = [MLModel modelWithContentsOfURL:compiledModelURL error:error];
258-
if (!model) {
259-
return nil;
260-
}
261-
256+
262257
__block NSMutableArray<ETCoreMLModelStructurePath *> *operationPaths = [NSMutableArray array];
263258
__block NSMutableDictionary<NSValue *, ETCoreMLModelStructurePath *> *operationToPathMap = [NSMutableDictionary dictionary];
264259
__block NSMutableArray<MLModelStructureProgramOperation *> *topologicallySortedOperations = [NSMutableArray new];
@@ -280,7 +275,6 @@ - (nullable instancetype)initWithCompiledModelAsset:(ETCoreMLAsset *)compiledMod
280275

281276
self = [super init];
282277
if (self) {
283-
_outputNames = [outputNames copy];
284278
_model = model;
285279
_computePlan = computePlan;
286280
_operationToPathMap = operationToPathMap;

0 commit comments

Comments
 (0)