Compute Library
 21.02
SchedulerTimer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2021 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 "SchedulerTimer.h"
25 
26 #include "Instruments.h"
27 #include "WallClockTimer.h"
30 #include "support/Cast.h"
31 
32 namespace arm_compute
33 {
34 namespace test
35 {
36 namespace framework
37 {
38 template <bool output_timestamps>
40 {
41  if(output_timestamps)
42  {
43  return "SchedulerTimestamps";
44  }
45  else
46  {
47  return "SchedulerTimer";
48  }
49 }
50 
51 template <bool output_timestamps>
52 class Interceptor final : public IScheduler
53 {
54 public:
55  /** Default constructor. */
56  Interceptor(std::list<struct SchedulerClock<output_timestamps>::kernel_info> &kernels, IScheduler &real_scheduler, ScaleFactor scale_factor)
57  : _kernels(kernels), _real_scheduler(real_scheduler), _timer(scale_factor), _prefix()
58  {
59  }
60 
61  void set_num_threads(unsigned int num_threads) override
62  {
63  _real_scheduler.set_num_threads(num_threads);
64  }
65 
66  void set_num_threads_with_affinity(unsigned int num_threads, BindFunc func) override
67  {
68  _real_scheduler.set_num_threads_with_affinity(num_threads, func);
69  }
70 
71  unsigned int num_threads() const override
72  {
73  return _real_scheduler.num_threads();
74  }
75 
76  void set_prefix(const std::string &prefix)
77  {
78  _prefix = prefix;
79  }
80 
81  void schedule(ICPPKernel *kernel, const Hints &hints) override
82  {
83  _timer.start();
84  _real_scheduler.schedule(kernel, hints);
85  _timer.stop();
86 
88  info.name = kernel->name();
89  info.prefix = _prefix;
90  info.measurements = _timer.measurements();
91  _kernels.push_back(std::move(info));
92  }
93 
94  void schedule_op(ICPPKernel *kernel, const Hints &hints, const Window &window, ITensorPack &tensors) override
95  {
96  _timer.start();
97  _real_scheduler.schedule_op(kernel, hints, window, tensors);
98  _timer.stop();
99 
101  info.name = kernel->name();
102  info.prefix = _prefix;
103  info.measurements = _timer.measurements();
104  _kernels.push_back(std::move(info));
105  }
106 
107  void run_tagged_workloads(std::vector<Workload> &workloads, const char *tag) override
108  {
109  _timer.start();
110  _real_scheduler.run_tagged_workloads(workloads, tag);
111  _timer.stop();
112 
114  info.name = tag != nullptr ? tag : "Unknown";
115  info.prefix = _prefix;
116  info.measurements = _timer.measurements();
117  _kernels.push_back(std::move(info));
118  }
119 
120 protected:
121  void run_workloads(std::vector<Workload> &workloads) override
122  {
123  ARM_COMPUTE_UNUSED(workloads);
124  ARM_COMPUTE_ERROR("Can't be reached");
125  }
126 
127 private:
128  std::list<struct SchedulerClock<output_timestamps>::kernel_info> &_kernels;
129  IScheduler &_real_scheduler;
131  std::string _prefix;
132 };
133 
134 template <bool output_timestamps>
136  : _kernels(), _real_scheduler(nullptr), _real_scheduler_type(), _real_graph_function(nullptr), _scale_factor(scale_factor), _interceptor(nullptr), _scheduler_users()
137 {
138  if(instruments_info != nullptr)
139  {
140  _scheduler_users = instruments_info->_scheduler_users;
141  }
142 }
143 
144 template <bool output_timestamps>
146 {
147  // Start intercepting tasks:
148  ARM_COMPUTE_ERROR_ON(_real_graph_function != nullptr);
149  _real_graph_function = graph::TaskExecutor::get().execute_function;
150  auto task_interceptor = [this](graph::ExecutionTask & task)
151  {
152  Interceptor<output_timestamps> *scheduler = nullptr;
153  if(dynamic_cast<Interceptor<output_timestamps> *>(this->_interceptor.get()) != nullptr)
154  {
155  scheduler = arm_compute::utils::cast::polymorphic_downcast<Interceptor<output_timestamps> *>(_interceptor.get());
156  if(task.node != nullptr && !task.node->name().empty())
157  {
158  scheduler->set_prefix(task.node->name() + "/");
159  }
160  else
161  {
162  scheduler->set_prefix("");
163  }
164  }
165 
166  this->_real_graph_function(task);
167 
168  if(scheduler != nullptr)
169  {
170  scheduler->set_prefix("");
171  }
172  };
173 
174  ARM_COMPUTE_ERROR_ON(_real_scheduler != nullptr);
175  _real_scheduler_type = Scheduler::get_type();
176  //Note: We can't currently replace a custom scheduler
177  if(_real_scheduler_type != Scheduler::Type::CUSTOM)
178  {
179  _real_scheduler = &Scheduler::get();
180  _interceptor = std::make_shared<Interceptor<output_timestamps>>(_kernels, *_real_scheduler, _scale_factor);
181  Scheduler::set(std::static_pointer_cast<IScheduler>(_interceptor));
182  graph::TaskExecutor::get().execute_function = task_interceptor;
183 
184  // Create an interceptor for each scheduler
185  // TODO(COMPID-2638) : Allow multiple schedulers, now it assumes the same scheduler is used.
186  std::for_each(std::begin(_scheduler_users), std::end(_scheduler_users),
187  [&](ISchedulerUser * user)
188  {
189  if(user != nullptr && user->scheduler() != nullptr)
190  {
191  user->intercept_scheduler(std::make_unique<Interceptor<output_timestamps>>(_kernels, *user->scheduler(), _scale_factor));
192  }
193  });
194  }
195 }
196 
197 template <bool output_timestamps>
199 {
200  _kernels.clear();
201 }
202 
203 template <bool output_timestamps>
205 {
206  // Restore real scheduler
207  Scheduler::set(_real_scheduler_type);
208  _real_scheduler = nullptr;
209  _interceptor = nullptr;
210  graph::TaskExecutor::get().execute_function = _real_graph_function;
211  _real_graph_function = nullptr;
212 
213  // Restore schedulers
214  std::for_each(std::begin(_scheduler_users), std::end(_scheduler_users),
215  [&](ISchedulerUser * user)
216  {
217  if(user != nullptr)
218  {
219  user->restore_scheduler();
220  }
221  });
222 }
223 
224 template <bool output_timestamps>
226 {
228  unsigned int kernel_number = 0;
229  for(auto kernel : _kernels)
230  {
231  std::string name = kernel.prefix + kernel.name + " #" + support::cpp11::to_string(kernel_number++);
232  if(output_timestamps)
233  {
234  ARM_COMPUTE_ERROR_ON(kernel.measurements.size() != 2);
235  for(auto const &m : kernel.measurements)
236  {
237  if(m.first.find("[start]") != std::string::npos)
238  {
239  measurements.emplace("[start]" + name, m.second);
240  }
241  else if(m.first.find("[end]") != std::string::npos)
242  {
243  measurements.emplace("[end]" + name, m.second);
244  }
245  else
246  {
247  ARM_COMPUTE_ERROR("Measurement not handled");
248  }
249  }
250  }
251  else
252  {
253  measurements.emplace(name, kernel.measurements.begin()->second);
254  }
255  }
256 
257  return measurements;
258 }
259 
260 } // namespace framework
261 } // namespace test
262 } // namespace arm_compute
263 
virtual IScheduler * scheduler()=0
Real scheduler accessor.
#define ARM_COMPUTE_ERROR(msg)
Print the given message then throw an std::runtime_error.
Definition: Error.h:352
Common interface for all kernels implemented in C++.
Definition: ICPPKernel.h:38
std::string to_string(T &&value)
Convert integer and float values to string.
Scheduler interface to run kernels.
Definition: IScheduler.h:41
Instrument::MeasurementsMap measurements
Time it took the kernel to run.
std::unique_ptr< InstrumentsInfo > instruments_info
Definition: Framework.cpp:50
static void set(std::shared_ptr< IScheduler > scheduler)
Sets the user defined scheduler and makes it the active scheduler.
Definition: Scheduler.cpp:126
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
Instrument::MeasurementsMap measurements() const override
Return the latest measurements.
Implementation of an instrument to measure elapsed wall-clock time in milliseconds.
Copyright (c) 2017-2021 Arm Limited.
SchedulerClock(ScaleFactor scale_factor)
Construct a Scheduler timer.
#define ARM_COMPUTE_UNUSED(...)
To avoid unused variables warnings.
Definition: Error.h:152
void start() override
Start measuring.
std::string id() const override
Identifier for the instrument.
virtual void restore_scheduler()=0
Restore the original scheduler.
void end(TokenStream &in, bool &valid)
Definition: MLGOParser.cpp:290
static TaskExecutor & get()
Task executor accessor.
Definition: Workload.cpp:74
const char * name
void for_each(F &&)
Base case of for_each.
Definition: Utility.h:108
FloorUKernelPtr func
void test_stop() override
End of the test.
ScaleKernelInfo info(interpolation_policy, default_border_mode, PixelValue(), sampling_policy, false)
static Type get_type()
Returns the type of the active scheduler.
Definition: Scheduler.cpp:89
Tensor packing service.
Definition: ITensorPack.h:37
std::map< std::string, Measurement > MeasurementsMap
Map of measurements.
Definition: Instrument.h:109
void test_start() override
Start of the test.
virtual void intercept_scheduler(std::unique_ptr< IScheduler > interceptor)=0
Intercept the scheduler used by.
Describe a multidimensional execution window.
Definition: Window.h:39
virtual const char * name() const =0
Name of the kernel.
std::function< decltype(execute_task)> execute_function
Function that is responsible for executing tasks.
Definition: Workload.h:63
Instrument creating measurements based on the information returned by clGetEventProfilingInfo for eac...
static IScheduler & get()
Access the scheduler singleton.
Definition: Scheduler.cpp:94