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
getArena()28 static tbb::task_arena& getArena()
29 {
30     static tbb::task_arena tbbArena(tbb::task_arena::automatic);
31     return tbbArena;
32 }
33 #else
getScheduler()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:
ParallelForBackend()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 
~ParallelForBackend()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:
CallbackProxy(int tasks_,FN_parallel_for_body_cb_t & callback_,void * callback_data_)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 
operator ()(const tbb::blocked_range<int> & range) const77         void operator()(const tbb::blocked_range<int>& range) const
78         {
79             this->callback(range.begin(), range.end(), callback_data);
80         }
81 
operator ()() const82         void operator()() const
83         {
84             tbb::parallel_for(tbb::blocked_range<int>(0, tasks), *this);
85         }
86     };
87 
parallel_for(int tasks,FN_parallel_for_body_cb_t body_callback,void * callback_data)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 
getThreadNum() const98     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 
getNumThreads() const109     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 
setNumThreads(int nThreads)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 
getName() const145     const char* getName() const CV_OVERRIDE
146     {
147         return "tbb";
148     }
149 };
150 
151 }}}  // namespace
152 
153 #endif  // OPENCV_CORE_PARALLEL_FOR_TBB_HPP
154