blob: a8382b42bf13aad769ec0753d38ab141112131f4 [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
2 * Copyright (c) 2016, 2017 ARM Limited.
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include "arm_compute/runtime/CPP/CPPScheduler.h"
25
26#include "arm_compute/core/CPP/ICPPKernel.h"
27#include "arm_compute/core/Error.h"
28#include "arm_compute/core/Helpers.h"
29#include "arm_compute/core/Utils.h"
30
31#include <iostream>
32#include <semaphore.h>
33#include <system_error>
34#include <thread>
35
36using namespace arm_compute;
37
38class arm_compute::Thread
39{
40public:
41 /** Start a new thread
42 */
43 Thread();
44 Thread(const Thread &) = delete;
45 Thread &operator=(const Thread &) = delete;
46 Thread(Thread &&) = delete;
47 Thread &operator=(Thread &&) = delete;
48 /** Make the thread join
49 */
50 ~Thread();
51 /** Request the worker thread to start executing the given kernel
52 * This function will return as soon as the kernel has been sent to the worker thread.
53 * wait() needs to be called to ensure the execution is complete.
54 */
Moritz Pflanzerc186b572017-09-07 09:48:04 +010055 void start(ICPPKernel *kernel, const Window &window, const ThreadInfo &info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010056 /** Wait for the current kernel execution to complete
57 */
58 void wait();
59 /** Function ran by the worker thread
60 */
61 void worker_thread();
62
63private:
64 std::thread _thread;
65 ICPPKernel *_kernel{ nullptr };
66 Window _window;
Moritz Pflanzerc186b572017-09-07 09:48:04 +010067 ThreadInfo _info;
Anthony Barbier6ff3b192017-09-04 18:44:23 +010068 sem_t _wait_for_work;
69 sem_t _job_complete;
70 std::exception_ptr _current_exception;
71};
72
73Thread::Thread()
Moritz Pflanzerc186b572017-09-07 09:48:04 +010074 : _thread(), _window(), _info(), _wait_for_work(), _job_complete(), _current_exception(nullptr)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010075{
76 int ret = sem_init(&_wait_for_work, 0, 0);
77 ARM_COMPUTE_ERROR_ON(ret < 0);
78 ARM_COMPUTE_UNUSED(ret);
79
80 ret = sem_init(&_job_complete, 0, 0);
81 ARM_COMPUTE_ERROR_ON(ret < 0);
82 ARM_COMPUTE_UNUSED(ret);
83
84 _thread = std::thread(&Thread::worker_thread, this);
85}
86
87Thread::~Thread()
88{
89 ARM_COMPUTE_ERROR_ON(!_thread.joinable());
90
Moritz Pflanzerc186b572017-09-07 09:48:04 +010091 start(nullptr, Window(), ThreadInfo());
Anthony Barbier6ff3b192017-09-04 18:44:23 +010092 _thread.join();
93
94 int ret = sem_destroy(&_wait_for_work);
95 ARM_COMPUTE_ERROR_ON(ret < 0);
96 ARM_COMPUTE_UNUSED(ret);
97
98 ret = sem_destroy(&_job_complete);
99 ARM_COMPUTE_ERROR_ON(ret < 0);
100 ARM_COMPUTE_UNUSED(ret);
101}
102
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100103void Thread::start(ICPPKernel *kernel, const Window &window, const ThreadInfo &info)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100104{
105 _kernel = kernel;
106 _window = window;
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100107 _info = info;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100108 int ret = sem_post(&_wait_for_work);
109 ARM_COMPUTE_UNUSED(ret);
110 ARM_COMPUTE_ERROR_ON(ret < 0);
111}
112
113void Thread::wait()
114{
115 int ret = sem_wait(&_job_complete);
116 ARM_COMPUTE_UNUSED(ret);
117 ARM_COMPUTE_ERROR_ON(ret < 0);
118 if(_current_exception)
119 {
120 std::rethrow_exception(_current_exception);
121 }
122}
123
124void Thread::worker_thread()
125{
126 while(sem_wait(&_wait_for_work) >= 0)
127 {
128 _current_exception = nullptr;
129 // Time to exit
130 if(_kernel == nullptr)
131 {
132 return;
133 }
134
135 try
136 {
137 _window.validate();
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100138 _kernel->run(_window, _info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100139 }
140 catch(...)
141 {
142 _current_exception = std::current_exception();
143 }
144 int ret = sem_post(&_job_complete);
145 ARM_COMPUTE_UNUSED(ret);
146 ARM_COMPUTE_ERROR_ON(ret < 0);
147 }
148
149 ARM_COMPUTE_ERROR("Wait failed");
150}
151
152namespace
153{
154void delete_threads(Thread *t)
155{
156 delete[] t;
157}
158} // namespace
159
160CPPScheduler &CPPScheduler::get()
161{
162 static CPPScheduler scheduler;
163 return scheduler;
164}
165
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100166CPPScheduler::CPPScheduler()
167 : _num_threads(std::thread::hardware_concurrency()),
Moritz Pflanzerba0f8dd2017-09-08 11:55:31 +0100168 _threads(std::unique_ptr<Thread[], void(*)(Thread *)>(new Thread[_num_threads - 1], delete_threads))
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100169{
170}
171
172void CPPScheduler::set_num_threads(unsigned int num_threads)
173{
174 const unsigned int num_cores = std::thread::hardware_concurrency();
175 _num_threads = num_threads == 0 ? num_cores : num_threads;
Moritz Pflanzerba0f8dd2017-09-08 11:55:31 +0100176 _threads.reset(new Thread[_num_threads - 1]);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100177}
178
Moritz Pflanzerd929b9c2017-06-28 10:15:48 +0100179unsigned int CPPScheduler::num_threads() const
180{
181 return _num_threads;
182}
183
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100184void CPPScheduler::schedule(ICPPKernel *kernel, unsigned int split_dimension)
185{
186 ARM_COMPUTE_ERROR_ON_MSG(!kernel, "The child class didn't set the kernel");
187
188 /** [Scheduler example] */
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100189 ThreadInfo info;
190 info.cpu = _target;
191
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100192 const Window &max_window = kernel->window();
193 const unsigned int num_iterations = max_window.num_iterations(split_dimension);
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100194 info.num_threads = std::min(num_iterations, _num_threads);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100195
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100196 if(!kernel->is_parallelisable() || info.num_threads == 1)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100197 {
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100198 kernel->run(max_window, info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100199 }
200 else
201 {
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100202 for(int t = 0; t < info.num_threads; ++t)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100203 {
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100204 Window win = max_window.split_window(split_dimension, t, info.num_threads);
205 info.thread_id = t;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100206
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100207 if(t != info.num_threads - 1)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100208 {
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100209 _threads[t].start(kernel, win, info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100210 }
211 else
212 {
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100213 kernel->run(win, info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100214 }
215 }
216
217 try
218 {
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100219 for(int t = 1; t < info.num_threads; ++t)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100220 {
221 _threads[t - 1].wait();
222 }
223 }
224 catch(const std::system_error &e)
225 {
226 std::cout << "Caught system_error with code " << e.code() << " meaning " << e.what() << '\n';
227 }
228 }
229 /** [Scheduler example] */
230}