Skip to content

Commit

Permalink
[core] Core read_model supports user configuration (openvinotoolkit#2…
Browse files Browse the repository at this point in the history
…8066)

### Details:
- Extend `Core::read_model` by additional optimal parameter, properties.
The properties passed to `read_model` are valid only for this usage and
will not change `Core` instance state.
 - Update python API to support new parameter.

### Tickets:
 - CVS-158761

---------

Signed-off-by: Raasz, Pawel <[email protected]>
  • Loading branch information
praasz authored Dec 18, 2024
1 parent 6f3796b commit 4371116
Show file tree
Hide file tree
Showing 12 changed files with 297 additions and 43 deletions.
17 changes: 14 additions & 3 deletions src/bindings/python/src/openvino/_ov_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,11 +495,22 @@ class Core(CoreBase):
between several Core instances. The recommended way is to have a single
Core instance per application.
"""
def read_model(self, model: Union[str, bytes, object], weights: Union[object, str, bytes, Tensor] = None) -> Model:
if weights is not None:
def read_model(
self,
model: Union[str, bytes, object],
weights: Union[object, str, bytes, Tensor] = None,
config: Optional[dict] = None
) -> Model:
config = {} if config is None else config

if isinstance(weights, Tensor):
return Model(super().read_model(model, weights))
elif isinstance(model, bytes):
return Model(super().read_model(model, bytes() if weights is None else weights))
elif weights is None:
return Model(super().read_model(model, config=config))
else:
return Model(super().read_model(model))
return Model(super().read_model(model, weights, config))

def compile_model(
self,
Expand Down
28 changes: 22 additions & 6 deletions src/bindings/python/src/pyopenvino/core/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,10 +393,17 @@ void regclass_Core(py::module m) {

cls.def(
"read_model",
(std::shared_ptr<ov::Model>(ov::Core::*)(const std::string&, const std::string&) const) & ov::Core::read_model,
py::call_guard<py::gil_scoped_release>(),
[](ov::Core& self,
const std::string& model_path,
const std::string& weight_path,
const std::map<std::string, py::object>& config) {
const auto any_map = Common::utils::properties_to_any_map(config);
py::gil_scoped_release release;
return self.read_model(model_path, weight_path, any_map);
},
py::arg("model"),
py::arg("weights") = "",
py::arg("config") = py::dict(),
R"(
Reads models from IR / ONNX / PDPD / TF and TFLite formats.
Expand All @@ -412,6 +419,8 @@ void regclass_Core(py::module m) {
For TF format (*.pb) weights parameter is not used.
For TFLite format (*.tflite) weights parameter is not used.
:type weights: str
:param config: Optional map of pairs: (property name, property value) relevant only for this read operation.
:type config: dict, optional
:return: A model.
:rtype: openvino.runtime.Model
)");
Expand All @@ -438,7 +447,10 @@ void regclass_Core(py::module m) {

cls.def(
"read_model",
[](ov::Core& self, py::object model_path, py::object weights_path) {
[](ov::Core& self,
py::object model_path,
py::object weights_path,
const std::map<std::string, py::object>& config) {
if (py::isinstance(model_path, pybind11::module::import("io").attr("BytesIO"))) {
std::stringstream _stream;
model_path.attr("seek")(0); // Always rewind stream!
Expand Down Expand Up @@ -466,8 +478,9 @@ void regclass_Core(py::module m) {
if (!py::isinstance<py::none>(weights_path)) {
weights_path_cpp = py::str(weights_path);
}
const auto any_map = Common::utils::properties_to_any_map(config);
py::gil_scoped_release release;
return self.read_model(model_path_cpp, weights_path_cpp);
return self.read_model(model_path_cpp, weights_path_cpp, any_map);
}

std::stringstream str;
Expand All @@ -477,6 +490,7 @@ void regclass_Core(py::module m) {
},
py::arg("model"),
py::arg("weights") = py::none(),
py::arg("config") = py::dict(),
R"(
Reads models from IR / ONNX / PDPD / TF and TFLite formats.
Expand All @@ -492,6 +506,8 @@ void regclass_Core(py::module m) {
For TF format (*.pb): weights parameter is not used.
For TFLite format (*.tflite) weights parameter is not used.
:type weights: pathlib.Path
:param config: Optional map of pairs: (property name, property value) relevant only for this read operation.
:type config: dict, optional
:return: A model.
:rtype: openvino.runtime.Model
)");
Expand Down Expand Up @@ -653,7 +669,7 @@ void regclass_Core(py::module m) {
:param properties: Optional dict of pairs: (property name, property value)
:type properties: dict
:return: Pairs a operation name -> a device name supporting this operation.
:rtype: dict
:rtype: dict
)");

cls.def("add_extension",
Expand All @@ -671,7 +687,7 @@ void regclass_Core(py::module m) {
py::arg("extension"),
R"(
Registers an extension to a Core object.
:param extension: Extension object.
:type extension: openvino.runtime.Extension
)");
Expand Down
36 changes: 36 additions & 0 deletions src/bindings/python/tests/test_runtime/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,24 @@ def test_read_model_from_ir(request, tmp_path):
assert isinstance(model, Model)


# request - https://docs.pytest.org/en/7.1.x/reference/reference.html#request
def test_read_model_from_ir_with_user_config(request, tmp_path):
core = Core()
xml_path, bin_path = create_filenames_for_ir(request.node.name, tmp_path)
relu_model = get_relu_model()
serialize(relu_model, xml_path, bin_path)

core_cache_dir = core.get_property("CACHE_DIR")
cache_path = tmp_path / Path("cache")

model = core.read_model(xml_path, bin_path, config={"CACHE_DIR": f"{cache_path}"})

assert isinstance(model, Model)
assert core_cache_dir == core.get_property("CACHE_DIR")
assert os.path.exists(cache_path)
os.rmdir(cache_path)


# request - https://docs.pytest.org/en/7.1.x/reference/reference.html#request
def test_read_model_from_tensor(request, tmp_path):
core = Core()
Expand Down Expand Up @@ -178,6 +196,24 @@ def test_read_model_as_path(request, tmp_path):
assert isinstance(model, Model)


# request - https://docs.pytest.org/en/7.1.x/reference/reference.html#request
def test_read_model_as_path_with_user_config(request, tmp_path):
core = Core()
xml_path, bin_path = create_filenames_for_ir(request.node.name, tmp_path)
relu_model = get_relu_model()
serialize(relu_model, xml_path, bin_path)

core_cache_dir = core.get_property("CACHE_DIR")
cache_path = tmp_path / Path("cache_as_path")

model = core.read_model(Path(xml_path), Path(bin_path), config={"CACHE_DIR": f"{cache_path}"})

assert isinstance(model, Model)
assert core_cache_dir == core.get_property("CACHE_DIR")
assert os.path.exists(cache_path)
os.rmdir(cache_path)


# request - https://docs.pytest.org/en/7.1.x/reference/reference.html#request
def test_read_model_from_buffer(request, tmp_path):
core = Core()
Expand Down
36 changes: 36 additions & 0 deletions src/frontends/ir/tests/frontend_test_mmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,42 @@ TEST_F(IRFrontendMMapTestsAdvanced, core_enable_mmap_property) {
auto model = core.read_model(xmlFileName);
auto rss_read = ov::test::utils::getVmRSSInKB();

if (is_mmap != core.get_property("", ov::enable_mmap)) {
std::cout << "Test failed: core property is not set correctly" << std::endl;
exit(1);
}

bool is_weights_read = (rss_read - rss_init) > REF_RSS;
if (is_mmap == is_weights_read) {
std::cerr << "Test failed: mmap is " << (is_mmap ? "enabled" : "disabled") << ", but weights are "
<< (is_weights_read ? "read" : "not read") << " in RAM" << std::endl;
exit(1);
}
std::cerr << "Test passed" << std::endl;
exit(0);
};

for (const auto is_mmap : {true, false})
// Run test in a separate process to not affect RAM values by previous tests
EXPECT_EXIT(test(is_mmap), ::testing::ExitedWithCode(0), "Test passed");
}

TEST_F(IRFrontendMMapTestsAdvanced, core_enable_mmap_property_user_config) {
// Test checks that with enabled `mmap` .bin file
// isn't read into RAM on `read_model` stage.
// Otherwise, with disabled `mmap` .bin file should
// be in RAM

auto test = [&](const bool& is_mmap) {
auto rss_init = ov::test::utils::getVmRSSInKB();
auto model = core.read_model(xmlFileName, {}, {{ov::enable_mmap(is_mmap)}});
auto rss_read = ov::test::utils::getVmRSSInKB();

if (true != core.get_property("", ov::enable_mmap)) {
std::cout << "Test failed: core property changed by user configuration" << std::endl;
exit(1);
}

bool is_weights_read = (rss_read - rss_init) > REF_RSS;
if (is_mmap == is_weights_read) {
std::cerr << "Test failed: mmap is " << (is_mmap ? "enabled" : "disabled") << ", but weights are "
Expand Down
5 changes: 4 additions & 1 deletion src/inference/dev_api/openvino/runtime/icore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ class OPENVINO_RUNTIME_API ICore {
* @param model_path path to IR file
* @param bin_path path to bin file, if path is empty, will try to read bin file with the same name as xml and
* if bin file with the same name was not found, will load IR without weights.
* @param properties Optional map of pairs: (property name, property value) relevant only for this read operation.
* @return shared pointer to ov::Model
*/
virtual std::shared_ptr<ov::Model> read_model(const std::string& model_path, const std::string& bin_path) const = 0;
virtual std::shared_ptr<ov::Model> read_model(const std::string& model_path,
const std::string& bin_path,
const AnyMap& properties) const = 0;

virtual ov::AnyMap create_compile_config(const std::string& device_name, const ov::AnyMap& origConfig) const = 0;

Expand Down
52 changes: 46 additions & 6 deletions src/inference/include/openvino/runtime/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,14 @@ class OPENVINO_RUNTIME_API Core {
* For the following file formats the `bin_path` parameter is not used:
* * ONNX format (*.onnx)
* * PDPD (*.pdmodel)
* * TF (*.pb)
* * TF (*.pb, *.meta, SavedModel directory)
* * TFLite (*.tflite)
* @param properties Optional map of pairs: (property name, property value) relevant only for this read operation.
* @return A model.
*/
std::shared_ptr<ov::Model> read_model(const std::wstring& model_path, const std::wstring& bin_path = {}) const;
std::shared_ptr<ov::Model> read_model(const std::wstring& model_path,
const std::wstring& bin_path = {},
const ov::AnyMap& properties = {}) const;
#endif

/**
Expand All @@ -96,17 +99,54 @@ class OPENVINO_RUNTIME_API Core {
* For the following file formats the `bin_path` parameter is not used:
* * ONNX format (*.onnx)
* * PDPD (*.pdmodel)
* * TF (*.pb)
* * TF (*.pb, *.meta, SavedModel directory)
* * TFLite (*.tflite)
* @param properties Optional map of pairs: (property name, property value) relevant only for this read operation.
* @return A model.
* @{
*/
std::shared_ptr<ov::Model> read_model(const std::string& model_path, const std::string& bin_path = {}) const;
std::shared_ptr<ov::Model> read_model(const std::string& model_path,
const std::string& bin_path = {},
const ov::AnyMap& properties = {}) const;

#ifdef OPENVINO_CPP_VER_17
template <class Path, std::enable_if_t<std::is_same_v<Path, std::filesystem::path>>* = nullptr>
std::shared_ptr<ov::Model> read_model(const Path& model_path, const Path& bin_path = {}) const {
return read_model(model_path.string(), bin_path.string());
auto read_model(const Path& model_path, const Path& bin_path = {}, const ov::AnyMap& properties = {}) const {
return read_model(model_path.string(), bin_path.string(), properties);
}
#endif
/// @}

/**
* @brief Reads models from IR / ONNX / PDPD / TF / TFLite file formats.
*
* @param model_path Path to a model.
* @param bin_path Path to a data file.
* For IR format (*.bin):
* * if `bin_path` is empty, will try to read a bin file with the same name as xml and
* * if the bin file with the same name is not found, will load IR without weights.
* For the following file formats the `bin_path` parameter is not used:
* * ONNX format (*.onnx)
* * PDPD (*.pdmodel)
* * TF (*.pb, *.meta, SavedModel directory)
* * TFLite (*.tflite)
* @param properties Optional pack of pairs: (property name, property value) relevant only for this read operation.
* @return A model.
* @{
*/
template <typename... Properties>
util::EnableIfAllStringAny<CompiledModel, Properties...> read_model(const std::string& model_path,
const std::string& bin_path,
Properties&&... properties) const {
return read_model(model_path, bin_path, AnyMap{std::forward<Properties>(properties)...});
}

#ifdef OPENVINO_CPP_VER_17
template <class Path,
class... Properties,
std::enable_if_t<std::is_same_v<Path, std::filesystem::path> && (sizeof...(Properties) > 0)>* = nullptr>
auto read_model(const Path& model_path, const Path& bin_path, Properties&&... properties) const {
return read_model(model_path.string(), bin_path.string(), std::forward<Properties>(properties)...);
}
#endif
/// @}
Expand Down
15 changes: 10 additions & 5 deletions src/inference/src/cpp/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,19 @@ Core::Core(const std::string& xml_config_file) {
std::map<std::string, Version> Core::get_versions(const std::string& device_name) const {
OV_CORE_CALL_STATEMENT({ return _impl->get_versions(device_name); })}
#ifdef OPENVINO_ENABLE_UNICODE_PATH_SUPPORT
std::shared_ptr<ov::Model> Core::read_model(const std::wstring& model_path, const std::wstring& bin_path) const {
OV_CORE_CALL_STATEMENT(
return _impl->read_model(ov::util::wstring_to_string(model_path), ov::util::wstring_to_string(bin_path)););
std::shared_ptr<ov::Model> Core::read_model(const std::wstring& model_path,
const std::wstring& bin_path,
const ov::AnyMap& properties) const {
OV_CORE_CALL_STATEMENT(return _impl->read_model(ov::util::wstring_to_string(model_path),
ov::util::wstring_to_string(bin_path),
properties););
}
#endif

std::shared_ptr<ov::Model> Core::read_model(const std::string& model_path, const std::string& bin_path) const {
OV_CORE_CALL_STATEMENT(return _impl->read_model(model_path, bin_path););
std::shared_ptr<ov::Model> Core::read_model(const std::string& model_path,
const std::string& bin_path,
const AnyMap& properties) const {
OV_CORE_CALL_STATEMENT(return _impl->read_model(model_path, bin_path, properties););
}

std::shared_ptr<ov::Model> Core::read_model(const std::string& model, const ov::Tensor& weights) const {
Expand Down
Loading

0 comments on commit 4371116

Please sign in to comment.