-
Notifications
You must be signed in to change notification settings - Fork 3k
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
[CoreML] support coreml model cache #23065
base: main
Are you sure you want to change the base?
Conversation
5bfc8eb
to
fc9db07
Compare
d539da2
to
1d1c874
Compare
81c2b9e
to
7b11848
Compare
include/onnxruntime/core/providers/coreml/coreml_provider_factory.h
Outdated
Show resolved
Hide resolved
if (require_static_shape_) { | ||
model_cache_path_ += "/static_shape"; | ||
} else { | ||
model_cache_path_ += "/dynamic_shape"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Is this required? Would be good to keep this as simple as possible. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, It's required.
require_static_shape_
or not will determinate the what the sub-graph looks like. And gen_metadef_name
didn't check the input/output shape info.
// } else { | ||
// // save to ModelCachePath | ||
// } | ||
// we wound not detect if the cached model match the onnx subgraph, so User should carefully manage the cache for a new model. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should document what we do and don't do here.
- Prefer cache key from model metadata
- we could include example python here, and skip the actual implementation of the hashing to keep it simple
- Use model path if available
If model changes user must do one of:
- Set different cache key in model metadata
- Load model from a different path
- Delete old cache information
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or we can document fully in CoreML-ExecutionProvider.md and include a link to that here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
// // save to ModelCachePath | ||
// } | ||
// we wound not detect if the cached model match the onnx subgraph, so User should carefully manage the cache for a new model. | ||
static const char* const kCoremlProviderOption_ModelCachePath = "ModelCachePath"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have a const for the model metadata key name here. I'd vote for COREML_CACHE_KEY
given the usage is CoreML specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
May the cache_key can be used by other EPs.
ORT_ENFORCE(std::count(subgraph_name.begin(), subgraph_name.end(), '_') == 3, | ||
"Unexpected graph name format: ", subgraph_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given the cache is an optional feature, it might be better to disable caching and log the error instead of throwing, as it could break an iOS app completely (e.g. if they don't have logic to explicitly turn off caching it will be broken).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't be possible to throw as long as CoreML EP developers didn't modify gen_metadef_name or modify both.
User can unset 'coreml_options.ModelCachePath()' to disable cache.
But it should be fine to remove this check.
// model_hash is a 64-bit hash value of model_path | ||
user_provide_key = std::to_string(model_hash); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only true if the model path is available. If not it hashes the graph input names and all the node output names. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have documented it in CoreML EP.
I will add more comments here.
main_graph = main_graph->ParentGraph(); | ||
} | ||
if (main_graph->GetModel().MetaData().count("CACHE_KEY") > 0) { | ||
user_provide_key = graph_viewer.GetGraph().GetModel().MetaData().at("CACHE_KEY"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to validate this with something like std::isalnum to guarantee it will be valid for use in the filesystem. I'd also suggest we enforce a maximum length to also try and avoid issues creating folders/files with this in the name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. we should do it.
For now, cache-key should has at most 32 characters. Or we will re-hash it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
64 chars + null is required to store a sha256 hash as hex so maybe we could use that as the limit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
64 comes to my mind in the first thought, would this be too long?
Let me use 64 as the limit again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a maximum so the user isn't forced to use it all. Allowing a 256 bit hash seems reasonable. Max directory/file name limit is 255 chars so 64 is well under that. Max path is somewhere around 1000 chars. User also controls the cache path.
But we should be careful about how deep the directory names get for the cache files. It might make more sense to shorten things like whether static shapes were enabled, and for the model it's really only the subgraph id that matters.
i.e. including the user hash and the model hash in a directory name (userhash_COREML_modelhash_subgraphid) shouldn't actually matter when all files for the model are stored under a top-level directory name that uses the preferred hash, as there a) should never be files from any other models in there, and b) the subgraphid should be deterministic (unless they run with a different optimization level).
Take a look at how long the paths are in your tests and figure out a good balance between readable/safe and avoiding exceeding the max path length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. It's quite balanced now.
<user_provided_path>/cc6b2111b15dcdcf00ed6647c4430315e01616efde46ab4109f80fd1d3c46731/0_dynamic_mlprogram/model/
Co-authored-by: Scott McKay <[email protected]>
include/onnxruntime/core/providers/coreml/coreml_provider_factory.h
Outdated
Show resolved
Hide resolved
include/onnxruntime/core/providers/coreml/coreml_provider_factory.h
Outdated
Show resolved
Hide resolved
include/onnxruntime/core/providers/coreml/coreml_provider_factory.h
Outdated
Show resolved
Hide resolved
…ory.h Co-authored-by: Scott McKay <[email protected]>
…ory.h Co-authored-by: Scott McKay <[email protected]>
…ory.h Co-authored-by: Scott McKay <[email protected]>
include/onnxruntime/core/providers/coreml/coreml_provider_factory.h
Outdated
Show resolved
Hide resolved
user_provided_key = graph_viewer.GetGraph().GetModel().MetaData().at(kCOREML_CACHE_KEY); | ||
if (user_provided_key.size() > 64 || | ||
std::any_of(user_provided_key.begin(), user_provided_key.end(), | ||
[](unsigned char c) { return !std::isalnum(c); })) { | ||
LOGS(logger, ERROR) << "[" << kCOREML_CACHE_KEY << ":" << user_provided_key << "] is not a valid cache key." | ||
<< " It should be alphanumeric and less than 64 characters."; | ||
} | ||
// invalid cache-key | ||
if (user_provided_key.size() == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this once outside of the gen_metadef_name lambda?
When there's an error we're logging it, but nothing is setting user_provided_key to empty, so not clear how the 'invalid cache-key' if
condition will be satisfied.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, Sounds good.
Co-authored-by: Scott McKay <[email protected]>
Description
Refactor compute plan profiling
Support cache coreml model to speed up session initialization. this is only support by user provided entry and user responsible to manage the cache
With the cache, session initialization time can be reduced by 50% or more:
Motivation and Context