Skip to content
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

Memory leak in C++ API with Core ML backend #14455

Closed
carsten-schultz-pix4d opened this issue Jan 27, 2023 · 7 comments · Fixed by #14738
Closed

Memory leak in C++ API with Core ML backend #14455

carsten-schultz-pix4d opened this issue Jan 27, 2023 · 7 comments · Fixed by #14738
Assignees
Labels
platform:mobile issues related to ONNX Runtime mobile; typically submitted using template

Comments

@carsten-schultz-pix4d
Copy link

Describe the issue

Using the C++ API with the CoreML backend we observed memory leaks, in particular each call to Ort::Session::Run() leaked a significant amount of memory.

We were able to fix the particular leak with following patch, but the problem does not only affect this one call.

--- a/onnxruntime/core/providers/coreml/model/model.mm
+++ b/onnxruntime/core/providers/coreml/model/model.mm
@@ -333,7 +333,9 @@ Status Execution::Predict(const std::unordered_map<std::string, OnnxTensorData>&
   ORT_RETURN_IF_NOT(model_loaded, "Execution::Predict requires Execution::LoadModel");
 
   if (HAS_VALID_BASE_OS_VERSION) {
-    return [execution_ predict:inputs outputs:outputs];
+    @autoreleasepool {
+        return [execution_ predict:inputs outputs:outputs];
+    }
   }
 
   return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Execution::LoadModel requires macos 10.15+ or ios 13+ ");

This code calls an Objective C method from Apple's Core ML API. These Objective C methods do not always fully release all of the temporary objects that they have created, they may only add them to the current autorelease pool, to be released when that pool is drained. However, the C++ code calling the Ort API has no idea of Objective C's autorelease pools, so none may exist or it may never be drained, and the objects are leaked.

Therefore the code in onnxruntime/core/providers/coreml which interfaces between C++ and Objective C would have to take care of creating and draining autorelease pools, but it currently doesn't.

One solution will be to sprinkle @autoreleaepool blocks around all calls to CoreML methods in the two Objective C++ files in coreml/model. I can certainly prepare a PR that does this, but maybe there is a more elegant solution. I do not normally write Objective C code, and someone here may feel ownership for the CoreML interface code.

(We are using 1.12.1, but I have not seen any relevant difference to the main branch.)

To reproduce

No example provided, sorry.

Urgency

No response

Platform

Mac

OS Version

12.6.2

ONNX Runtime Installation

Built from Source

ONNX Runtime Version or Commit ID

1.12.1

ONNX Runtime API

C++

Architecture

ARM64

Execution Provider

CoreML

Execution Provider Library Version

No response

@github-actions github-actions bot added the platform:mobile issues related to ONNX Runtime mobile; typically submitted using template label Jan 27, 2023
@carsten-schultz-pix4d
Copy link
Author

A bot tagged this for "mobile". We are dealing with macOS here, I specified the ARM architecture because the Macbook on which I tested happens to have an M1 processor. I do not know enough about iOS/iPadOS to say whether this is a problem on there, too. I do not know if pure C++ applications are even possible on those platforms, so the situation may be different.

@yuslepukhin
Copy link
Member

You are welcome to submit a patch. Thanks for reporting it.

@skottmckay skottmckay assigned skottmckay and edgchen1 and unassigned skottmckay Feb 6, 2023
@skottmckay
Copy link
Contributor

TBH we don't usually write Objective-C code either so we don't claim any expertise there.

Are there any potential issues with adding the @autoreleasepool blocks, in particular with the lifetime of objects that are returned from within the block?

I didn't notice any places in model.h that could be wrapped.

Would it just be things in the Execution class in model.mm that need to be wrapped if that's the class used to bridge to Objective-C (according to the comment anyway)?

@carsten-schultz-pix4d
Copy link
Author

Thanks for coming back to this.

All expertise in Objective C memory handling that I can offer comes from one day of Internet search after locating the memory leak in our program. Doing a bit more reading right now I realise that that is certainly not enough. Also, looking again at model.mm I see that I had previously misidentified the border between onnxruntime and CoreML code.

The code change from my initial comment is used by us and has been tested to resolve the memory leak with any adverse effect. Of course it would be ideal to have extensive test coverage and run it with both ASAN and a leak checker, but the world is rarely ideal.

Looking at model.mm again I think that you are right that it would suffice to add the autorelease blocks in the Execution class. We have actually also added one in the block that contains [execution_ loadModel]. The statement in the constructor looks harmless, so that's probably it then. The relevant functions in Execution only return enums, so even not fully understanding all aspects of this it seems safe to me.

@edgchen1
Copy link
Contributor

This issue about a memory leak from CoreML seems relevant.
https://developer.apple.com/forums/thread/692425

The suggested fix is to wrap the CoreML prediction call in an @autoreleasepool block.

@edgchen1
Copy link
Contributor

@carsten-schultz-pix4d
Please see #14738. Tested it locally with a program that repeatedly calls Ort::Session::Run(). Not sure if it exactly matches your scenario, please feel free to verify the changes on your end.

@carsten-schultz-pix4d
Copy link
Author

@edgchen1, thank you. I did not do further tests, but we use very similar changes.

edgchen1 added a commit that referenced this issue Feb 21, 2023
- Fix CoreML API usage memory leak by putting CoreML API prediction call in an `@autoreleasepool` block as suggested in #14455 and [here](https://developer.apple.com/forums/thread/692425). Conservatively wrapping all CoreML API usage.

- Use MLModelConfiguration.computeUnits instead of deprecated MLPredictionOptions.usesCPUOnly (originally in #11382).
verback2308 added a commit to verback2308/onnxruntime that referenced this issue Jun 13, 2023
…all in an `@autoreleasepool` block as suggested in microsoft#14455 and [here](https://developer.apple.com/forums/thread/692425). Conservatively wrapping all CoreML API usage.

- Use MLModelConfiguration.computeUnits instead of deprecated MLPredictionOptions.usesCPUOnly (originally in microsoft#11382).

Credits to @edgchen1
verback2308 pushed a commit to verback2308/onnxruntime that referenced this issue Jun 14, 2023
- Fix CoreML API usage memory leak by putting CoreML API prediction call in an `@autoreleasepool` block as suggested in microsoft#14455 and [here](https://developer.apple.com/forums/thread/692425). Conservatively wrapping all CoreML API usage.

- Use MLModelConfiguration.computeUnits instead of deprecated MLPredictionOptions.usesCPUOnly (originally in microsoft#11382).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform:mobile issues related to ONNX Runtime mobile; typically submitted using template
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants