Skip to content

Commit 37c12db

Browse files
committed
Merge pull request opencv#19365 from alalek:parallel_api
2 parents 8c22198 + b73bf03 commit 37c12db

File tree

13 files changed

+527
-30
lines changed

13 files changed

+527
-30
lines changed

cmake/templates/opencv_abi.xml.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
opencv2/core/hal/*.impl.*
2727
opencv2/core/cuda*
2828
opencv2/core/opencl*
29+
opencv2/core/parallel/backend/*
2930
opencv2/core/private*
3031
opencv2/core/quaternion*
3132
opencv/cxeigen.hpp

doc/Doxyfile.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ INCLUDE_PATH =
227227
INCLUDE_FILE_PATTERNS =
228228
PREDEFINED = __cplusplus=1 \
229229
CVAPI(x)=x \
230+
CV_API_CALL= \
230231
CV_DOXYGEN= \
231232
CV_EXPORTS= \
232233
CV_EXPORTS_W= \

modules/core/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,15 @@ file(GLOB_RECURSE module_opencl_hdrs
5858
source_group("Include\\Cuda Headers" FILES ${lib_cuda_hdrs})
5959
source_group("Include\\Cuda Headers\\Detail" FILES ${lib_cuda_hdrs_detail})
6060

61+
file(GLOB_RECURSE core_parallel_hdrs
62+
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/parallel/*.hpp"
63+
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/parallel/*.h")
64+
ocv_source_group("Include" DIRBASE "${CMAKE_CURRENT_LIST_DIR}/include" FILES ${core_parallel_hdrs})
65+
6166
source_group("Src" FILES "${OPENCV_MODULE_opencv_core_BINARY_DIR}/version_string.inc")
6267

6368
ocv_glob_module_sources(SOURCES "${OPENCV_MODULE_opencv_core_BINARY_DIR}/version_string.inc"
64-
HEADERS ${module_opencl_hdrs} ${lib_cuda_hdrs} ${lib_cuda_hdrs_detail})
69+
HEADERS ${core_parallel_hdrs} ${module_opencl_hdrs} ${lib_cuda_hdrs} ${lib_cuda_hdrs_detail})
6570

6671
ocv_module_include_directories(${the_module} ${ZLIB_INCLUDE_DIRS} ${OPENCL_INCLUDE_DIRS})
6772
if(ANDROID AND HAVE_CPUFEATURES)

modules/core/include/opencv2/core.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@
9797
@}
9898
@defgroup core_lowlevel_api Low-level API for external libraries / plugins
9999
@}
100+
@defgroup core_parallel Parallel Processing
101+
@{
102+
@defgroup core_parallel_backend Parallel backends API
103+
@}
100104
@}
101105
*/
102106

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#ifndef OPENCV_CORE_PARALLEL_FOR_OPENMP_HPP
6+
#define OPENCV_CORE_PARALLEL_FOR_OPENMP_HPP
7+
8+
#include "opencv2/core/parallel/parallel_backend.hpp"
9+
10+
#if !defined(_OPENMP) && !defined(OPENCV_SKIP_OPENMP_PRESENSE_CHECK)
11+
#error "This file must be compiled with enabled OpenMP"
12+
#endif
13+
14+
#include <omp.h>
15+
16+
namespace cv { namespace parallel { namespace openmp {
17+
18+
/** OpenMP parallel_for API implementation
19+
*
20+
* @sa setParallelForBackend
21+
* @ingroup core_parallel_backend
22+
*/
23+
class ParallelForBackend : public ParallelForAPI
24+
{
25+
protected:
26+
int numThreads;
27+
int numThreadsMax;
28+
public:
29+
ParallelForBackend()
30+
{
31+
numThreads = 0;
32+
numThreadsMax = omp_get_max_threads();
33+
}
34+
35+
virtual ~ParallelForBackend() {}
36+
37+
virtual void parallel_for(int tasks, FN_parallel_for_body_cb_t body_callback, void* callback_data) CV_OVERRIDE
38+
{
39+
#pragma omp parallel for schedule(dynamic) num_threads(numThreads > 0 ? numThreads : numThreadsMax)
40+
for (int i = 0; i < tasks; ++i)
41+
body_callback(i, i + 1, callback_data);
42+
}
43+
44+
virtual int getThreadNum() const CV_OVERRIDE
45+
{
46+
return omp_get_thread_num();
47+
}
48+
49+
virtual int getNumThreads() const CV_OVERRIDE
50+
{
51+
return numThreads > 0
52+
? numThreads
53+
: numThreadsMax;
54+
}
55+
56+
virtual int setNumThreads(int nThreads) CV_OVERRIDE
57+
{
58+
int oldNumThreads = numThreads;
59+
numThreads = nThreads;
60+
// nothing needed as numThreads is used in #pragma omp parallel for directly
61+
return oldNumThreads;
62+
}
63+
64+
const char* getName() const CV_OVERRIDE
65+
{
66+
return "openmp";
67+
}
68+
};
69+
70+
}}} // namespace
71+
72+
#endif // OPENCV_CORE_PARALLEL_FOR_OPENMP_HPP
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#ifndef OPENCV_CORE_PARALLEL_FOR_TBB_HPP
6+
#define OPENCV_CORE_PARALLEL_FOR_TBB_HPP
7+
8+
#include "opencv2/core/parallel/parallel_backend.hpp"
9+
#include <opencv2/core/utils/logger.hpp>
10+
11+
#ifndef TBB_SUPPRESS_DEPRECATED_MESSAGES // supress warning
12+
#define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
13+
#endif
14+
#include "tbb/tbb.h"
15+
#if !defined(TBB_INTERFACE_VERSION)
16+
#error "Unknows/unsupported TBB version"
17+
#endif
18+
19+
#if TBB_INTERFACE_VERSION >= 8000
20+
#include "tbb/task_arena.h"
21+
#endif
22+
23+
namespace cv { namespace parallel { namespace tbb {
24+
25+
using namespace ::tbb;
26+
27+
#if TBB_INTERFACE_VERSION >= 8000
28+
static tbb::task_arena& getArena()
29+
{
30+
static tbb::task_arena tbbArena(tbb::task_arena::automatic);
31+
return tbbArena;
32+
}
33+
#else
34+
static tbb::task_scheduler_init& getScheduler()
35+
{
36+
static tbb::task_scheduler_init tbbScheduler(tbb::task_scheduler_init::deferred);
37+
return tbbScheduler;
38+
}
39+
#endif
40+
41+
/** OpenMP parallel_for API implementation
42+
*
43+
* @sa setParallelForBackend
44+
* @ingroup core_parallel_backend
45+
*/
46+
class ParallelForBackend : public ParallelForAPI
47+
{
48+
protected:
49+
int numThreads;
50+
int numThreadsMax;
51+
public:
52+
ParallelForBackend()
53+
{
54+
CV_LOG_INFO(NULL, "Initializing TBB parallel backend: TBB_INTERFACE_VERSION=" << TBB_INTERFACE_VERSION);
55+
numThreads = 0;
56+
#if TBB_INTERFACE_VERSION >= 8000
57+
(void)getArena();
58+
#else
59+
(void)getScheduler();
60+
#endif
61+
}
62+
63+
virtual ~ParallelForBackend() {}
64+
65+
class CallbackProxy
66+
{
67+
const FN_parallel_for_body_cb_t& callback;
68+
void* const callback_data;
69+
const int tasks;
70+
public:
71+
inline CallbackProxy(int tasks_, FN_parallel_for_body_cb_t& callback_, void* callback_data_)
72+
: callback(callback_), callback_data(callback_data_), tasks(tasks_)
73+
{
74+
// nothing
75+
}
76+
77+
void operator()(const tbb::blocked_range<int>& range) const
78+
{
79+
this->callback(range.begin(), range.end(), callback_data);
80+
}
81+
82+
void operator()() const
83+
{
84+
tbb::parallel_for(tbb::blocked_range<int>(0, tasks), *this);
85+
}
86+
};
87+
88+
virtual void parallel_for(int tasks, FN_parallel_for_body_cb_t body_callback, void* callback_data) CV_OVERRIDE
89+
{
90+
CallbackProxy task(tasks, body_callback, callback_data);
91+
#if TBB_INTERFACE_VERSION >= 8000
92+
getArena().execute(task);
93+
#else
94+
task();
95+
#endif
96+
}
97+
98+
virtual int getThreadNum() const CV_OVERRIDE
99+
{
100+
#if TBB_INTERFACE_VERSION >= 9100
101+
return tbb::this_task_arena::current_thread_index();
102+
#elif TBB_INTERFACE_VERSION >= 8000
103+
return tbb::task_arena::current_thread_index();
104+
#else
105+
return 0;
106+
#endif
107+
}
108+
109+
virtual int getNumThreads() const CV_OVERRIDE
110+
{
111+
#if TBB_INTERFACE_VERSION >= 9100
112+
return getArena().max_concurrency();
113+
#elif TBB_INTERFACE_VERSION >= 8000
114+
return numThreads > 0
115+
? numThreads
116+
: tbb::task_scheduler_init::default_num_threads();
117+
#else
118+
return getScheduler().is_active()
119+
? numThreads
120+
: tbb::task_scheduler_init::default_num_threads();
121+
#endif
122+
}
123+
124+
virtual int setNumThreads(int nThreads) CV_OVERRIDE
125+
{
126+
int oldNumThreads = numThreads;
127+
numThreads = nThreads;
128+
129+
#if TBB_INTERFACE_VERSION >= 8000
130+
auto& tbbArena = getArena();
131+
if (tbbArena.is_active())
132+
tbbArena.terminate();
133+
if (numThreads > 0)
134+
tbbArena.initialize(numThreads);
135+
#else
136+
auto& tbbScheduler = getScheduler();
137+
if (tbbScheduler.is_active())
138+
tbbScheduler.terminate();
139+
if (numThreads > 0)
140+
tbbScheduler.initialize(numThreads);
141+
#endif
142+
return oldNumThreads;
143+
}
144+
145+
const char* getName() const CV_OVERRIDE
146+
{
147+
return "tbb";
148+
}
149+
};
150+
151+
}}} // namespace
152+
153+
#endif // OPENCV_CORE_PARALLEL_FOR_TBB_HPP
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#ifndef OPENCV_CORE_PARALLEL_BACKEND_HPP
6+
#define OPENCV_CORE_PARALLEL_BACKEND_HPP
7+
8+
#include <memory>
9+
10+
namespace cv { namespace parallel {
11+
#ifndef CV_API_CALL
12+
#define CV_API_CALL
13+
#endif
14+
15+
/** @addtogroup core_parallel_backend
16+
* @{
17+
* API below is provided to resolve problem of CPU resource over-subscription by multiple thread pools from different multi-threading frameworks.
18+
* This is common problem for cases when OpenCV compiled threading framework is different from the Users Applications framework.
19+
*
20+
* Applications can replace OpenCV `parallel_for()` backend with own implementation (to reuse Application's thread pool).
21+
*
22+
* @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).
23+
*
24+
* #### Intel TBB usage example:
25+
*
26+
* - include header with simple implementation of TBB backend:
27+
* @snippet parallel_backend/example-tbb.cpp tbb_include
28+
* - execute backend replacement code:
29+
* @snippet parallel_backend/example-tbb.cpp tbb_backend
30+
* - configuration of compiler/linker options is responsibility of Application's scripts
31+
*
32+
* #### OpenMP usage example:
33+
*
34+
* - include header with simple implementation of OpenMP backend:
35+
* @snippet parallel_backend/example-openmp.cpp openmp_include
36+
* - execute backend replacement code:
37+
* @snippet parallel_backend/example-openmp.cpp openmp_backend
38+
* - Configuration of compiler/linker options is responsibility of Application's scripts
39+
*/
40+
41+
/** Interface for parallel_for backends implementations
42+
*
43+
* @sa setParallelForBackend
44+
*/
45+
class CV_EXPORTS ParallelForAPI
46+
{
47+
public:
48+
virtual ~ParallelForAPI();
49+
50+
typedef void (CV_API_CALL *FN_parallel_for_body_cb_t)(int start, int end, void* data);
51+
52+
virtual void parallel_for(int tasks, FN_parallel_for_body_cb_t body_callback, void* callback_data) = 0;
53+
54+
virtual int getThreadNum() const = 0;
55+
56+
virtual int getNumThreads() const = 0;
57+
58+
virtual int setNumThreads(int nThreads) = 0;
59+
60+
virtual const char* getName() const = 0;
61+
};
62+
63+
/** @brief Replace OpenCV parallel_for backend
64+
*
65+
* Application can replace OpenCV `parallel_for()` backend with own implementation.
66+
*
67+
* @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).
68+
*/
69+
CV_EXPORTS void setParallelForBackend(const std::shared_ptr<ParallelForAPI>& api, bool propagateNumThreads = true);
70+
71+
//! @}
72+
}} // namespace
73+
#endif // OPENCV_CORE_PARALLEL_BACKEND_HPP

modules/core/include/opencv2/core/utility.hpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ static inline size_t getElemSize(int type) { return (size_t)CV_ELEM_SIZE(type);
570570
/////////////////////////////// Parallel Primitives //////////////////////////////////
571571

572572
/** @brief Base class for parallel data processors
573+
574+
@ingroup core_parallel
573575
*/
574576
class CV_EXPORTS ParallelLoopBody
575577
{
@@ -579,29 +581,38 @@ class CV_EXPORTS ParallelLoopBody
579581
};
580582

581583
/** @brief Parallel data processor
584+
585+
@ingroup core_parallel
582586
*/
583587
CV_EXPORTS void parallel_for_(const Range& range, const ParallelLoopBody& body, double nstripes=-1.);
584588

589+
//! @ingroup core_parallel
585590
class ParallelLoopBodyLambdaWrapper : public ParallelLoopBody
586591
{
587592
private:
588593
std::function<void(const Range&)> m_functor;
589594
public:
590-
ParallelLoopBodyLambdaWrapper(std::function<void(const Range&)> functor) :
591-
m_functor(functor)
592-
{ }
595+
inline
596+
ParallelLoopBodyLambdaWrapper(std::function<void(const Range&)> functor)
597+
: m_functor(functor)
598+
{
599+
// nothing
600+
}
593601

594602
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
595603
{
596604
m_functor(range);
597605
}
598606
};
599607

600-
inline void parallel_for_(const Range& range, std::function<void(const Range&)> functor, double nstripes=-1.)
608+
//! @ingroup core_parallel
609+
static inline
610+
void parallel_for_(const Range& range, std::function<void(const Range&)> functor, double nstripes=-1.)
601611
{
602612
parallel_for_(range, ParallelLoopBodyLambdaWrapper(functor), nstripes);
603613
}
604614

615+
605616
/////////////////////////////// forEach method of cv::Mat ////////////////////////////
606617
template<typename _Tp, typename Functor> inline
607618
void Mat::forEach_impl(const Functor& operation) {

0 commit comments

Comments
 (0)