[AUTO] Update AUTO logic to support state-full model (#21061)

* 1. Update the logic for filtering out the device that supports the stateful model.
2. Enable the function to create stateful model in the test case.

* 1. Enable unit test cases for stateful model support.
2. disable accelerator device(CPU_HELP) if model is stateful.

Signed-off-by: Wang, Yang <yang4.wang@intel.com>

* Update.

* Updated.

* Updated.

* Updated.

---------

Signed-off-by: Wang, Yang <yang4.wang@intel.com>
Co-authored-by: Chen Peter <peter.chen@intel.com>
This commit is contained in:
Wang, Yang 2023-12-04 10:51:10 +08:00 committed by GitHub
parent 44f7bf7e3f
commit 1e2b7c66f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 476 additions and 16 deletions

View File

@ -464,7 +464,7 @@ std::shared_ptr<ov::ICompiledModel> Plugin::compile_model_impl(const std::string
std::list<DeviceInformation> devices_with_priority(support_devices.begin(), support_devices.end());
std::shared_ptr<ov::Model> cloned_model, ppp_model;
if (model_path.empty()) {
support_devices = filter_device_by_model(support_devices_by_property, model);
support_devices = filter_device_by_model(support_devices_by_property, model, load_config);
cloned_model = model->clone();
ppp_model = cloned_model->clone();
@ -910,39 +910,83 @@ std::vector<DeviceInformation> Plugin::filter_device(const std::vector<DeviceInf
}
std::vector<DeviceInformation> Plugin::filter_device_by_model(const std::vector<DeviceInformation>& meta_devices,
const std::shared_ptr<const ov::Model>& model) const {
const std::shared_ptr<const ov::Model>& model,
PluginConfig& load_config) const {
if (meta_devices.empty()) {
OPENVINO_THROW("No available device to filter ", get_device_name(), " plugin");
}
std::vector<DeviceInformation> filter_device;
auto is_stateful = [&]() {
for (auto& op : model->get_ops()) {
if (std::dynamic_pointer_cast<ngraph::op::AssignBase>(op) ||
std::dynamic_pointer_cast<ngraph::op::ReadValueBase>(op)) {
LOG_INFO_TAG("stateful mode, try deployed to CPU");
return true;
}
auto disable_startup_runtime_fallback = [&]() {
if (load_config.get_property(ov::intel_auto::enable_startup_fallback)) {
LOG_WARNING_TAG("Setting property ov::intel_auto::enable_startup_fallback to false for stateful model.");
load_config.set_property(ov::intel_auto::enable_startup_fallback(false));
}
if (load_config.get_property(ov::intel_auto::enable_runtime_fallback)) {
LOG_WARNING_TAG("Setting property ov::intel_auto::enable_running_fallback to false for stateful model.");
load_config.set_property(ov::intel_auto::enable_runtime_fallback(false));
}
return false;
};
if (meta_devices.size() == 1) {
return meta_devices;
}
std::vector<DeviceInformation> filter_device;
std::vector<std::string> stateful_node_names;
// Check if CPU is in candidate list
auto cpuiter = std::find_if(meta_devices.begin(), meta_devices.end(), [](const DeviceInformation& device_info) {
return device_info.device_name.find("CPU") != std::string::npos;
});
// If CPU is in candidate list, load dynamic model to CPU first
// For MULTI do not only load stateful model to CPU
// For AUTO CTPUT only load stateful model to CPU
if (((model->is_dynamic()) || (is_stateful() && get_device_name() != "MULTI")) && cpuiter != meta_devices.end()) {
if (model->is_dynamic() && cpuiter != meta_devices.end()) {
filter_device.push_back(*cpuiter);
return filter_device;
}
// If CPU is not in candidate list, continue to run selection logic regardless of whether the input model is a
// dynamic model or not
return meta_devices;
for (auto& op : model->get_ops()) {
if (std::dynamic_pointer_cast<ov::op::util::AssignBase>(op) ||
std::dynamic_pointer_cast<ov::op::util::ReadValueBase>(op)) {
stateful_node_names.push_back(op->get_friendly_name());
}
}
if (stateful_node_names.empty()) {
// not stateful model
return meta_devices;
}
// disable CPU_HELP and runtime fallback if model is stateful
disable_startup_runtime_fallback();
auto is_supported_stateful = [&](const std::string& device_name, const ov::AnyMap& config) {
auto device_qm = get_core()->query_model(model, device_name, config);
for (auto&& node_name : stateful_node_names) {
if (device_qm.find(node_name) == device_qm.end())
return false;
}
return true;
};
for (auto& item : meta_devices) {
if (is_supported_stateful(item.device_name, item.config))
filter_device.push_back(item);
}
bool isCumulative = (get_device_name() == "MULTI") || (load_config.get_property(ov::hint::performance_mode) ==
ov::hint::PerformanceMode::CUMULATIVE_THROUGHPUT);
if (isCumulative) {
if (filter_device.empty() || filter_device.size() > 1)
OPENVINO_THROW("AUTO cumulative model doesn't support stateful model.");
else
return filter_device;
}
if (filter_device.empty()) {
return meta_devices;
}
return filter_device;
}
std::string Plugin::get_log_tag() const noexcept {

View File

@ -78,7 +78,8 @@ private:
std::vector<DeviceInformation> filter_device(const std::vector<DeviceInformation>& meta_devices,
const ov::AnyMap& properties) const;
std::vector<DeviceInformation> filter_device_by_model(const std::vector<DeviceInformation>& meta_devices,
const std::shared_ptr<const ov::Model>& model) const;
const std::shared_ptr<const ov::Model>& model,
PluginConfig& load_config) const;
std::string get_log_tag() const noexcept;
static std::mutex m_mtx;
static std::map<unsigned int, std::list<std::string>> m_priority_map;

View File

@ -0,0 +1,415 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <common_test_utils/common_utils.hpp>
#include <thread>
#include "include/auto_unit_test.hpp"
#include "openvino/opsets/opset11.hpp"
using StatefulModelConfigParams =
std::tuple<std::string, // device candidate list
bool, // is dynamic model
bool, // is stateful model
std::map<std::string, bool>, // device and the flag if device supports stateful
bool, // is cumulative mode
std::vector<std::pair<std::string, int>> // expected compiling model times on each device
>;
class StatefulModelSupportedTest : public tests::AutoTest, public ::testing::TestWithParam<StatefulModelConfigParams> {
public:
std::shared_ptr<ov::Model> create_dynamic_output_model();
std::shared_ptr<ov::Model> create_stateful_model();
static std::string getTestCaseName(testing::TestParamInfo<StatefulModelConfigParams> obj);
void SetUp() override;
protected:
bool isDynamicModel;
bool isStatefulModel;
std::map<std::string, bool> isDevSupportStatefulMap;
std::vector<std::pair<std::string, int>> expectedCalledTimes;
bool isCumulative;
std::string devicesList;
};
std::string StatefulModelSupportedTest::getTestCaseName(testing::TestParamInfo<StatefulModelConfigParams> obj) {
bool isDynamicModel;
bool isStatefulModel;
std::map<std::string, bool> isDevSupportStatefulMap;
std::vector<std::pair<std::string, int>> expectedCalledTimes;
bool isCumulative;
std::string devicesList;
std::tie(devicesList, isDynamicModel, isStatefulModel, isDevSupportStatefulMap, isCumulative, expectedCalledTimes) =
obj.param;
std::ostringstream result;
result << "_devicesList_" << devicesList;
result << "_isDynamic_" << isDynamicModel;
result << "_isStatefulModel_" << isStatefulModel;
for (auto& item : isDevSupportStatefulMap) {
result << "_" << item.first << "_" << item.second;
}
result << "_isCumulative_" << isCumulative;
for (auto& item : expectedCalledTimes) {
result << "_calling_on_" << item.first << "_expected_times_" << item.second;
}
auto string = result.str();
return string;
}
std::shared_ptr<ov::Model> StatefulModelSupportedTest::create_dynamic_output_model() {
auto boxes = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1, 2, 4});
boxes->set_friendly_name("param_1");
boxes->get_output_tensor(0).set_names({"input_tensor_1"});
auto scores = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1, 1, 2});
scores->set_friendly_name("param_2");
scores->get_output_tensor(0).set_names({"input_tensor_2"});
auto max_output_boxes_per_class = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{}, {10});
auto iou_threshold = ov::op::v0::Constant::create(ov::element::f32, ov::Shape{}, {0.75});
auto score_threshold = ov::op::v0::Constant::create(ov::element::f32, ov::Shape{}, {0.7});
auto nms = std::make_shared<ov::op::v9::NonMaxSuppression>(boxes,
scores,
max_output_boxes_per_class,
iou_threshold,
score_threshold);
auto res = std::make_shared<ov::op::v0::Result>(nms);
res->set_friendly_name("output_dynamic");
return std::make_shared<ov::Model>(ov::NodeVector{nms}, ov::ParameterVector{boxes, scores});
}
std::shared_ptr<ov::Model> StatefulModelSupportedTest::create_stateful_model() {
auto arg = std::make_shared<ov::opset11::Parameter>(ov::element::f32, ov::Shape{1, 1});
auto init_const = ov::opset11::Constant::create(ov::element::f32, ov::Shape{1, 1}, {0});
// The ReadValue/Assign operations must be used in pairs in the model.
// For each such a pair, its own variable object must be created.
const std::string variable_name("variable0");
// auto variable = std::make_shared<ov::op::util::Variable>(
// ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_name});
auto variable = std::make_shared<ov::op::util::Variable>(
ov::op::util::VariableInfo{init_const->get_shape(), ov::element::f32, variable_name});
// Creating ov::Model
auto read = std::make_shared<ov::opset11::ReadValue>(init_const, variable);
std::vector<std::shared_ptr<ov::Node>> args = {arg, read};
auto add = std::make_shared<ov::opset11::Add>(arg, read);
add->set_friendly_name("add_sum");
auto assign = std::make_shared<ov::opset11::Assign>(add, variable);
assign->set_friendly_name("save");
auto res = std::make_shared<ov::opset11::Result>(add);
res->set_friendly_name("res");
auto model =
std::make_shared<ov::Model>(ov::ResultVector({res}), ov::SinkVector({assign}), ov::ParameterVector({arg}));
return model;
}
void StatefulModelSupportedTest::SetUp() {
std::tie(devicesList, isDynamicModel, isStatefulModel, isDevSupportStatefulMap, isCumulative, expectedCalledTimes) =
GetParam();
if (isDynamicModel) {
model = create_dynamic_output_model();
} else if (isStatefulModel) {
model = create_stateful_model();
}
std::map<std::string, ov::SupportedOpsMap> devicesSupportedLayers;
for (auto& item : isDevSupportStatefulMap) {
ov::SupportedOpsMap res;
auto deviceName = item.first;
auto isSupportStateful = item.second;
std::unordered_set<std::string> device_supported_layers;
for (auto& op : model->get_ops()) {
if (!std::dynamic_pointer_cast<ngraph::op::AssignBase>(op) &&
!std::dynamic_pointer_cast<ngraph::op::ReadValueBase>(op)) {
res[op->get_friendly_name()] = deviceName;
continue;
}
if (isSupportStateful) {
res[op->get_friendly_name()] = deviceName;
}
}
devicesSupportedLayers[deviceName] = res;
}
for (auto& item : devicesSupportedLayers) {
ON_CALL(*core,
query_model(::testing::Matcher<const std::shared_ptr<const ov::Model>&>(_),
::testing::Matcher<const std::string&>(StrEq(item.first)),
_))
.WillByDefault(Return(item.second));
}
ON_CALL(*core,
compile_model(::testing::Matcher<const std::shared_ptr<const ov::Model>&>(_),
::testing::Matcher<const std::string&>(StrEq(ov::test::utils::DEVICE_CPU)),
(_)))
.WillByDefault(Return(mockExeNetwork));
ON_CALL(*core,
compile_model(::testing::Matcher<const std::shared_ptr<const ov::Model>&>(_),
::testing::Matcher<const std::string&>(StrEq(ov::test::utils::DEVICE_GPU)),
(_)))
.WillByDefault(Return(mockExeNetworkActual));
if (isCumulative)
plugin->set_device_name("MULTI");
else
plugin->set_device_name("AUTO");
}
TEST_P(StatefulModelSupportedTest, CanFilterOutCorrectTargetDeviceWithStatefulModel) {
metaDevices.clear();
int priority = 0;
for (auto& item : expectedCalledTimes) {
auto deviceName = item.first;
auto times = item.second;
DeviceInformation devInfo(deviceName, {}, -1, {}, deviceName, priority++);
metaDevices.push_back(devInfo);
if (times >= 0) {
EXPECT_CALL(*core,
compile_model(::testing::Matcher<const std::shared_ptr<const ov::Model>&>(_),
::testing::Matcher<const std::string&>(StrEq(deviceName)),
::testing::Matcher<const ov::AnyMap&>(_)))
.Times(times);
}
}
int expectedTimes = expectedCalledTimes.begin()->second;
ov::AnyMap config = {};
if (!devicesList.empty())
config.insert(ov::device::priorities(devicesList));
ON_CALL(*plugin, parse_meta_devices(_, _)).WillByDefault(Return(metaDevices));
ON_CALL(*plugin, get_valid_device)
.WillByDefault([](const std::vector<DeviceInformation>& metaDevices, const std::string& netPrecision) {
std::list<DeviceInformation> devices(metaDevices.begin(), metaDevices.end());
return devices;
});
config.insert(ov::intel_auto::enable_runtime_fallback(false));
if (isCumulative) {
config.insert(ov::hint::performance_mode(ov::hint::PerformanceMode::CUMULATIVE_THROUGHPUT));
}
if (expectedTimes < 0) {
ASSERT_THROW(plugin->compile_model(model, config), ov::Exception);
} else {
ASSERT_NO_THROW(plugin->compile_model(model, config));
}
}
const std::vector<StatefulModelConfigParams> testConfigs = {
// test cases for dynamic model
StatefulModelConfigParams{
"CPU", // device candidate list is CPU
true, // model is dynamic model
true, // model is stateful model
std::map<std::string, bool>{{"CPU", true}}, // device CPU supports stateful model
true, // performance mode is cumulative mode
std::vector<std::pair<std::string, int>>{{"CPU", 1}}}, // expected compiling model count is 1 on device CPU
StatefulModelConfigParams{"CPU",
true,
false,
std::map<std::string, bool>{{"CPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"CPU",
true,
true,
std::map<std::string, bool>{{"CPU", false}},
true,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"CPU",
true,
true,
std::map<std::string, bool>{{"CPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"CPU",
true,
true,
std::map<std::string, bool>{{"CPU", false}},
false,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"GPU",
true,
false,
std::map<std::string, bool>{{"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 1}}},
StatefulModelConfigParams{"GPU",
true,
false,
std::map<std::string, bool>{{"GPU", false}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 1}}},
StatefulModelConfigParams{"GPU",
true,
false,
std::map<std::string, bool>{{"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"GPU", 1}}},
StatefulModelConfigParams{"CPU,GPU",
true,
true,
std::map<std::string, bool>{{"CPU", true}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"CPU", 1}, {"GPU", 0}}},
StatefulModelConfigParams{"GPU,CPU",
true,
true,
std::map<std::string, bool>{{"CPU", true}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 0}, {"CPU", 1}}},
StatefulModelConfigParams{"CPU,GPU",
true,
false,
std::map<std::string, bool>{{"CPU", true}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"CPU", 1}, {"GPU", 0}}},
StatefulModelConfigParams{"GPU,CPU",
true,
false,
std::map<std::string, bool>{{"CPU", true}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 0}, {"CPU", 1}}},
StatefulModelConfigParams{"CPU",
false,
false,
std::map<std::string, bool>{{"CPU", true}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"CPU",
false,
false,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"GPU",
false,
false,
std::map<std::string, bool>{{"CPU", true}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 1}}},
StatefulModelConfigParams{"GPU",
false,
false,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"GPU", 1}}},
StatefulModelConfigParams{"CPU,GPU",
false,
false,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"CPU", 1}, {"GPU", 0}}},
StatefulModelConfigParams{"CPU,GPU",
false,
false,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"CPU", 1}, {"GPU", 1}}},
StatefulModelConfigParams{"GPU,CPU",
false,
false,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"GPU", 1}, {"CPU", 1}}},
StatefulModelConfigParams{"GPU,CPU",
false,
false,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 1}, {"CPU", 1}}},
StatefulModelConfigParams{"CPU",
false,
true,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"CPU",
false,
true,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"CPU",
false,
true,
std::map<std::string, bool>{{"CPU", true}, {"GPU", false}},
false,
std::vector<std::pair<std::string, int>>{{"CPU", 1}}},
StatefulModelConfigParams{"GPU",
false,
true,
std::map<std::string, bool>{{"CPU", true}, {"GPU", false}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 1}}},
StatefulModelConfigParams{"GPU",
false,
true,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 1}}},
StatefulModelConfigParams{"GPU",
false,
true,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"GPU", 1}}},
StatefulModelConfigParams{"CPU,GPU",
false,
true,
std::map<std::string, bool>{{"CPU", true}, {"GPU", false}},
false,
std::vector<std::pair<std::string, int>>{{"CPU", 1}, {"GPU", 0}}},
StatefulModelConfigParams{"CPU,GPU",
false,
true,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"GPU", 1}, {"CPU", 0}}},
StatefulModelConfigParams{"CPU,GPU",
false,
true,
std::map<std::string, bool>{{"CPU", true}, {"GPU", true}},
false,
std::vector<std::pair<std::string, int>>{{"GPU", 1}, {"CPU", 0}}},
StatefulModelConfigParams{"CPU,GPU",
false,
true,
std::map<std::string, bool>{{"CPU", false}, {"GPU", false}},
false,
std::vector<std::pair<std::string, int>>{{"GPU", 1}, {"CPU", 0}}},
StatefulModelConfigParams{"CPU,GPU",
false,
true,
std::map<std::string, bool>{{"CPU", true}, {"GPU", false}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 0}, {"CPU", 1}}},
StatefulModelConfigParams{"CPU,GPU",
false,
true,
std::map<std::string, bool>{{"CPU", false}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", 1}, {"CPU", 0}}},
StatefulModelConfigParams{"CPU,GPU",
false,
true,
std::map<std::string, bool>{{"CPU", true}, {"GPU", true}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", -1}, {"CPU", -1}}},
StatefulModelConfigParams{"CPU,GPU",
false,
true,
std::map<std::string, bool>{{"CPU", false}, {"GPU", false}},
true,
std::vector<std::pair<std::string, int>>{{"GPU", -1}, {"CPU", -1}}},
};
INSTANTIATE_TEST_SUITE_P(smoke_Auto_BehaviorTests,
StatefulModelSupportedTest,
::testing::ValuesIn(testConfigs),
StatefulModelSupportedTest::getTestCaseName);