1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 08:13:04 +04:00

core(parallel): plugins support

This commit is contained in:
Alexander Alekhin
2021-02-03 22:04:35 +00:00
parent e5d78960c6
commit cc73c36e32
35 changed files with 1509 additions and 354 deletions
+1 -26
View File
@@ -46,6 +46,7 @@
#include <opencv2/core/utils/trace.private.hpp>
#include "opencv2/core/parallel/parallel_backend.hpp"
#include "parallel/parallel.hpp"
#if defined _WIN32 || defined WINCE
#include <windows.h>
@@ -158,32 +159,6 @@ namespace cv {
ParallelLoopBody::~ParallelLoopBody() {}
namespace parallel {
static int numThreads = -1;
static
std::shared_ptr<ParallelForAPI>& getCurrentParallelForAPI()
{
static std::shared_ptr<ParallelForAPI> g_currentParallelForAPI;
return g_currentParallelForAPI;
}
ParallelForAPI::~ParallelForAPI()
{
// nothing
}
void setParallelForBackend(const std::shared_ptr<ParallelForAPI>& api, bool propagateNumThreads)
{
getCurrentParallelForAPI() = api;
if (propagateNumThreads && api)
{
setNumThreads(numThreads);
}
}
} // namespace
using namespace cv::parallel;
namespace {
@@ -0,0 +1,48 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_CORE_PARALLEL_FACTORY_HPP
#define OPENCV_CORE_PARALLEL_FACTORY_HPP
#include "opencv2/core/parallel/parallel_backend.hpp"
namespace cv { namespace parallel {
class IParallelBackendFactory
{
public:
virtual ~IParallelBackendFactory() {}
virtual std::shared_ptr<cv::parallel::ParallelForAPI> create() const = 0;
};
class StaticBackendFactory CV_FINAL: public IParallelBackendFactory
{
protected:
std::function<std::shared_ptr<cv::parallel::ParallelForAPI>(void)> create_fn_;
public:
StaticBackendFactory(std::function<std::shared_ptr<cv::parallel::ParallelForAPI>(void)>&& create_fn)
: create_fn_(create_fn)
{
// nothing
}
~StaticBackendFactory() CV_OVERRIDE {}
std::shared_ptr<cv::parallel::ParallelForAPI> create() const CV_OVERRIDE
{
return create_fn_();
}
};
//
// PluginBackendFactory is implemented in plugin_wrapper.cpp
//
std::shared_ptr<IParallelBackendFactory> createPluginParallelBackendFactory(const std::string& baseName);
}} // namespace
#endif // OPENCV_CORE_PARALLEL_FACTORY_HPP
+173
View File
@@ -0,0 +1,173 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#include "parallel.hpp"
#include <opencv2/core/utils/configuration.private.hpp>
#include <opencv2/core/utils/logger.defines.hpp>
#ifdef NDEBUG
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1
#else
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1
#endif
#include <opencv2/core/utils/logger.hpp>
#include "registry_parallel.hpp"
#include "registry_parallel.impl.hpp"
#include "plugin_parallel_api.hpp"
#include "plugin_parallel_wrapper.impl.hpp"
namespace cv { namespace parallel {
int numThreads = -1;
ParallelForAPI::~ParallelForAPI()
{
// nothing
}
static
std::string& getParallelBackendName()
{
static std::string g_backendName = toUpperCase(cv::utils::getConfigurationParameterString("OPENCV_PARALLEL_BACKEND", ""));
return g_backendName;
}
static bool g_initializedParallelForAPI = false;
static
std::shared_ptr<ParallelForAPI> createParallelForAPI()
{
const std::string& name = getParallelBackendName();
bool isKnown = false;
const auto& backends = getParallelBackendsInfo();
if (!name.empty())
{
CV_LOG_INFO(NULL, "core(parallel): requested backend name: " << name);
}
for (size_t i = 0; i < backends.size(); i++)
{
const auto& info = backends[i];
if (!name.empty())
{
if (name != info.name)
{
continue;
}
isKnown = true;
}
try
{
CV_LOG_DEBUG(NULL, "core(parallel): trying backend: " << info.name << " (priority=" << info.priority << ")");
CV_Assert(info.backendFactory);
std::shared_ptr<ParallelForAPI> backend = info.backendFactory->create();
if (!backend)
{
CV_LOG_VERBOSE(NULL, 0, "core(parallel): not available: " << info.name);
continue;
}
CV_LOG_INFO(NULL, "core(parallel): using backend: " << info.name << " (priority=" << info.priority << ")");
g_initializedParallelForAPI = true;
getParallelBackendName() = info.name;
return backend;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "core(parallel): can't initialize " << info.name << " backend: " << e.what());
}
catch (...)
{
CV_LOG_WARNING(NULL, "core(parallel): can't initialize " << info.name << " backend: Unknown C++ exception");
}
}
if (name.empty())
{
CV_LOG_DEBUG(NULL, "core(parallel): fallback on builtin code");
}
else
{
if (!isKnown)
CV_LOG_INFO(NULL, "core(parallel): unknown backend: " << name);
}
g_initializedParallelForAPI = true;
return std::shared_ptr<ParallelForAPI>();
}
static inline
std::shared_ptr<ParallelForAPI> createDefaultParallelForAPI()
{
CV_LOG_DEBUG(NULL, "core(parallel): Initializing parallel backend...");
return createParallelForAPI();
}
std::shared_ptr<ParallelForAPI>& getCurrentParallelForAPI()
{
static std::shared_ptr<ParallelForAPI> g_currentParallelForAPI = createDefaultParallelForAPI();
return g_currentParallelForAPI;
}
void setParallelForBackend(const std::shared_ptr<ParallelForAPI>& api, bool propagateNumThreads)
{
getCurrentParallelForAPI() = api;
if (propagateNumThreads && api)
{
setNumThreads(numThreads);
}
}
bool setParallelForBackend(const std::string& backendName, bool propagateNumThreads)
{
CV_TRACE_FUNCTION();
std::string backendName_u = toUpperCase(backendName);
if (g_initializedParallelForAPI)
{
// ... already initialized
if (getParallelBackendName() == backendName_u)
{
CV_LOG_INFO(NULL, "core(parallel): backend is already activated: " << (backendName.empty() ? "builtin(legacy)" : backendName));
return true;
}
else
{
// ... re-create new
CV_LOG_DEBUG(NULL, "core(parallel): replacing parallel backend...");
getParallelBackendName() = backendName_u;
getCurrentParallelForAPI() = createParallelForAPI();
}
}
else
{
// ... no backend exists, just specify the name (initialization is triggered by getCurrentParallelForAPI() call)
getParallelBackendName() = backendName_u;
}
std::shared_ptr<ParallelForAPI> api = getCurrentParallelForAPI();
if (!api)
{
if (!backendName.empty())
{
CV_LOG_WARNING(NULL, "core(parallel): backend is not available: " << backendName << " (using builtin legacy code)");
return false;
}
else
{
CV_LOG_WARNING(NULL, "core(parallel): switched to builtin code (legacy)");
}
}
if (!backendName_u.empty())
{
CV_Assert(backendName_u == getParallelBackendName()); // data race?
}
if (propagateNumThreads)
{
setNumThreads(numThreads);
}
return true;
}
}} // namespace
+29
View File
@@ -0,0 +1,29 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_CORE_SRC_PARALLEL_PARALLEL_HPP
#define OPENCV_CORE_SRC_PARALLEL_PARALLEL_HPP
#include "opencv2/core/parallel/parallel_backend.hpp"
namespace cv { namespace parallel {
extern int numThreads;
std::shared_ptr<ParallelForAPI>& getCurrentParallelForAPI();
#ifndef BUILD_PLUGIN
#ifdef HAVE_TBB
std::shared_ptr<cv::parallel::ParallelForAPI> createParallelBackendTBB();
#endif
#ifdef HAVE_OPENMP
std::shared_ptr<cv::parallel::ParallelForAPI> createParallelBackendOpenMP();
#endif
#endif // BUILD_PLUGIN
}} // namespace
#endif // OPENCV_CORE_SRC_PARALLEL_PARALLEL_HPP
@@ -0,0 +1,72 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#ifdef HAVE_OPENMP
#include "parallel.hpp"
#include "opencv2/core/parallel/backend/parallel_for.openmp.hpp"
namespace cv { namespace parallel {
static
std::shared_ptr<cv::parallel::openmp::ParallelForBackend>& getInstance()
{
static std::shared_ptr<cv::parallel::openmp::ParallelForBackend> g_instance = std::make_shared<cv::parallel::openmp::ParallelForBackend>();
return g_instance;
}
#ifndef BUILD_PLUGIN
std::shared_ptr<cv::parallel::ParallelForAPI> createParallelBackendOpenMP()
{
return getInstance();
}
#endif
}} // namespace
#ifdef BUILD_PLUGIN
#define ABI_VERSION 0
#define API_VERSION 0
#include "plugin_parallel_api.hpp"
static
CvResult cv_getInstance(CV_OUT CvPluginParallelBackendAPI* handle) CV_NOEXCEPT
{
try
{
if (!handle)
return CV_ERROR_FAIL;
*handle = cv::parallel::getInstance().get();
return CV_ERROR_OK;
}
catch (...)
{
return CV_ERROR_FAIL;
}
}
static const OpenCV_Core_Parallel_Plugin_API plugin_api =
{
{
sizeof(OpenCV_Core_Parallel_Plugin_API), ABI_VERSION, API_VERSION,
CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
"OpenMP (" CVAUX_STR(_OPENMP) ") OpenCV parallel plugin"
},
{
/* 1*/cv_getInstance
}
};
const OpenCV_Core_Parallel_Plugin_API* CV_API_CALL opencv_core_parallel_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
{
if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION)
return &plugin_api;
return NULL;
}
#endif // BUILD_PLUGIN
#endif // HAVE_TBB
@@ -0,0 +1,74 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#include "factory_parallel.hpp"
#ifdef HAVE_TBB
#include "parallel.hpp"
#include "opencv2/core/parallel/backend/parallel_for.tbb.hpp"
namespace cv { namespace parallel {
static
std::shared_ptr<cv::parallel::tbb::ParallelForBackend>& getInstance()
{
static std::shared_ptr<cv::parallel::tbb::ParallelForBackend> g_instance = std::make_shared<cv::parallel::tbb::ParallelForBackend>();
return g_instance;
}
#ifndef BUILD_PLUGIN
std::shared_ptr<cv::parallel::ParallelForAPI> createParallelBackendTBB()
{
return getInstance();
}
#endif
}} // namespace
#ifdef BUILD_PLUGIN
#define ABI_VERSION 0
#define API_VERSION 0
#include "plugin_parallel_api.hpp"
static
CvResult cv_getInstance(CV_OUT CvPluginParallelBackendAPI* handle) CV_NOEXCEPT
{
try
{
if (!handle)
return CV_ERROR_FAIL;
*handle = cv::parallel::getInstance().get();
return CV_ERROR_OK;
}
catch (...)
{
return CV_ERROR_FAIL;
}
}
static const OpenCV_Core_Parallel_Plugin_API plugin_api =
{
{
sizeof(OpenCV_Core_Parallel_Plugin_API), ABI_VERSION, API_VERSION,
CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
"TBB (interface " CVAUX_STR(TBB_INTERFACE_VERSION) ") OpenCV parallel plugin"
},
{
/* 1*/cv_getInstance
}
};
const OpenCV_Core_Parallel_Plugin_API* CV_API_CALL opencv_core_parallel_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
{
if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION)
return &plugin_api;
return NULL;
}
#endif // BUILD_PLUGIN
#endif // HAVE_TBB
@@ -0,0 +1,72 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef PARALLEL_PLUGIN_API_HPP
#define PARALLEL_PLUGIN_API_HPP
#include <opencv2/core/cvdef.h>
#include <opencv2/core/llapi/llapi.h>
#include "opencv2/core/parallel/parallel_backend.hpp"
#if !defined(BUILD_PLUGIN)
/// increased for backward-compatible changes, e.g. add new function
/// Caller API <= Plugin API -> plugin is fully compatible
/// Caller API > Plugin API -> plugin is not fully compatible, caller should use extra checks to use plugins with older API
#define API_VERSION 0 // preview
/// increased for incompatible changes, e.g. remove function argument
/// Caller ABI == Plugin ABI -> plugin is compatible
/// Caller ABI > Plugin ABI -> plugin is not compatible, caller should use shim code to use old ABI plugins (caller may know how lower ABI works, so it is possible)
/// Caller ABI < Plugin ABI -> plugin can't be used (plugin should provide interface with lower ABI to handle that)
#define ABI_VERSION 0 // preview
#else // !defined(BUILD_PLUGIN)
#if !defined(ABI_VERSION) || !defined(API_VERSION)
#error "Plugin must define ABI_VERSION and API_VERSION before including parallel_plugin_api.hpp"
#endif
#endif // !defined(BUILD_PLUGIN)
typedef cv::parallel::ParallelForAPI* CvPluginParallelBackendAPI;
struct OpenCV_Core_Parallel_Plugin_API_v0_0_api_entries
{
/** @brief Get parallel backend API instance
@param[out] handle pointer on backend API handle
@note API-CALL 1, API-Version == 0
*/
CvResult (CV_API_CALL *getInstance)(CV_OUT CvPluginParallelBackendAPI* handle) CV_NOEXCEPT;
}; // OpenCV_Core_Parallel_Plugin_API_v0_0_api_entries
typedef struct OpenCV_Core_Parallel_Plugin_API_v0
{
OpenCV_API_Header api_header;
struct OpenCV_Core_Parallel_Plugin_API_v0_0_api_entries v0;
} OpenCV_Core_Parallel_Plugin_API_v0;
#if ABI_VERSION == 0 && API_VERSION == 0
typedef OpenCV_Core_Parallel_Plugin_API_v0 OpenCV_Core_Parallel_Plugin_API;
#else
#error "Not supported configuration: check ABI_VERSION/API_VERSION"
#endif
#ifdef BUILD_PLUGIN
extern "C" {
CV_PLUGIN_EXPORTS
const OpenCV_Core_Parallel_Plugin_API* CV_API_CALL opencv_core_parallel_plugin_init_v0
(int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT;
} // extern "C"
#else // BUILD_PLUGIN
typedef const OpenCV_Core_Parallel_Plugin_API* (CV_API_CALL *FN_opencv_core_parallel_plugin_init_t)
(int requested_abi_version, int requested_api_version, void* reserved /*NULL*/);
#endif // BUILD_PLUGIN
#endif // PARALLEL_PLUGIN_API_HPP
@@ -0,0 +1,286 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Not a standalone header, part of parallel.cpp
//
//==================================================================================================
// Dynamic backend implementation
#include "opencv2/core/utils/plugin_loader.private.hpp"
namespace cv { namespace impl {
using namespace cv::parallel;
#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(PARALLEL_ENABLE_PLUGINS)
using namespace cv::plugin::impl; // plugin_loader.hpp
class PluginParallelBackend CV_FINAL: public std::enable_shared_from_this<PluginParallelBackend>
{
protected:
void initPluginAPI()
{
const char* init_name = "opencv_core_parallel_plugin_init_v0";
FN_opencv_core_parallel_plugin_init_t fn_init = reinterpret_cast<FN_opencv_core_parallel_plugin_init_t>(lib_->getSymbol(init_name));
if (fn_init)
{
CV_LOG_DEBUG(NULL, "Found entry: '" << init_name << "'");
for (int supported_api_version = API_VERSION; supported_api_version >= 0; supported_api_version--)
{
plugin_api_ = fn_init(ABI_VERSION, supported_api_version, NULL);
if (plugin_api_)
break;
}
if (!plugin_api_)
{
CV_LOG_INFO(NULL, "core(parallel): plugin is incompatible (can't be initialized): " << lib_->getName());
return;
}
if (!checkCompatibility(plugin_api_->api_header, ABI_VERSION, API_VERSION, false))
{
plugin_api_ = NULL;
return;
}
CV_LOG_INFO(NULL, "core(parallel): plugin is ready to use '" << plugin_api_->api_header.api_description << "'");
}
else
{
CV_LOG_INFO(NULL, "core(parallel): plugin is incompatible, missing init function: '" << init_name << "', file: " << lib_->getName());
}
}
bool checkCompatibility(const OpenCV_API_Header& api_header, unsigned int abi_version, unsigned int api_version, bool checkMinorOpenCVVersion)
{
if (api_header.opencv_version_major != CV_VERSION_MAJOR)
{
CV_LOG_ERROR(NULL, "core(parallel): wrong OpenCV major version used by plugin '" << api_header.api_description << "': " <<
cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor))
return false;
}
if (!checkMinorOpenCVVersion)
{
// no checks for OpenCV minor version
}
else if (api_header.opencv_version_minor != CV_VERSION_MINOR)
{
CV_LOG_ERROR(NULL, "core(parallel): wrong OpenCV minor version used by plugin '" << api_header.api_description << "': " <<
cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor))
return false;
}
CV_LOG_DEBUG(NULL, "core(parallel): initialized '" << api_header.api_description << "': built with "
<< cv::format("OpenCV %d.%d (ABI/API = %d/%d)",
api_header.opencv_version_major, api_header.opencv_version_minor,
api_header.min_api_version, api_header.api_version)
<< ", current OpenCV version is '" CV_VERSION "' (ABI/API = " << abi_version << "/" << api_version << ")"
);
if (api_header.min_api_version != abi_version) // future: range can be here
{
// actually this should never happen due to checks in plugin's init() function
CV_LOG_ERROR(NULL, "core(parallel): plugin is not supported due to incompatible ABI = " << api_header.min_api_version);
return false;
}
if (api_header.api_version != api_version)
{
CV_LOG_INFO(NULL, "core(parallel): NOTE: plugin is supported, but there is API version mismath: "
<< cv::format("plugin API level (%d) != OpenCV API level (%d)", api_header.api_version, api_version));
if (api_header.api_version < api_version)
{
CV_LOG_INFO(NULL, "core(parallel): NOTE: some functionality may be unavailable due to lack of support by plugin implementation");
}
}
return true;
}
public:
std::shared_ptr<cv::plugin::impl::DynamicLib> lib_;
const OpenCV_Core_Parallel_Plugin_API* plugin_api_;
PluginParallelBackend(const std::shared_ptr<cv::plugin::impl::DynamicLib>& lib)
: lib_(lib)
, plugin_api_(NULL)
{
initPluginAPI();
}
std::shared_ptr<cv::parallel::ParallelForAPI> create() const
{
CV_Assert(plugin_api_);
CvPluginParallelBackendAPI instancePtr = NULL;
if (plugin_api_->v0.getInstance)
{
if (CV_ERROR_OK == plugin_api_->v0.getInstance(&instancePtr))
{
CV_Assert(instancePtr);
// TODO C++20 "aliasing constructor"
return std::shared_ptr<cv::parallel::ParallelForAPI>(instancePtr, [](cv::parallel::ParallelForAPI*){}); // empty deleter
}
}
return std::shared_ptr<cv::parallel::ParallelForAPI>();
}
};
class PluginParallelBackendFactory CV_FINAL: public IParallelBackendFactory
{
public:
std::string baseName_;
std::shared_ptr<PluginParallelBackend> backend;
bool initialized;
public:
PluginParallelBackendFactory(const std::string& baseName)
: baseName_(baseName)
, initialized(false)
{
// nothing, plugins are loaded on demand
}
std::shared_ptr<cv::parallel::ParallelForAPI> create() const CV_OVERRIDE
{
if (!initialized)
{
const_cast<PluginParallelBackendFactory*>(this)->initBackend();
}
if (backend)
return backend->create();
return std::shared_ptr<cv::parallel::ParallelForAPI>();
}
protected:
void initBackend()
{
AutoLock lock(getInitializationMutex());
try
{
if (!initialized)
loadPlugin();
}
catch (...)
{
CV_LOG_INFO(NULL, "core(parallel): exception during plugin loading: " << baseName_ << ". SKIP");
}
initialized = true;
}
void loadPlugin();
};
static
std::vector<FileSystemPath_t> getPluginCandidates(const std::string& baseName)
{
using namespace cv::utils;
using namespace cv::utils::fs;
const std::string baseName_l = toLowerCase(baseName);
const std::string baseName_u = toUpperCase(baseName);
const FileSystemPath_t baseName_l_fs = toFileSystemPath(baseName_l);
std::vector<FileSystemPath_t> paths;
// TODO OPENCV_PLUGIN_PATH
const std::vector<std::string> paths_ = getConfigurationParameterPaths("OPENCV_CORE_PLUGIN_PATH", std::vector<std::string>());
if (paths_.size() != 0)
{
for (size_t i = 0; i < paths_.size(); i++)
{
paths.push_back(toFileSystemPath(paths_[i]));
}
}
else
{
FileSystemPath_t binaryLocation;
if (getBinLocation(binaryLocation))
{
binaryLocation = getParent(binaryLocation);
#ifndef CV_CORE_PARALLEL_PLUGIN_SUBDIRECTORY
paths.push_back(binaryLocation);
#else
paths.push_back(binaryLocation + toFileSystemPath("/") + toFileSystemPath(CV_CORE_PARALLEL_PLUGIN_SUBDIRECTORY_STR));
#endif
}
}
const std::string default_expr = libraryPrefix() + "opencv_core_parallel_" + baseName_l + "*" + librarySuffix();
const std::string plugin_expr = getConfigurationParameterString((std::string("OPENCV_CORE_PARALLEL_PLUGIN_") + baseName_u).c_str(), default_expr.c_str());
std::vector<FileSystemPath_t> results;
#ifdef _WIN32
FileSystemPath_t moduleName = toFileSystemPath(libraryPrefix() + "opencv_core_parallel_" + baseName_l + librarySuffix());
if (plugin_expr != default_expr)
{
moduleName = toFileSystemPath(plugin_expr);
results.push_back(moduleName);
}
for (const FileSystemPath_t& path : paths)
{
results.push_back(path + L"\\" + moduleName);
}
results.push_back(moduleName);
#else
CV_LOG_DEBUG(NULL, "core(parallel): " << baseName << " plugin's glob is '" << plugin_expr << "', " << paths.size() << " location(s)");
for (const std::string& path : paths)
{
if (path.empty())
continue;
std::vector<std::string> candidates;
cv::glob(utils::fs::join(path, plugin_expr), candidates);
CV_LOG_DEBUG(NULL, " - " << path << ": " << candidates.size());
copy(candidates.begin(), candidates.end(), back_inserter(results));
}
#endif
CV_LOG_DEBUG(NULL, "Found " << results.size() << " plugin(s) for " << baseName);
return results;
}
void PluginParallelBackendFactory::loadPlugin()
{
for (const FileSystemPath_t& plugin : getPluginCandidates(baseName_))
{
auto lib = std::make_shared<cv::plugin::impl::DynamicLib>(plugin);
if (!lib->isLoaded())
{
continue;
}
try
{
auto pluginBackend = std::make_shared<PluginParallelBackend>(lib);
if (!pluginBackend)
{
continue;
}
if (pluginBackend->plugin_api_ == NULL)
{
CV_LOG_ERROR(NULL, "core(parallel): no compatible plugin API for backend: " << baseName_ << " in " << toPrintablePath(plugin));
continue;
}
#if !defined(_WIN32)
// NB: we are going to use parallel backend, so prevent automatic library unloading
// (avoid uncontrolled crashes in worker threads of underlying libraries: libgomp, libtbb)
// details: https://github.com/opencv/opencv/pull/19470#pullrequestreview-589834777
lib->disableAutomaticLibraryUnloading();
#endif
backend = pluginBackend;
return;
}
catch (...)
{
CV_LOG_WARNING(NULL, "core(parallel): exception during plugin initialization: " << toPrintablePath(plugin) << ". SKIP");
}
}
}
#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(PARALLEL_ENABLE_PLUGINS)
} // namespace
namespace parallel {
std::shared_ptr<IParallelBackendFactory> createPluginParallelBackendFactory(const std::string& baseName)
{
#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(PARALLEL_ENABLE_PLUGINS)
return std::make_shared<impl::PluginParallelBackendFactory>(baseName);
#else
return std::shared_ptr<IParallelBackendFactory>();
#endif
}
}} // namespace
@@ -0,0 +1,25 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_CORE_PARALLEL_REGISTRY_HPP
#define OPENCV_CORE_PARALLEL_REGISTRY_HPP
#include "factory_parallel.hpp"
namespace cv { namespace parallel {
struct ParallelBackendInfo
{
int priority; // 1000-<index*10> - default builtin priority
// 0 - disabled (OPENCV_PARALLEL_PRIORITY_<name> = 0)
// >10000 - prioritized list (OPENCV_PARALLEL_PRIORITY_LIST)
std::string name;
std::shared_ptr<IParallelBackendFactory> backendFactory;
};
const std::vector<ParallelBackendInfo>& getParallelBackendsInfo();
}} // namespace
#endif // OPENCV_CORE_PARALLEL_REGISTRY_HPP
@@ -0,0 +1,167 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Not a standalone header, part of parallel.cpp
//
namespace cv { namespace parallel {
#define DECLARE_DYNAMIC_BACKEND(name) \
ParallelBackendInfo { \
1000, name, createPluginParallelBackendFactory(name) \
}
#define DECLARE_STATIC_BACKEND(name, createBackendAPI) \
ParallelBackendInfo { \
1000, name, std::make_shared<cv::parallel::StaticBackendFactory>([=] () -> std::shared_ptr<cv::parallel::ParallelForAPI> { return createBackendAPI(); }) \
}
static
std::vector<ParallelBackendInfo>& getBuiltinParallelBackendsInfo()
{
static std::vector<ParallelBackendInfo> g_backends
{
#ifdef HAVE_TBB
DECLARE_STATIC_BACKEND("TBB", createParallelBackendTBB),
#elif defined(PARALLEL_ENABLE_PLUGINS)
DECLARE_DYNAMIC_BACKEND("ONETBB"), // dedicated oneTBB plugin (interface >= 12000, binary incompatibe with TBB 2017-2020)
DECLARE_DYNAMIC_BACKEND("TBB"), // generic TBB plugins
#endif
#ifdef HAVE_OPENMP
DECLARE_STATIC_BACKEND("OPENMP", createParallelBackendOpenMP),
#elif defined(PARALLEL_ENABLE_PLUGINS)
DECLARE_DYNAMIC_BACKEND("OPENMP") // TODO Intel OpenMP?
#endif
};
return g_backends;
};
static
bool sortByPriority(const ParallelBackendInfo &lhs, const ParallelBackendInfo &rhs)
{
return lhs.priority > rhs.priority;
}
/** @brief Manages list of enabled backends
*/
class ParallelBackendRegistry
{
protected:
std::vector<ParallelBackendInfo> enabledBackends;
ParallelBackendRegistry()
{
enabledBackends = getBuiltinParallelBackendsInfo();
int N = (int)enabledBackends.size();
for (int i = 0; i < N; i++)
{
ParallelBackendInfo& info = enabledBackends[i];
info.priority = 1000 - i * 10;
}
CV_LOG_DEBUG(NULL, "core(parallel): Builtin backends(" << N << "): " << dumpBackends());
if (readPrioritySettings())
{
CV_LOG_INFO(NULL, "core(parallel): Updated backends priorities: " << dumpBackends());
N = (int)enabledBackends.size();
}
int enabled = 0;
for (int i = 0; i < N; i++)
{
ParallelBackendInfo& info = enabledBackends[enabled];
if (enabled != i)
info = enabledBackends[i];
size_t param_priority = utils::getConfigurationParameterSizeT(cv::format("OPENCV_PARALLEL_PRIORITY_%s", info.name.c_str()).c_str(), (size_t)info.priority);
CV_Assert(param_priority == (size_t)(int)param_priority); // overflow check
if (param_priority > 0)
{
info.priority = (int)param_priority;
enabled++;
}
else
{
CV_LOG_INFO(NULL, "core(parallel): Disable backend: " << info.name);
}
}
enabledBackends.resize(enabled);
CV_LOG_DEBUG(NULL, "core(parallel): Available backends(" << enabled << "): " << dumpBackends());
std::sort(enabledBackends.begin(), enabledBackends.end(), sortByPriority);
CV_LOG_INFO(NULL, "core(parallel): Enabled backends(" << enabled << ", sorted by priority): " << (enabledBackends.empty() ? std::string("N/A") : dumpBackends()));
}
static std::vector<std::string> tokenize_string(const std::string& input, char token)
{
std::vector<std::string> result;
std::string::size_type prev_pos = 0, pos = 0;
while((pos = input.find(token, pos)) != std::string::npos)
{
result.push_back(input.substr(prev_pos, pos-prev_pos));
prev_pos = ++pos;
}
result.push_back(input.substr(prev_pos));
return result;
}
bool readPrioritySettings()
{
bool hasChanges = false;
cv::String prioritized_backends = utils::getConfigurationParameterString("OPENCV_PARALLEL_PRIORITY_LIST", NULL);
if (prioritized_backends.empty())
return hasChanges;
CV_LOG_INFO(NULL, "core(parallel): Configured priority list (OPENCV_PARALLEL_PRIORITY_LIST): " << prioritized_backends);
const std::vector<std::string> names = tokenize_string(prioritized_backends, ',');
for (size_t i = 0; i < names.size(); i++)
{
const std::string& name = names[i];
int priority = (int)(100000 + (names.size() - i) * 1000);
bool found = false;
for (size_t k = 0; k < enabledBackends.size(); k++)
{
ParallelBackendInfo& info = enabledBackends[k];
if (name == info.name)
{
info.priority = priority;
CV_LOG_DEBUG(NULL, "core(parallel): New backend priority: '" << name << "' => " << info.priority);
found = true;
hasChanges = true;
break;
}
}
if (!found)
{
CV_LOG_INFO(NULL, "core(parallel): Adding parallel backend (plugin): '" << name << "'");
enabledBackends.push_back(ParallelBackendInfo{priority, name, createPluginParallelBackendFactory(name)});
hasChanges = true;
}
}
return hasChanges;
}
public:
std::string dumpBackends() const
{
std::ostringstream os;
for (size_t i = 0; i < enabledBackends.size(); i++)
{
if (i > 0) os << "; ";
const ParallelBackendInfo& info = enabledBackends[i];
os << info.name << '(' << info.priority << ')';
}
return os.str();
}
static ParallelBackendRegistry& getInstance()
{
static ParallelBackendRegistry g_instance;
return g_instance;
}
inline const std::vector<ParallelBackendInfo>& getEnabledBackends() const { return enabledBackends; }
};
const std::vector<ParallelBackendInfo>& getParallelBackendsInfo()
{
return cv::parallel::ParallelBackendRegistry::getInstance().getEnabledBackends();
}
}} // namespace
+6 -1
View File
@@ -43,6 +43,10 @@
#ifndef __OPENCV_PRECOMP_H__
#define __OPENCV_PRECOMP_H__
#ifdef BUILD_PLUGIN
#include "opencv2/core/utility.hpp"
#else // BUILD_PLUGIN
#include "opencv2/opencv_modules.hpp"
#include "cvconfig.h"
@@ -375,4 +379,5 @@ int cv_snprintf(char* buf, int len, const char* fmt, ...);
int cv_vsnprintf(char* buf, int len, const char* fmt, va_list args);
}
#endif /*_CXCORE_INTERNAL_H_*/
#endif // BUILD_PLUGIN
#endif // __OPENCV_PRECOMP_H__
+5
View File
@@ -587,3 +587,8 @@ cv::String getCacheDirectory(const char* /*sub_directory_name*/, const char* /*c
#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT
}}} // namespace
#if OPENCV_HAVE_FILESYSTEM_SUPPORT
#include "plugin_loader.impl.hpp"
#endif
@@ -0,0 +1,80 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Not a standalone header, part of filesystem.cpp
//
#include "opencv2/core/utils/plugin_loader.private.hpp"
#if !OPENCV_HAVE_FILESYSTEM_SUPPORT
#error "Invalid build configuration"
#endif
#if 0 // TODO
#ifdef NDEBUG
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1
#else
#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1
#endif
#include <opencv2/core/utils/logger.hpp>
#endif
namespace cv { namespace plugin { namespace impl {
DynamicLib::DynamicLib(const FileSystemPath_t& filename)
: handle(0), fname(filename), disableAutoUnloading_(false)
{
libraryLoad(filename);
}
DynamicLib::~DynamicLib()
{
if (!disableAutoUnloading_)
{
libraryRelease();
}
else if (handle)
{
CV_LOG_INFO(NULL, "skip auto unloading (disabled): " << toPrintablePath(fname));
handle = 0;
}
}
void* DynamicLib::getSymbol(const char* symbolName) const
{
if (!handle)
{
return 0;
}
void* res = getSymbol_(handle, symbolName);
if (!res)
{
CV_LOG_DEBUG(NULL, "No symbol '" << symbolName << "' in " << toPrintablePath(fname));
}
return res;
}
const std::string DynamicLib::getName() const
{
return toPrintablePath(fname);
}
void DynamicLib::libraryLoad(const FileSystemPath_t& filename)
{
handle = libraryLoad_(filename);
CV_LOG_INFO(NULL, "load " << toPrintablePath(filename) << " => " << (handle ? "OK" : "FAILED"));
}
void DynamicLib::libraryRelease()
{
if (handle)
{
CV_LOG_INFO(NULL, "unload "<< toPrintablePath(fname));
libraryRelease_(handle);
handle = 0;
}
}
}}} // namespace