Skip to content

Commit 34971fb

Browse files
metascroyDannyYuyang-quic
authored andcommitted
Add dynamic shape support to CoreML
Differential Revision: D70904915 Pull Request resolved: pytorch#9094
1 parent 4a33023 commit 34971fb

File tree

12 files changed

+99
-11
lines changed

12 files changed

+99
-11
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,10 @@ if(EXECUTORCH_BUILD_EXECUTOR_RUNNER)
922922
endif()
923923
endif()
924924

925+
if(EXECUTORCH_BUILD_COREML)
926+
list(APPEND _executor_runner_libs coremldelegate)
927+
endif()
928+
925929
add_executable(executor_runner ${_executor_runner__srcs})
926930
if(CMAKE_BUILD_TYPE STREQUAL "Release")
927931
if(APPLE)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ __attribute__((objc_subclassing_restricted))
7171
/// @param error On failure, error is filled with the failure information.
7272
/// @retval `YES` if the execution succeeded otherwise `NO`.
7373
- (BOOL)executeModelWithHandle:(ModelHandle*)handle
74-
argsVec:(const std::vector<executorchcoreml::MultiArray>&)argsVec
74+
argsVec:(std::vector<executorchcoreml::MultiArray>&)argsVec
7575
loggingOptions:(const executorchcoreml::ModelLoggingOptions&)loggingOptions
7676
eventLogger:(const executorchcoreml::ModelEventLogger* _Nullable)eventLogger
7777
error:(NSError* __autoreleasing*)error;

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ - (BOOL)executeModelWithHandle:(ModelHandle *)handle
734734
}
735735

736736
- (BOOL)executeModelWithHandle:(ModelHandle *)handle
737-
argsVec:(const std::vector<executorchcoreml::MultiArray>&)argsVec
737+
argsVec:(std::vector<executorchcoreml::MultiArray>&)argsVec
738738
loggingOptions:(const executorchcoreml::ModelLoggingOptions&)loggingOptions
739739
eventLogger:(const executorchcoreml::ModelEventLogger* _Nullable)eventLogger
740740
error:(NSError * __autoreleasing *)error {
@@ -785,6 +785,12 @@ - (BOOL)executeModelWithHandle:(ModelHandle *)handle
785785
return NO;
786786
}
787787

788+
// Resize for dynamic shapes
789+
for (int i = 0; i < outputArgs.size(); i++) {
790+
auto new_size = to_vector<size_t>(modelOutputs[i].shape);
791+
outputArgs[i].resize(new_size);
792+
argsVec[model.orderedInputNames.count + i].resize(new_size);
793+
}
788794
::set_outputs(outputArgs, modelOutputs);
789795
return YES;
790796
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class BackendDelegate {
8989
/// @param error On failure, error is filled with the failure information.
9090
/// @retval `true` if the execution succeeded otherwise `false`.
9191
virtual bool execute(Handle* handle,
92-
const std::vector<MultiArray>& args,
92+
std::vector<MultiArray>& args,
9393
const ModelLoggingOptions& logging_options,
9494
ModelEventLogger* event_logger,
9595
std::error_code& error) const noexcept = 0;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ - (ModelHandle*)loadModelFromAOTData:(NSData*)data
104104
error:(NSError* __autoreleasing*)error;
105105

106106
- (BOOL)executeModelWithHandle:(ModelHandle*)handle
107-
argsVec:(const std::vector<executorchcoreml::MultiArray>&)argsVec
107+
argsVec:(std::vector<executorchcoreml::MultiArray>&)argsVec
108108
loggingOptions:(const executorchcoreml::ModelLoggingOptions&)loggingOptions
109109
eventLogger:(const executorchcoreml::ModelEventLogger* _Nullable)eventLogger
110110
error:(NSError* __autoreleasing*)error;
@@ -199,7 +199,7 @@ - (ModelHandle*)loadModelFromAOTData:(NSData*)data
199199
}
200200

201201
- (BOOL)executeModelWithHandle:(ModelHandle*)handle
202-
argsVec:(const std::vector<executorchcoreml::MultiArray>&)argsVec
202+
argsVec:(std::vector<executorchcoreml::MultiArray>&)argsVec
203203
loggingOptions:(const executorchcoreml::ModelLoggingOptions&)loggingOptions
204204
eventLogger:(const executorchcoreml::ModelEventLogger* _Nullable)eventLogger
205205
error:(NSError* __autoreleasing*)error {
@@ -286,7 +286,7 @@ explicit BackendDelegateImpl(const Config& config) noexcept
286286
}
287287

288288
bool execute(Handle* handle,
289-
const std::vector<MultiArray>& args,
289+
std::vector<MultiArray>& args,
290290
const ModelLoggingOptions& logging_options,
291291
ModelEventLogger *event_logger,
292292
std::error_code& ec) const noexcept override {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
#import <coreml_backend/delegate.h>
1313
#import <executorch/runtime/core/evalue.h>
1414
#import <executorch/runtime/platform/log.h>
15+
#import <executorch/runtime/kernel/kernel_includes.h>
1516
#import <memory>
1617
#import <model_event_logger.h>
1718
#import <model_logging_options.h>
1819
#import <multiarray.h>
1920
#import <objc_safe_cast.h>
2021
#import <unordered_map>
2122
#import <vector>
23+
#include <array>
2224

2325
#ifdef ET_EVENT_TRACER_ENABLED
2426
#import <model_event_logger_impl.h>
@@ -40,6 +42,9 @@
4042
using executorch::runtime::FreeableBuffer;
4143
using executorch::runtime::get_backend_class;
4244
using executorch::runtime::Result;
45+
using executorch::aten::SizesType;
46+
using executorch::aten::Tensor;
47+
using executorch::runtime::kTensorDimensionLimit;
4348

4449
std::optional<MultiArray::DataType> get_data_type(ScalarType scalar_type) {
4550
switch (scalar_type) {
@@ -221,6 +226,21 @@ ModelLoggingOptions get_logging_options(BackendExecutionContext& context) {
221226
ETCoreMLStrings.delegateIdentifier.UTF8String);
222227
#endif
223228

229+
// Resize for dynamic shape
230+
std::array<SizesType, kTensorDimensionLimit> new_shape;
231+
for (size_t i = nInputs; i < nInputs + nOutputs; i++) {
232+
Tensor& t = args[i]->toTensor();
233+
int rank = delegate_args[i].layout().rank();
234+
assert (rank <= new_shape.size());
235+
for (int d = 0; d < rank; d++) {
236+
new_shape[d] = delegate_args[i].layout().shape()[d];
237+
}
238+
ET_CHECK_OR_RETURN_ERROR(
239+
resize_tensor(t, ArrayRef(new_shape.data(), rank)) == Error::Ok,
240+
DelegateInvalidHandle,
241+
"%s: Failed to resize delegate output %zu", ETCoreMLStrings.delegateIdentifier.UTF8String, i);
242+
}
243+
224244
return Error::Ok;
225245
}
226246

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ class MultiArray final {
8484
/// Returns `true` if the memory layout is packed otherwise `false`.
8585
bool is_packed() const noexcept;
8686

87+
// Resizes memory layout
88+
// New shape must be the same dimension and no larger than current shape in all dimensions
89+
// New format is contiguous
90+
void resize(const std::vector<size_t>& shape);
91+
8792
private:
8893
DataType dataType_;
8994
std::vector<size_t> shape_;
@@ -126,6 +131,8 @@ class MultiArray final {
126131
*ptr = value;
127132
}
128133

134+
void resize(const std::vector<size_t>& shape) { layout_.resize(shape); }
135+
129136
private:
130137
void* data(const std::vector<size_t>& indices) const noexcept;
131138

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,24 @@ ssize_t get_data_offset(size_t index, const std::vector<size_t>& shape, const st
512512

513513
namespace executorchcoreml {
514514

515+
void MultiArray::MemoryLayout::resize(const std::vector<size_t>& shape) {
516+
assert(shape.size() == shape_.size());
517+
for (int i = 0; i < shape.size(); ++i) {
518+
assert (shape[i] >= 1);
519+
assert(shape[i] <= shape_[i]);
520+
}
521+
int stride = 1;
522+
for (int i = shape.size() - 1; i >= 0; --i) {
523+
shape_[i] = shape[i];
524+
strides_[i] = stride;
525+
if (shape[i] > 1) {
526+
stride *= shape[i];
527+
}
528+
}
529+
}
530+
531+
532+
515533
size_t MultiArray::MemoryLayout::num_elements() const noexcept {
516534
if (shape_.size() == 0) {
517535
return 0;

backends/apple/coreml/runtime/test/BackendDelegateTests.mm

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,9 @@ - (void)testAddModelExecution {
162162
MLMultiArray *output = [ETCoreMLTestUtils filledMultiArrayWithShape:inputs[0].shape dataType:inputs[0].dataType repeatedValue:@(0) error:&localError];
163163
NSArray<MLMultiArray *> *args = [inputs arrayByAddingObject:output];
164164
std::error_code errorCode;
165+
auto argsVec = to_multiarrays(args);
165166
XCTAssertTrue(_delegate->execute(handle,
166-
to_multiarrays(args),
167+
argsVec,
167168
ModelLoggingOptions(),
168169
nullptr,
169170
errorCode));
@@ -187,8 +188,9 @@ - (void)testMulModelExecution {
187188
MLMultiArray *output = [ETCoreMLTestUtils filledMultiArrayWithShape:inputs[0].shape dataType:inputs[0].dataType repeatedValue:@(0) error:&localError];
188189
NSArray<MLMultiArray *> *args = [inputs arrayByAddingObject:output];
189190
std::error_code errorCode;
190-
XCTAssertTrue(_delegate->execute(handle,
191-
to_multiarrays(args),
191+
auto argsVec = to_multiarrays(args);
192+
XCTAssertTrue(_delegate->execute(handle,
193+
argsVec,
192194
ModelLoggingOptions(),
193195
nullptr,
194196
errorCode));

backends/apple/coreml/runtime/test/MultiArrayTests.mm

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,20 @@ - (void)testNonAdjacentDataCopy {
130130
[self verifyDataCopyWithShape:shape srcStrides:srcStrides dstStrides:dstStrides];
131131
}
132132

133+
- (void)testResize {
134+
std::vector<size_t> shape = {3, 1, 2, 5};
135+
std::vector<ssize_t> strides = {1*2*5, 2*5, 5, 1};
136+
std::vector<uint8_t> storage;
137+
std::vector<size_t> newShape = {3, 1, 1, 1};
138+
139+
auto array = make_multi_array_and_fill<int>(shape, strides, storage);
140+
for (size_t i = 0; i < array.layout().rank(); ++i) {
141+
XCTAssertEqual(array.layout().shape()[i], shape[i]);
142+
}
143+
array.resize(newShape);
144+
for (size_t i = 0; i < array.layout().rank(); ++i) {
145+
XCTAssertEqual(array.layout().shape()[i], newShape[i]);
146+
}
147+
}
148+
133149
@end

backends/apple/coreml/runtime/workspace/executorchcoreml.xcodeproj/xcshareddata/xcschemes/executorchcoreml_tests.xcscheme

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
BlueprintName = "executorchcoreml_tests"
2424
ReferencedContainer = "container:executorchcoreml.xcodeproj">
2525
</BuildableReference>
26+
<SkippedTests>
27+
<Test
28+
Identifier = "ETCoreMLModelDebuggerTests/testMV3ProgramDebugging">
29+
</Test>
30+
</SkippedTests>
2631
</TestableReference>
2732
</Testables>
2833
</TestAction>

examples/apple/coreml/scripts/export.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ def parse_args() -> argparse.ArgumentParser:
7676
parser.add_argument("--use_partitioner", action=argparse.BooleanOptionalAction)
7777
parser.add_argument("--generate_etrecord", action=argparse.BooleanOptionalAction)
7878
parser.add_argument("--save_processed_bytes", action=argparse.BooleanOptionalAction)
79+
parser.add_argument(
80+
"--dynamic_shapes",
81+
action=argparse.BooleanOptionalAction,
82+
required=False,
83+
default=False,
84+
)
7985

8086
args = parser.parse_args()
8187
# pyre-fixme[7]: Expected `ArgumentParser` but got `Namespace`.
@@ -164,16 +170,20 @@ def main():
164170
f"Valid compute units are {valid_compute_units}."
165171
)
166172

167-
model, example_inputs, _, _ = EagerModelFactory.create_model(
173+
model, example_inputs, _, dynamic_shapes = EagerModelFactory.create_model(
168174
*MODEL_NAME_TO_MODEL[args.model_name]
169175
)
176+
if not args.dynamic_shapes:
177+
dynamic_shapes = None
170178

171179
compile_specs = generate_compile_specs_from_args(args)
172180
lowered_module = None
173181

174182
if args.use_partitioner:
175183
model.eval()
176-
exir_program_aten = torch.export.export(model, example_inputs, strict=True)
184+
exir_program_aten = torch.export.export(
185+
model, example_inputs, dynamic_shapes=dynamic_shapes, strict=True
186+
)
177187

178188
edge_program_manager = exir.to_edge(exir_program_aten)
179189
edge_copy = copy.deepcopy(edge_program_manager)

0 commit comments

Comments
 (0)