Bell/release resource (#9222)
* recycle helper resources when hw is ready Signed-off-by: fishbell <bell.song@intel.com> * use cv to avoid additional while loop Signed-off-by: fishbell <bell.song@intel.com> * refine the logic Signed-off-by: fishbell <bell.song@intel.com> * fix potential threading issue Signed-off-by: fishbell <bell.song@intel.com> * refine logic Signed-off-by: fishbell <bell.song@intel.com> * avoid using global var Signed-off-by: fishbell <bell.song@intel.com> * clean up code Signed-off-by: fishbell <bell.song@intel.com> * refine Signed-off-by: fishbell <bell.song@intel.com> * release helper network/plugin also Signed-off-by: fishbell <bell.song@intel.com> * lock when release, avoid double release in destructor Signed-off-by: fishbell <bell.song@intel.com> * formatting Signed-off-by: fishbell <bell.song@intel.com> * add test case Signed-off-by: fishbell <bell.song@intel.com> * add case coverage Signed-off-by: fishbell <bell.song@intel.com> * move the task Signed-off-by: fishbell <bell.song@intel.com> * remove uncessary lock Signed-off-by: fishbell <bell.song@intel.com>
This commit is contained in:
parent
91c89e77d8
commit
3e9ae4bea7
@ -266,11 +266,34 @@ MultiDeviceExecutableNetwork::MultiDeviceExecutableNetwork(const std::string&
|
|||||||
_inferPipelineTasksDeviceSpecific["CPU_HELP"] = nullptr;
|
_inferPipelineTasksDeviceSpecific["CPU_HELP"] = nullptr;
|
||||||
_executor->run(_loadContext[CPU].task);
|
_executor->run(_loadContext[CPU].task);
|
||||||
_executor->run(_loadContext[ACTUALDEVICE].task);
|
_executor->run(_loadContext[ACTUALDEVICE].task);
|
||||||
|
auto recycleTask = [this]() mutable {
|
||||||
|
WaitActualNetworkReady();
|
||||||
|
while (!_exitFlag && _loadContext[ACTUALDEVICE].isAlready) {
|
||||||
|
// clean up helper infer requests
|
||||||
|
// first, wait for all the remaining requests to finish
|
||||||
|
for (auto& iter : _workerRequests["CPU_HELP"]) {
|
||||||
|
iter._inferRequest._ptr->Wait(InferRequest::WaitMode::RESULT_READY);
|
||||||
|
}
|
||||||
|
// late enough to check the idle queue now
|
||||||
|
// second, check the idle queue if all requests are in place
|
||||||
|
size_t destroynum = 0;
|
||||||
|
WorkerInferRequest *workerRequestPtr = nullptr;
|
||||||
|
while (_idleWorkerRequests["CPU_HELP"].try_pop(workerRequestPtr))
|
||||||
|
destroynum++;
|
||||||
|
if (destroynum == _workerRequests["CPU_HELP"].size()) {
|
||||||
|
std::lock_guard<std::mutex> lock(_confMutex);
|
||||||
|
_workerRequests["CPU_HELP"].clear();
|
||||||
|
_loadContext[CPU].executableNetwork._ptr.reset();
|
||||||
|
_loadContext[CPU].executableNetwork._so.reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_executor->run(std::move(recycleTask));
|
||||||
} else {
|
} else {
|
||||||
// only one device need to load network, do not need to load it async
|
// only one device need to load network, do not need to load it async
|
||||||
_loadContext[ACTUALDEVICE].task();
|
_loadContext[ACTUALDEVICE].task();
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitFirstNetworkReady();
|
WaitFirstNetworkReady();
|
||||||
}
|
}
|
||||||
void MultiDeviceExecutableNetwork::TryToLoadNetWork(AutoLoadContext& context,
|
void MultiDeviceExecutableNetwork::TryToLoadNetWork(AutoLoadContext& context,
|
||||||
@ -396,12 +419,6 @@ void MultiDeviceExecutableNetwork::WaitActualNetworkReady() const {
|
|||||||
if (_loadContext[ACTUALDEVICE].future.valid()) {
|
if (_loadContext[ACTUALDEVICE].future.valid()) {
|
||||||
_loadContext[ACTUALDEVICE].future.wait();
|
_loadContext[ACTUALDEVICE].future.wait();
|
||||||
}
|
}
|
||||||
// if _loadContext[ACTUALDEVICE] load failed, fall back to _loadContext[CPU]
|
|
||||||
if (!_loadContext[ACTUALDEVICE].isAlready) {
|
|
||||||
_loadContext[ACTUALDEVICE].executableNetwork = _loadContext[CPU].executableNetwork;
|
|
||||||
_loadContext[ACTUALDEVICE].deviceInfo = _loadContext[CPU].deviceInfo;
|
|
||||||
_loadContext[ACTUALDEVICE].isAlready = true;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,6 +492,7 @@ MultiDeviceExecutableNetwork::~MultiDeviceExecutableNetwork() {
|
|||||||
if (_workModeIsAUTO) {
|
if (_workModeIsAUTO) {
|
||||||
// this is necessary to guarantee member destroyed after getting future
|
// this is necessary to guarantee member destroyed after getting future
|
||||||
if (_loadContext[CPU].isEnabled) {
|
if (_loadContext[CPU].isEnabled) {
|
||||||
|
_exitFlag = true;
|
||||||
_loadContext[CPU].future.wait();
|
_loadContext[CPU].future.wait();
|
||||||
WaitActualNetworkReady();
|
WaitActualNetworkReady();
|
||||||
// it's necessary to wait the loading network threads to stop here.
|
// it's necessary to wait the loading network threads to stop here.
|
||||||
@ -495,7 +513,10 @@ MultiDeviceExecutableNetwork::~MultiDeviceExecutableNetwork() {
|
|||||||
// stop accepting any idle requests back (for re-scheduling)
|
// stop accepting any idle requests back (for re-scheduling)
|
||||||
idleWorker.second.set_capacity(0);
|
idleWorker.second.set_capacity(0);
|
||||||
}
|
}
|
||||||
_workerRequests.clear();
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_confMutex);
|
||||||
|
_workerRequests.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<InferenceEngine::RemoteContext> MultiDeviceExecutableNetwork::GetContext() const {
|
std::shared_ptr<InferenceEngine::RemoteContext> MultiDeviceExecutableNetwork::GetContext() const {
|
||||||
|
@ -152,7 +152,8 @@ private:
|
|||||||
std::promise<void> _firstLoadPromise;
|
std::promise<void> _firstLoadPromise;
|
||||||
mutable AutoLoadContext _loadContext[CONTEXTNUM];
|
mutable AutoLoadContext _loadContext[CONTEXTNUM];
|
||||||
mutable std::mutex _confMutex;
|
mutable std::mutex _confMutex;
|
||||||
|
bool _exitFlag = {false};
|
||||||
const InferenceEngine::CNNNetwork _network;
|
const InferenceEngine::CNNNetwork _network;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MultiDevicePlugin
|
} // namespace MultiDevicePlugin
|
190
src/tests/unit/auto/auto_release_helper_test.cpp
Normal file
190
src/tests/unit/auto/auto_release_helper_test.cpp
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
// Copyright (C) 2018-2021 Intel Corporation
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <ie_metric_helpers.hpp>
|
||||||
|
#include <common_test_utils/test_constants.hpp>
|
||||||
|
#include "unit_test_utils/mocks/cpp_interfaces/interface/mock_icore.hpp"
|
||||||
|
#include "unit_test_utils/mocks/mock_iinfer_request.hpp"
|
||||||
|
#include "unit_test_utils/mocks/cpp_interfaces/impl/mock_inference_plugin_internal.hpp"
|
||||||
|
#include "unit_test_utils/mocks/cpp_interfaces/interface/mock_iexecutable_network_internal.hpp"
|
||||||
|
#include "unit_test_utils/mocks/cpp_interfaces/interface/mock_ivariable_state_internal.hpp"
|
||||||
|
#include "unit_test_utils/mocks/cpp_interfaces/interface/mock_iinference_plugin.hpp"
|
||||||
|
#include <ie_core.hpp>
|
||||||
|
#include <multi-device/multi_device_config.hpp>
|
||||||
|
#include <ngraph_functions/subgraph_builders.hpp>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include "plugin/mock_auto_device_plugin.hpp"
|
||||||
|
#include "cpp/ie_plugin.hpp"
|
||||||
|
#include "mock_common.hpp"
|
||||||
|
|
||||||
|
using ::testing::MatcherCast;
|
||||||
|
using ::testing::AllOf;
|
||||||
|
using ::testing::Throw;
|
||||||
|
using ::testing::Matches;
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::StrEq;
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::Property;
|
||||||
|
using ::testing::Eq;
|
||||||
|
using ::testing::ReturnRef;
|
||||||
|
using ::testing::AtLeast;
|
||||||
|
using ::testing::AnyNumber;
|
||||||
|
using ::testing::InvokeWithoutArgs;
|
||||||
|
using ::testing::NiceMock;
|
||||||
|
using Config = std::map<std::string, std::string>;
|
||||||
|
using namespace MockMultiDevice;
|
||||||
|
|
||||||
|
using ConfigParams = std::tuple<
|
||||||
|
bool, // cpu load success
|
||||||
|
bool // hw device load success
|
||||||
|
>;
|
||||||
|
class AutoReleaseHelperTest : public ::testing::TestWithParam<ConfigParams> {
|
||||||
|
public:
|
||||||
|
std::shared_ptr<ngraph::Function> function;
|
||||||
|
InferenceEngine::CNNNetwork cnnNet;
|
||||||
|
std::shared_ptr<NiceMock<MockICore>> core;
|
||||||
|
std::shared_ptr<NiceMock<MockMultiDeviceInferencePlugin>> plugin;
|
||||||
|
|
||||||
|
//mock exeNetwork helper
|
||||||
|
ov::runtime::SoPtr<IExecutableNetworkInternal> mockExeNetwork;
|
||||||
|
//mock exeNetwork actual
|
||||||
|
ov::runtime::SoPtr<IExecutableNetworkInternal> mockExeNetworkActual;
|
||||||
|
// config for Auto device
|
||||||
|
std::map<std::string, std::string> config;
|
||||||
|
std::vector<DeviceInformation> metaDevices;
|
||||||
|
std::shared_ptr<NiceMock<MockIInferRequestInternal>> inferReqInternal;
|
||||||
|
std::shared_ptr<NiceMock<MockIInferRequestInternal>> inferReqInternalActual;
|
||||||
|
size_t optimalNum;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::string getTestCaseName(testing::TestParamInfo<ConfigParams> obj) {
|
||||||
|
bool cpuSuccess;
|
||||||
|
bool accSuccess;
|
||||||
|
std::tie(cpuSuccess, accSuccess) = obj.param;
|
||||||
|
std::ostringstream result;
|
||||||
|
if (!cpuSuccess) {
|
||||||
|
result << "cpuLoadFailure_";
|
||||||
|
} else {
|
||||||
|
result << "cpuLoadSuccess_";
|
||||||
|
}
|
||||||
|
if (!accSuccess) {
|
||||||
|
result << "accelerateorLoadFailure";
|
||||||
|
} else {
|
||||||
|
result << "accelerateorLoadSuccess";
|
||||||
|
}
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
core.reset();
|
||||||
|
plugin.reset();
|
||||||
|
//mockIExeNet.reset();
|
||||||
|
mockExeNetwork = {};
|
||||||
|
mockExeNetworkActual = {};
|
||||||
|
config.clear();
|
||||||
|
metaDevices.clear();
|
||||||
|
inferReqInternal.reset();
|
||||||
|
inferReqInternalActual.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
// prepare mockExeNetwork
|
||||||
|
auto mockIExeNet = std::make_shared<NiceMock<MockIExecutableNetworkInternal>>();
|
||||||
|
mockExeNetwork = {mockIExeNet, {}};
|
||||||
|
|
||||||
|
auto mockIExeNetActual = std::make_shared<NiceMock<MockIExecutableNetworkInternal>>();
|
||||||
|
mockExeNetworkActual = {mockIExeNetActual, {}};
|
||||||
|
|
||||||
|
// prepare mockicore and cnnNetwork for loading
|
||||||
|
core = std::make_shared<NiceMock<MockICore>>();
|
||||||
|
NiceMock<MockMultiDeviceInferencePlugin>* mock_multi = new NiceMock<MockMultiDeviceInferencePlugin>();
|
||||||
|
plugin.reset(mock_multi);
|
||||||
|
function = ngraph::builder::subgraph::makeConvPoolRelu();
|
||||||
|
cnnNet = InferenceEngine::CNNNetwork(function);
|
||||||
|
// replace core with mock Icore
|
||||||
|
plugin->SetCore(core);
|
||||||
|
// mock execNetwork can work
|
||||||
|
inferReqInternal = std::make_shared<NiceMock<MockIInferRequestInternal>>();
|
||||||
|
ON_CALL(*mockIExeNet.get(), CreateInferRequest()).WillByDefault(Return(inferReqInternal));
|
||||||
|
IE_SET_METRIC(OPTIMAL_NUMBER_OF_INFER_REQUESTS, optimalNum, 1);
|
||||||
|
ON_CALL(*mockIExeNet.get(), GetMetric(StrEq(METRIC_KEY(OPTIMAL_NUMBER_OF_INFER_REQUESTS))))
|
||||||
|
.WillByDefault(Return(optimalNum));
|
||||||
|
inferReqInternalActual = std::make_shared<NiceMock<MockIInferRequestInternal>>();
|
||||||
|
ON_CALL(*mockIExeNetActual.get(), CreateInferRequest()).WillByDefault(Return(inferReqInternalActual));
|
||||||
|
ON_CALL(*mockIExeNetActual.get(), GetMetric(StrEq(METRIC_KEY(OPTIMAL_NUMBER_OF_INFER_REQUESTS))))
|
||||||
|
.WillByDefault(Return(optimalNum));
|
||||||
|
IE_SET_METRIC(SUPPORTED_CONFIG_KEYS, supportConfigs, {});
|
||||||
|
ON_CALL(*core, GetMetric(_, StrEq(METRIC_KEY(SUPPORTED_CONFIG_KEYS)), _))
|
||||||
|
.WillByDefault(Return(supportConfigs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(AutoReleaseHelperTest, releaseResource) {
|
||||||
|
// get Parameter
|
||||||
|
bool cpuSuccess;
|
||||||
|
bool accSuccess;
|
||||||
|
std::tie(cpuSuccess, accSuccess) = this->GetParam();
|
||||||
|
size_t decreaseCount = 0;
|
||||||
|
// test auto plugin
|
||||||
|
config.insert({CONFIG_KEY_INTERNAL(MULTI_WORK_MODE_AS_AUTO), InferenceEngine::PluginConfigParams::YES});
|
||||||
|
const std::string strDevices = CommonTestUtils::DEVICE_GPU + std::string(",") +
|
||||||
|
CommonTestUtils::DEVICE_CPU;
|
||||||
|
|
||||||
|
if (accSuccess) {
|
||||||
|
ON_CALL(*core, LoadNetwork(::testing::Matcher<const InferenceEngine::CNNNetwork&>(_),
|
||||||
|
::testing::Matcher<const std::string&>(StrEq(CommonTestUtils::DEVICE_GPU)),
|
||||||
|
::testing::Matcher<const Config&>(_))).WillByDefault(InvokeWithoutArgs([this]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
return mockExeNetworkActual; }));
|
||||||
|
} else {
|
||||||
|
ON_CALL(*core, LoadNetwork(::testing::Matcher<const InferenceEngine::CNNNetwork&>(_),
|
||||||
|
::testing::Matcher<const std::string&>(StrEq(CommonTestUtils::DEVICE_GPU)),
|
||||||
|
::testing::Matcher<const Config&>(_))).WillByDefault(InvokeWithoutArgs([this]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
IE_THROW() << "";
|
||||||
|
return mockExeNetworkActual; }));
|
||||||
|
}
|
||||||
|
if (cpuSuccess) {
|
||||||
|
ON_CALL(*core, LoadNetwork(::testing::Matcher<const InferenceEngine::CNNNetwork&>(_),
|
||||||
|
::testing::Matcher<const std::string&>(StrEq(CommonTestUtils::DEVICE_CPU)),
|
||||||
|
::testing::Matcher<const Config&>(_))).WillByDefault(Return(mockExeNetwork));
|
||||||
|
if (accSuccess)
|
||||||
|
decreaseCount++;
|
||||||
|
} else {
|
||||||
|
ON_CALL(*core, LoadNetwork(::testing::Matcher<const InferenceEngine::CNNNetwork&>(_),
|
||||||
|
::testing::Matcher<const std::string&>(StrEq(CommonTestUtils::DEVICE_CPU)),
|
||||||
|
::testing::Matcher<const Config&>(_))).WillByDefault(Throw(InferenceEngine::GeneralError{""}));
|
||||||
|
}
|
||||||
|
metaDevices = {{CommonTestUtils::DEVICE_CPU, {}, -1}, {CommonTestUtils::DEVICE_GPU, {}, -1}};
|
||||||
|
DeviceInformation devInfo;
|
||||||
|
ON_CALL(*plugin, ParseMetaDevices(_, _)).WillByDefault(Return(metaDevices));
|
||||||
|
ON_CALL(*plugin, SelectDevice(Property(&std::vector<DeviceInformation>::size, Eq(2)), _, _))
|
||||||
|
.WillByDefault(Return(metaDevices[1]));
|
||||||
|
ON_CALL(*plugin, SelectDevice(Property(&std::vector<DeviceInformation>::size, Eq(1)), _, _))
|
||||||
|
.WillByDefault(Return(metaDevices[0]));
|
||||||
|
config.insert({InferenceEngine::MultiDeviceConfigParams::KEY_MULTI_DEVICE_PRIORITIES,
|
||||||
|
CommonTestUtils::DEVICE_CPU + std::string(",") + CommonTestUtils::DEVICE_GPU});
|
||||||
|
std::shared_ptr<InferenceEngine::IExecutableNetworkInternal> exeNetwork;
|
||||||
|
if (cpuSuccess || accSuccess)
|
||||||
|
ASSERT_NO_THROW(exeNetwork = plugin->LoadExeNetworkImpl(cnnNet, config));
|
||||||
|
else
|
||||||
|
ASSERT_THROW(exeNetwork = plugin->LoadExeNetworkImpl(cnnNet, config), InferenceEngine::Exception);
|
||||||
|
auto sharedcount = mockExeNetwork._ptr.use_count();
|
||||||
|
auto requestsharedcount = inferReqInternal.use_count();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
|
EXPECT_EQ(mockExeNetwork._ptr.use_count(), sharedcount - decreaseCount);
|
||||||
|
EXPECT_EQ(inferReqInternal.use_count(), requestsharedcount - decreaseCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
const std::vector<ConfigParams> testConfigs = {ConfigParams {true, true},
|
||||||
|
ConfigParams {true, false},
|
||||||
|
ConfigParams {false, true},
|
||||||
|
ConfigParams {false, false}
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(smoke_Auto_BehaviorTests, AutoReleaseHelperTest,
|
||||||
|
::testing::ValuesIn(testConfigs),
|
||||||
|
AutoReleaseHelperTest::getTestCaseName);
|
Loading…
Reference in New Issue
Block a user