mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
Merge pull request #19365 from alalek:parallel_api
This commit is contained in:
commit
37c12db366
@ -26,6 +26,7 @@
|
|||||||
opencv2/core/hal/*.impl.*
|
opencv2/core/hal/*.impl.*
|
||||||
opencv2/core/cuda*
|
opencv2/core/cuda*
|
||||||
opencv2/core/opencl*
|
opencv2/core/opencl*
|
||||||
|
opencv2/core/parallel/backend/*
|
||||||
opencv2/core/private*
|
opencv2/core/private*
|
||||||
opencv2/core/quaternion*
|
opencv2/core/quaternion*
|
||||||
opencv/cxeigen.hpp
|
opencv/cxeigen.hpp
|
||||||
|
@ -227,6 +227,7 @@ INCLUDE_PATH =
|
|||||||
INCLUDE_FILE_PATTERNS =
|
INCLUDE_FILE_PATTERNS =
|
||||||
PREDEFINED = __cplusplus=1 \
|
PREDEFINED = __cplusplus=1 \
|
||||||
CVAPI(x)=x \
|
CVAPI(x)=x \
|
||||||
|
CV_API_CALL= \
|
||||||
CV_DOXYGEN= \
|
CV_DOXYGEN= \
|
||||||
CV_EXPORTS= \
|
CV_EXPORTS= \
|
||||||
CV_EXPORTS_W= \
|
CV_EXPORTS_W= \
|
||||||
|
@ -58,10 +58,15 @@ file(GLOB_RECURSE module_opencl_hdrs
|
|||||||
source_group("Include\\Cuda Headers" FILES ${lib_cuda_hdrs})
|
source_group("Include\\Cuda Headers" FILES ${lib_cuda_hdrs})
|
||||||
source_group("Include\\Cuda Headers\\Detail" FILES ${lib_cuda_hdrs_detail})
|
source_group("Include\\Cuda Headers\\Detail" FILES ${lib_cuda_hdrs_detail})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE core_parallel_hdrs
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/parallel/*.hpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/parallel/*.h")
|
||||||
|
ocv_source_group("Include" DIRBASE "${CMAKE_CURRENT_LIST_DIR}/include" FILES ${core_parallel_hdrs})
|
||||||
|
|
||||||
source_group("Src" FILES "${OPENCV_MODULE_opencv_core_BINARY_DIR}/version_string.inc")
|
source_group("Src" FILES "${OPENCV_MODULE_opencv_core_BINARY_DIR}/version_string.inc")
|
||||||
|
|
||||||
ocv_glob_module_sources(SOURCES "${OPENCV_MODULE_opencv_core_BINARY_DIR}/version_string.inc"
|
ocv_glob_module_sources(SOURCES "${OPENCV_MODULE_opencv_core_BINARY_DIR}/version_string.inc"
|
||||||
HEADERS ${module_opencl_hdrs} ${lib_cuda_hdrs} ${lib_cuda_hdrs_detail})
|
HEADERS ${core_parallel_hdrs} ${module_opencl_hdrs} ${lib_cuda_hdrs} ${lib_cuda_hdrs_detail})
|
||||||
|
|
||||||
ocv_module_include_directories(${the_module} ${ZLIB_INCLUDE_DIRS} ${OPENCL_INCLUDE_DIRS})
|
ocv_module_include_directories(${the_module} ${ZLIB_INCLUDE_DIRS} ${OPENCL_INCLUDE_DIRS})
|
||||||
if(ANDROID AND HAVE_CPUFEATURES)
|
if(ANDROID AND HAVE_CPUFEATURES)
|
||||||
|
@ -97,6 +97,10 @@
|
|||||||
@}
|
@}
|
||||||
@defgroup core_lowlevel_api Low-level API for external libraries / plugins
|
@defgroup core_lowlevel_api Low-level API for external libraries / plugins
|
||||||
@}
|
@}
|
||||||
|
@defgroup core_parallel Parallel Processing
|
||||||
|
@{
|
||||||
|
@defgroup core_parallel_backend Parallel backends API
|
||||||
|
@}
|
||||||
@}
|
@}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -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 OPENCV_CORE_PARALLEL_FOR_OPENMP_HPP
|
||||||
|
#define OPENCV_CORE_PARALLEL_FOR_OPENMP_HPP
|
||||||
|
|
||||||
|
#include "opencv2/core/parallel/parallel_backend.hpp"
|
||||||
|
|
||||||
|
#if !defined(_OPENMP) && !defined(OPENCV_SKIP_OPENMP_PRESENSE_CHECK)
|
||||||
|
#error "This file must be compiled with enabled OpenMP"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <omp.h>
|
||||||
|
|
||||||
|
namespace cv { namespace parallel { namespace openmp {
|
||||||
|
|
||||||
|
/** OpenMP parallel_for API implementation
|
||||||
|
*
|
||||||
|
* @sa setParallelForBackend
|
||||||
|
* @ingroup core_parallel_backend
|
||||||
|
*/
|
||||||
|
class ParallelForBackend : public ParallelForAPI
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
int numThreads;
|
||||||
|
int numThreadsMax;
|
||||||
|
public:
|
||||||
|
ParallelForBackend()
|
||||||
|
{
|
||||||
|
numThreads = 0;
|
||||||
|
numThreadsMax = omp_get_max_threads();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ParallelForBackend() {}
|
||||||
|
|
||||||
|
virtual void parallel_for(int tasks, FN_parallel_for_body_cb_t body_callback, void* callback_data) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
#pragma omp parallel for schedule(dynamic) num_threads(numThreads > 0 ? numThreads : numThreadsMax)
|
||||||
|
for (int i = 0; i < tasks; ++i)
|
||||||
|
body_callback(i, i + 1, callback_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int getThreadNum() const CV_OVERRIDE
|
||||||
|
{
|
||||||
|
return omp_get_thread_num();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int getNumThreads() const CV_OVERRIDE
|
||||||
|
{
|
||||||
|
return numThreads > 0
|
||||||
|
? numThreads
|
||||||
|
: numThreadsMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int setNumThreads(int nThreads) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
int oldNumThreads = numThreads;
|
||||||
|
numThreads = nThreads;
|
||||||
|
// nothing needed as numThreads is used in #pragma omp parallel for directly
|
||||||
|
return oldNumThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getName() const CV_OVERRIDE
|
||||||
|
{
|
||||||
|
return "openmp";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}}} // namespace
|
||||||
|
|
||||||
|
#endif // OPENCV_CORE_PARALLEL_FOR_OPENMP_HPP
|
@ -0,0 +1,153 @@
|
|||||||
|
// 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_FOR_TBB_HPP
|
||||||
|
#define OPENCV_CORE_PARALLEL_FOR_TBB_HPP
|
||||||
|
|
||||||
|
#include "opencv2/core/parallel/parallel_backend.hpp"
|
||||||
|
#include <opencv2/core/utils/logger.hpp>
|
||||||
|
|
||||||
|
#ifndef TBB_SUPPRESS_DEPRECATED_MESSAGES // supress warning
|
||||||
|
#define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
|
||||||
|
#endif
|
||||||
|
#include "tbb/tbb.h"
|
||||||
|
#if !defined(TBB_INTERFACE_VERSION)
|
||||||
|
#error "Unknows/unsupported TBB version"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if TBB_INTERFACE_VERSION >= 8000
|
||||||
|
#include "tbb/task_arena.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cv { namespace parallel { namespace tbb {
|
||||||
|
|
||||||
|
using namespace ::tbb;
|
||||||
|
|
||||||
|
#if TBB_INTERFACE_VERSION >= 8000
|
||||||
|
static tbb::task_arena& getArena()
|
||||||
|
{
|
||||||
|
static tbb::task_arena tbbArena(tbb::task_arena::automatic);
|
||||||
|
return tbbArena;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static tbb::task_scheduler_init& getScheduler()
|
||||||
|
{
|
||||||
|
static tbb::task_scheduler_init tbbScheduler(tbb::task_scheduler_init::deferred);
|
||||||
|
return tbbScheduler;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** OpenMP parallel_for API implementation
|
||||||
|
*
|
||||||
|
* @sa setParallelForBackend
|
||||||
|
* @ingroup core_parallel_backend
|
||||||
|
*/
|
||||||
|
class ParallelForBackend : public ParallelForAPI
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
int numThreads;
|
||||||
|
int numThreadsMax;
|
||||||
|
public:
|
||||||
|
ParallelForBackend()
|
||||||
|
{
|
||||||
|
CV_LOG_INFO(NULL, "Initializing TBB parallel backend: TBB_INTERFACE_VERSION=" << TBB_INTERFACE_VERSION);
|
||||||
|
numThreads = 0;
|
||||||
|
#if TBB_INTERFACE_VERSION >= 8000
|
||||||
|
(void)getArena();
|
||||||
|
#else
|
||||||
|
(void)getScheduler();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ParallelForBackend() {}
|
||||||
|
|
||||||
|
class CallbackProxy
|
||||||
|
{
|
||||||
|
const FN_parallel_for_body_cb_t& callback;
|
||||||
|
void* const callback_data;
|
||||||
|
const int tasks;
|
||||||
|
public:
|
||||||
|
inline CallbackProxy(int tasks_, FN_parallel_for_body_cb_t& callback_, void* callback_data_)
|
||||||
|
: callback(callback_), callback_data(callback_data_), tasks(tasks_)
|
||||||
|
{
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const tbb::blocked_range<int>& range) const
|
||||||
|
{
|
||||||
|
this->callback(range.begin(), range.end(), callback_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()() const
|
||||||
|
{
|
||||||
|
tbb::parallel_for(tbb::blocked_range<int>(0, tasks), *this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void parallel_for(int tasks, FN_parallel_for_body_cb_t body_callback, void* callback_data) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
CallbackProxy task(tasks, body_callback, callback_data);
|
||||||
|
#if TBB_INTERFACE_VERSION >= 8000
|
||||||
|
getArena().execute(task);
|
||||||
|
#else
|
||||||
|
task();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int getThreadNum() const CV_OVERRIDE
|
||||||
|
{
|
||||||
|
#if TBB_INTERFACE_VERSION >= 9100
|
||||||
|
return tbb::this_task_arena::current_thread_index();
|
||||||
|
#elif TBB_INTERFACE_VERSION >= 8000
|
||||||
|
return tbb::task_arena::current_thread_index();
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int getNumThreads() const CV_OVERRIDE
|
||||||
|
{
|
||||||
|
#if TBB_INTERFACE_VERSION >= 9100
|
||||||
|
return getArena().max_concurrency();
|
||||||
|
#elif TBB_INTERFACE_VERSION >= 8000
|
||||||
|
return numThreads > 0
|
||||||
|
? numThreads
|
||||||
|
: tbb::task_scheduler_init::default_num_threads();
|
||||||
|
#else
|
||||||
|
return getScheduler().is_active()
|
||||||
|
? numThreads
|
||||||
|
: tbb::task_scheduler_init::default_num_threads();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int setNumThreads(int nThreads) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
int oldNumThreads = numThreads;
|
||||||
|
numThreads = nThreads;
|
||||||
|
|
||||||
|
#if TBB_INTERFACE_VERSION >= 8000
|
||||||
|
auto& tbbArena = getArena();
|
||||||
|
if (tbbArena.is_active())
|
||||||
|
tbbArena.terminate();
|
||||||
|
if (numThreads > 0)
|
||||||
|
tbbArena.initialize(numThreads);
|
||||||
|
#else
|
||||||
|
auto& tbbScheduler = getScheduler();
|
||||||
|
if (tbbScheduler.is_active())
|
||||||
|
tbbScheduler.terminate();
|
||||||
|
if (numThreads > 0)
|
||||||
|
tbbScheduler.initialize(numThreads);
|
||||||
|
#endif
|
||||||
|
return oldNumThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getName() const CV_OVERRIDE
|
||||||
|
{
|
||||||
|
return "tbb";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}}} // namespace
|
||||||
|
|
||||||
|
#endif // OPENCV_CORE_PARALLEL_FOR_TBB_HPP
|
@ -0,0 +1,73 @@
|
|||||||
|
// 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_BACKEND_HPP
|
||||||
|
#define OPENCV_CORE_PARALLEL_BACKEND_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace cv { namespace parallel {
|
||||||
|
#ifndef CV_API_CALL
|
||||||
|
#define CV_API_CALL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @addtogroup core_parallel_backend
|
||||||
|
* @{
|
||||||
|
* API below is provided to resolve problem of CPU resource over-subscription by multiple thread pools from different multi-threading frameworks.
|
||||||
|
* This is common problem for cases when OpenCV compiled threading framework is different from the Users Applications framework.
|
||||||
|
*
|
||||||
|
* Applications can replace OpenCV `parallel_for()` backend with own implementation (to reuse Application's thread pool).
|
||||||
|
*
|
||||||
|
* @note This call is not thread-safe. Consider calling this function from the `main()` before any other OpenCV processing functions (and without any other created threads).
|
||||||
|
*
|
||||||
|
* #### Intel TBB usage example:
|
||||||
|
*
|
||||||
|
* - include header with simple implementation of TBB backend:
|
||||||
|
* @snippet parallel_backend/example-tbb.cpp tbb_include
|
||||||
|
* - execute backend replacement code:
|
||||||
|
* @snippet parallel_backend/example-tbb.cpp tbb_backend
|
||||||
|
* - configuration of compiler/linker options is responsibility of Application's scripts
|
||||||
|
*
|
||||||
|
* #### OpenMP usage example:
|
||||||
|
*
|
||||||
|
* - include header with simple implementation of OpenMP backend:
|
||||||
|
* @snippet parallel_backend/example-openmp.cpp openmp_include
|
||||||
|
* - execute backend replacement code:
|
||||||
|
* @snippet parallel_backend/example-openmp.cpp openmp_backend
|
||||||
|
* - Configuration of compiler/linker options is responsibility of Application's scripts
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Interface for parallel_for backends implementations
|
||||||
|
*
|
||||||
|
* @sa setParallelForBackend
|
||||||
|
*/
|
||||||
|
class CV_EXPORTS ParallelForAPI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ParallelForAPI();
|
||||||
|
|
||||||
|
typedef void (CV_API_CALL *FN_parallel_for_body_cb_t)(int start, int end, void* data);
|
||||||
|
|
||||||
|
virtual void parallel_for(int tasks, FN_parallel_for_body_cb_t body_callback, void* callback_data) = 0;
|
||||||
|
|
||||||
|
virtual int getThreadNum() const = 0;
|
||||||
|
|
||||||
|
virtual int getNumThreads() const = 0;
|
||||||
|
|
||||||
|
virtual int setNumThreads(int nThreads) = 0;
|
||||||
|
|
||||||
|
virtual const char* getName() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Replace OpenCV parallel_for backend
|
||||||
|
*
|
||||||
|
* Application can replace OpenCV `parallel_for()` backend with own implementation.
|
||||||
|
*
|
||||||
|
* @note This call is not thread-safe. Consider calling this function from the `main()` before any other OpenCV processing functions (and without any other created threads).
|
||||||
|
*/
|
||||||
|
CV_EXPORTS void setParallelForBackend(const std::shared_ptr<ParallelForAPI>& api, bool propagateNumThreads = true);
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
}} // namespace
|
||||||
|
#endif // OPENCV_CORE_PARALLEL_BACKEND_HPP
|
@ -570,6 +570,8 @@ static inline size_t getElemSize(int type) { return (size_t)CV_ELEM_SIZE(type);
|
|||||||
/////////////////////////////// Parallel Primitives //////////////////////////////////
|
/////////////////////////////// Parallel Primitives //////////////////////////////////
|
||||||
|
|
||||||
/** @brief Base class for parallel data processors
|
/** @brief Base class for parallel data processors
|
||||||
|
|
||||||
|
@ingroup core_parallel
|
||||||
*/
|
*/
|
||||||
class CV_EXPORTS ParallelLoopBody
|
class CV_EXPORTS ParallelLoopBody
|
||||||
{
|
{
|
||||||
@ -579,17 +581,23 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** @brief Parallel data processor
|
/** @brief Parallel data processor
|
||||||
|
|
||||||
|
@ingroup core_parallel
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS void parallel_for_(const Range& range, const ParallelLoopBody& body, double nstripes=-1.);
|
CV_EXPORTS void parallel_for_(const Range& range, const ParallelLoopBody& body, double nstripes=-1.);
|
||||||
|
|
||||||
|
//! @ingroup core_parallel
|
||||||
class ParallelLoopBodyLambdaWrapper : public ParallelLoopBody
|
class ParallelLoopBodyLambdaWrapper : public ParallelLoopBody
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::function<void(const Range&)> m_functor;
|
std::function<void(const Range&)> m_functor;
|
||||||
public:
|
public:
|
||||||
ParallelLoopBodyLambdaWrapper(std::function<void(const Range&)> functor) :
|
inline
|
||||||
m_functor(functor)
|
ParallelLoopBodyLambdaWrapper(std::function<void(const Range&)> functor)
|
||||||
{ }
|
: m_functor(functor)
|
||||||
|
{
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
|
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
|
||||||
{
|
{
|
||||||
@ -597,11 +605,14 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void parallel_for_(const Range& range, std::function<void(const Range&)> functor, double nstripes=-1.)
|
//! @ingroup core_parallel
|
||||||
|
static inline
|
||||||
|
void parallel_for_(const Range& range, std::function<void(const Range&)> functor, double nstripes=-1.)
|
||||||
{
|
{
|
||||||
parallel_for_(range, ParallelLoopBodyLambdaWrapper(functor), nstripes);
|
parallel_for_(range, ParallelLoopBodyLambdaWrapper(functor), nstripes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////// forEach method of cv::Mat ////////////////////////////
|
/////////////////////////////// forEach method of cv::Mat ////////////////////////////
|
||||||
template<typename _Tp, typename Functor> inline
|
template<typename _Tp, typename Functor> inline
|
||||||
void Mat::forEach_impl(const Functor& operation) {
|
void Mat::forEach_impl(const Functor& operation) {
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
#include <opencv2/core/utils/configuration.private.hpp>
|
#include <opencv2/core/utils/configuration.private.hpp>
|
||||||
#include <opencv2/core/utils/trace.private.hpp>
|
#include <opencv2/core/utils/trace.private.hpp>
|
||||||
|
|
||||||
|
#include "opencv2/core/parallel/parallel_backend.hpp"
|
||||||
|
|
||||||
#if defined _WIN32 || defined WINCE
|
#if defined _WIN32 || defined WINCE
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#undef small
|
#undef small
|
||||||
@ -145,9 +147,7 @@
|
|||||||
# define CV_PARALLEL_FRAMEWORK "pthreads"
|
# define CV_PARALLEL_FRAMEWORK "pthreads"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CV_PARALLEL_FRAMEWORK
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "parallel_impl.hpp"
|
#include "parallel_impl.hpp"
|
||||||
|
|
||||||
@ -159,9 +159,36 @@ namespace cv {
|
|||||||
|
|
||||||
ParallelLoopBody::~ParallelLoopBody() {}
|
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 {
|
namespace {
|
||||||
|
|
||||||
#ifdef CV_PARALLEL_FRAMEWORK
|
|
||||||
#ifdef ENABLE_INSTRUMENTATION
|
#ifdef ENABLE_INSTRUMENTATION
|
||||||
static void SyncNodes(cv::instr::InstrNode *pNode)
|
static void SyncNodes(cv::instr::InstrNode *pNode)
|
||||||
{
|
{
|
||||||
@ -430,8 +457,6 @@ namespace {
|
|||||||
typedef ParallelLoopBodyWrapper ProxyLoopBody;
|
typedef ParallelLoopBodyWrapper ProxyLoopBody;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int numThreads = -1;
|
|
||||||
|
|
||||||
#if defined HAVE_TBB
|
#if defined HAVE_TBB
|
||||||
#if TBB_INTERFACE_VERSION >= 8000
|
#if TBB_INTERFACE_VERSION >= 8000
|
||||||
static tbb::task_arena tbbArena(tbb::task_arena::automatic);
|
static tbb::task_arena tbbArena(tbb::task_arena::automatic);
|
||||||
@ -446,7 +471,7 @@ static inline int _initMaxThreads()
|
|||||||
int maxThreads = omp_get_max_threads();
|
int maxThreads = omp_get_max_threads();
|
||||||
if (!utils::getConfigurationParameterBool("OPENCV_FOR_OPENMP_DYNAMIC_DISABLE", false))
|
if (!utils::getConfigurationParameterBool("OPENCV_FOR_OPENMP_DYNAMIC_DISABLE", false))
|
||||||
{
|
{
|
||||||
omp_set_dynamic(maxThreads);
|
omp_set_dynamic(1);
|
||||||
}
|
}
|
||||||
return maxThreads;
|
return maxThreads;
|
||||||
}
|
}
|
||||||
@ -477,15 +502,11 @@ static SchedPtr pplScheduler;
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // CV_PARALLEL_FRAMEWORK
|
|
||||||
|
|
||||||
} // namespace anon
|
} // namespace anon
|
||||||
|
|
||||||
/* ================================ parallel_for_ ================================ */
|
/* ================================ parallel_for_ ================================ */
|
||||||
|
|
||||||
#ifdef CV_PARALLEL_FRAMEWORK
|
|
||||||
static void parallel_for_impl(const cv::Range& range, const cv::ParallelLoopBody& body, double nstripes); // forward declaration
|
static void parallel_for_impl(const cv::Range& range, const cv::ParallelLoopBody& body, double nstripes); // forward declaration
|
||||||
#endif
|
|
||||||
|
|
||||||
void parallel_for_(const cv::Range& range, const cv::ParallelLoopBody& body, double nstripes)
|
void parallel_for_(const cv::Range& range, const cv::ParallelLoopBody& body, double nstripes)
|
||||||
{
|
{
|
||||||
@ -500,7 +521,6 @@ void parallel_for_(const cv::Range& range, const cv::ParallelLoopBody& body, dou
|
|||||||
if (range.empty())
|
if (range.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef CV_PARALLEL_FRAMEWORK
|
|
||||||
static std::atomic<bool> flagNestedParallelFor(false);
|
static std::atomic<bool> flagNestedParallelFor(false);
|
||||||
bool isNotNestedRegion = !flagNestedParallelFor.load();
|
bool isNotNestedRegion = !flagNestedParallelFor.load();
|
||||||
if (isNotNestedRegion)
|
if (isNotNestedRegion)
|
||||||
@ -519,16 +539,23 @@ void parallel_for_(const cv::Range& range, const cv::ParallelLoopBody& body, dou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // nested parallel_for_() calls are not parallelized
|
else // nested parallel_for_() calls are not parallelized
|
||||||
#endif // CV_PARALLEL_FRAMEWORK
|
|
||||||
{
|
{
|
||||||
CV_UNUSED(nstripes);
|
CV_UNUSED(nstripes);
|
||||||
body(range);
|
body(range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CV_PARALLEL_FRAMEWORK
|
static
|
||||||
|
void parallel_for_cb(int start, int end, void* data)
|
||||||
|
{
|
||||||
|
CV_DbgAssert(data);
|
||||||
|
const cv::ParallelLoopBody& body = *(const cv::ParallelLoopBody*)data;
|
||||||
|
body(Range(start, end));
|
||||||
|
}
|
||||||
|
|
||||||
static void parallel_for_impl(const cv::Range& range, const cv::ParallelLoopBody& body, double nstripes)
|
static void parallel_for_impl(const cv::Range& range, const cv::ParallelLoopBody& body, double nstripes)
|
||||||
{
|
{
|
||||||
|
using namespace cv::parallel;
|
||||||
if ((numThreads < 0 || numThreads > 1) && range.end - range.start > 1)
|
if ((numThreads < 0 || numThreads > 1) && range.end - range.start > 1)
|
||||||
{
|
{
|
||||||
ParallelLoopBodyWrapperContext ctx(body, range, nstripes);
|
ParallelLoopBodyWrapperContext ctx(body, range, nstripes);
|
||||||
@ -540,6 +567,16 @@ static void parallel_for_impl(const cv::Range& range, const cv::ParallelLoopBody
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ParallelForAPI>& api = getCurrentParallelForAPI();
|
||||||
|
if (api)
|
||||||
|
{
|
||||||
|
CV_CheckEQ(stripeRange.start, 0, "");
|
||||||
|
api->parallel_for(stripeRange.end, parallel_for_cb, (void*)&pbody);
|
||||||
|
ctx.finalize(); // propagate exceptions if exists
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CV_PARALLEL_FRAMEWORK
|
||||||
#if defined HAVE_TBB
|
#if defined HAVE_TBB
|
||||||
|
|
||||||
#if TBB_INTERFACE_VERSION >= 8000
|
#if TBB_INTERFACE_VERSION >= 8000
|
||||||
@ -590,24 +627,25 @@ static void parallel_for_impl(const cv::Range& range, const cv::ParallelLoopBody
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
ctx.finalize(); // propagate exceptions if exists
|
ctx.finalize(); // propagate exceptions if exists
|
||||||
}
|
return;
|
||||||
else
|
|
||||||
{
|
|
||||||
body(range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // CV_PARALLEL_FRAMEWORK
|
#endif // CV_PARALLEL_FRAMEWORK
|
||||||
|
}
|
||||||
|
|
||||||
|
body(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int getNumThreads(void)
|
int getNumThreads(void)
|
||||||
{
|
{
|
||||||
#ifdef CV_PARALLEL_FRAMEWORK
|
std::shared_ptr<ParallelForAPI>& api = getCurrentParallelForAPI();
|
||||||
|
if (api)
|
||||||
|
{
|
||||||
|
return api->getNumThreads();
|
||||||
|
}
|
||||||
|
|
||||||
if(numThreads == 0)
|
if (numThreads == 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined HAVE_TBB
|
#if defined HAVE_TBB
|
||||||
|
|
||||||
#if TBB_INTERFACE_VERSION >= 9100
|
#if TBB_INTERFACE_VERSION >= 9100
|
||||||
@ -682,10 +720,15 @@ unsigned defaultNumberOfThreads()
|
|||||||
void setNumThreads( int threads_ )
|
void setNumThreads( int threads_ )
|
||||||
{
|
{
|
||||||
CV_UNUSED(threads_);
|
CV_UNUSED(threads_);
|
||||||
#ifdef CV_PARALLEL_FRAMEWORK
|
|
||||||
int threads = (threads_ < 0) ? defaultNumberOfThreads() : (unsigned)threads_;
|
int threads = (threads_ < 0) ? defaultNumberOfThreads() : (unsigned)threads_;
|
||||||
numThreads = threads;
|
numThreads = threads;
|
||||||
#endif
|
|
||||||
|
std::shared_ptr<ParallelForAPI>& api = getCurrentParallelForAPI();
|
||||||
|
if (api)
|
||||||
|
{
|
||||||
|
api->setNumThreads(numThreads);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_TBB
|
#ifdef HAVE_TBB
|
||||||
|
|
||||||
@ -741,6 +784,12 @@ void setNumThreads( int threads_ )
|
|||||||
|
|
||||||
int getThreadNum()
|
int getThreadNum()
|
||||||
{
|
{
|
||||||
|
std::shared_ptr<ParallelForAPI>& api = getCurrentParallelForAPI();
|
||||||
|
if (api)
|
||||||
|
{
|
||||||
|
return api->getThreadNum();
|
||||||
|
}
|
||||||
|
|
||||||
#if defined HAVE_TBB
|
#if defined HAVE_TBB
|
||||||
#if TBB_INTERFACE_VERSION >= 9100
|
#if TBB_INTERFACE_VERSION >= 9100
|
||||||
return tbb::this_task_arena::current_thread_index();
|
return tbb::this_task_arena::current_thread_index();
|
||||||
@ -963,7 +1012,13 @@ int getNumberOfCPUs()
|
|||||||
return nCPUs; // cached value
|
return nCPUs; // cached value
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* currentParallelFramework() {
|
const char* currentParallelFramework()
|
||||||
|
{
|
||||||
|
std::shared_ptr<ParallelForAPI>& api = getCurrentParallelForAPI();
|
||||||
|
if (api)
|
||||||
|
{
|
||||||
|
return api->getName();
|
||||||
|
}
|
||||||
#ifdef CV_PARALLEL_FRAMEWORK
|
#ifdef CV_PARALLEL_FRAMEWORK
|
||||||
return CV_PARALLEL_FRAMEWORK;
|
return CV_PARALLEL_FRAMEWORK;
|
||||||
#else
|
#else
|
||||||
|
@ -33,6 +33,7 @@ if(NOT HAVE_opencv_cudaarithm OR NOT HAVE_opencv_cudafilters)
|
|||||||
ocv_list_filterout(cpp_samples "/gpu/")
|
ocv_list_filterout(cpp_samples "/gpu/")
|
||||||
endif()
|
endif()
|
||||||
ocv_list_filterout(cpp_samples "real_time_pose_estimation/")
|
ocv_list_filterout(cpp_samples "real_time_pose_estimation/")
|
||||||
|
ocv_list_filterout(cpp_samples "parallel_backend/")
|
||||||
foreach(sample_filename ${cpp_samples})
|
foreach(sample_filename ${cpp_samples})
|
||||||
set(package "cpp")
|
set(package "cpp")
|
||||||
if(sample_filename MATCHES "tutorial_code/snippet")
|
if(sample_filename MATCHES "tutorial_code/snippet")
|
||||||
@ -66,3 +67,11 @@ foreach(sample_filename ${cpp_samples})
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
include("tutorial_code/calib3d/real_time_pose_estimation/CMakeLists.txt" OPTIONAL)
|
include("tutorial_code/calib3d/real_time_pose_estimation/CMakeLists.txt" OPTIONAL)
|
||||||
|
|
||||||
|
# Standalone samples only
|
||||||
|
if(OpenCV_FOUND AND NOT CMAKE_VERSION VERSION_LESS "3.1")
|
||||||
|
add_subdirectory("example_cmake")
|
||||||
|
endif()
|
||||||
|
if(OpenCV_FOUND AND NOT CMAKE_VERSION VERSION_LESS "3.9")
|
||||||
|
add_subdirectory("tutorial_code/core/parallel_backend")
|
||||||
|
endif()
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.9)
|
||||||
|
|
||||||
|
find_package(OpenCV REQUIRED COMPONENTS opencv_core)
|
||||||
|
|
||||||
|
find_package(OpenMP)
|
||||||
|
if(OpenMP_FOUND)
|
||||||
|
project(opencv_example_openmp_backend)
|
||||||
|
add_executable(opencv_example_openmp_backend example-openmp.cpp)
|
||||||
|
target_link_libraries(opencv_example_openmp_backend PRIVATE
|
||||||
|
opencv_core
|
||||||
|
OpenMP::OpenMP_CXX
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO: find_package(TBB)
|
||||||
|
find_path(TBB_INCLUDE_DIR NAMES "tbb/tbb.h")
|
||||||
|
find_library(TBB_LIBRARY NAMES "tbb")
|
||||||
|
if(TBB_INCLUDE_DIR AND TBB_LIBRARY AND NOT OPENCV_EXAMPLE_SKIP_TBB)
|
||||||
|
project(opencv_example_tbb_backend)
|
||||||
|
add_executable(opencv_example_tbb_backend example-tbb.cpp)
|
||||||
|
target_include_directories(opencv_example_tbb_backend SYSTEM PRIVATE ${TBB_INCLUDE_DIR})
|
||||||
|
target_link_libraries(opencv_example_tbb_backend PRIVATE
|
||||||
|
opencv_core
|
||||||
|
${TBB_LIBRARY}
|
||||||
|
)
|
||||||
|
endif()
|
@ -0,0 +1,44 @@
|
|||||||
|
#include "opencv2/core.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
//! [openmp_include]
|
||||||
|
#include "opencv2/core/parallel/backend/parallel_for.openmp.hpp"
|
||||||
|
//! [openmp_include]
|
||||||
|
|
||||||
|
namespace cv { // private.hpp
|
||||||
|
CV_EXPORTS const char* currentParallelFramework();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string currentParallelFrameworkSafe()
|
||||||
|
{
|
||||||
|
const char* framework = cv::currentParallelFramework();
|
||||||
|
if (framework)
|
||||||
|
return framework;
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace cv;
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::cout << "OpenCV builtin parallel framework: '" << currentParallelFrameworkSafe() << "' (nthreads=" << getNumThreads() << ")" << std::endl;
|
||||||
|
|
||||||
|
//! [openmp_backend]
|
||||||
|
//omp_set_dynamic(1);
|
||||||
|
cv::parallel::setParallelForBackend(std::make_shared<cv::parallel::openmp::ParallelForBackend>());
|
||||||
|
//! [openmp_backend]
|
||||||
|
|
||||||
|
std::cout << "New parallel backend: '" << currentParallelFrameworkSafe() << "'" << "' (nthreads=" << getNumThreads() << ")" << std::endl;
|
||||||
|
|
||||||
|
parallel_for_(Range(0, 20), [&](const Range range)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Thread " << getThreadNum() << "(opencv=" << utils::getThreadID() << "): range " << range.start << "-" << range.end << std::endl;
|
||||||
|
std::cout << out.str() << std::flush;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
#include "opencv2/core.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
//! [tbb_include]
|
||||||
|
#include "opencv2/core/parallel/backend/parallel_for.tbb.hpp"
|
||||||
|
//! [tbb_include]
|
||||||
|
|
||||||
|
namespace cv { // private.hpp
|
||||||
|
CV_EXPORTS const char* currentParallelFramework();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string currentParallelFrameworkSafe()
|
||||||
|
{
|
||||||
|
const char* framework = cv::currentParallelFramework();
|
||||||
|
if (framework)
|
||||||
|
return framework;
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace cv;
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::cout << "OpenCV builtin parallel framework: '" << currentParallelFrameworkSafe() << "' (nthreads=" << getNumThreads() << ")" << std::endl;
|
||||||
|
|
||||||
|
//! [tbb_backend]
|
||||||
|
cv::parallel::setParallelForBackend(std::make_shared<cv::parallel::tbb::ParallelForBackend>());
|
||||||
|
//! [tbb_backend]
|
||||||
|
|
||||||
|
std::cout << "New parallel backend: '" << currentParallelFrameworkSafe() << "'" << "' (nthreads=" << getNumThreads() << ")" << std::endl;
|
||||||
|
|
||||||
|
parallel_for_(Range(0, 20), [&](const Range range)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Thread " << getThreadNum() << "(opencv=" << utils::getThreadID() << "): range " << range.start << "-" << range.end << std::endl;
|
||||||
|
std::cout << out.str() << std::flush;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user