Compute Library
 22.05
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"
31 #include "support/Cast.h"
32 
33 namespace arm_compute
34 {
35 namespace test
36 {
37 namespace framework
38 {
39 template <bool output_timestamps>
41 {
42  if(output_timestamps)
43  {
44  return "SchedulerTimestamps";
45  }
46  else
47  {
48  return "SchedulerTimer";
49  }
50 }
51 
52 template <bool output_timestamps>
53 class Interceptor final : public IScheduler
54 {
55 public:
56  /** Default constructor. */
57  Interceptor(std::list<struct SchedulerClock<output_timestamps>::kernel_info> &kernels,
58  std::map<std::string, SchedulerTimer::LayerData> &layers, IScheduler &real_scheduler,
59  ScaleFactor scale_factor)
60  : _kernels(kernels), _layer_data_map(layers), _real_scheduler(real_scheduler), _timer(scale_factor), _prefix()
61  {
62  }
63 
64  void set_num_threads(unsigned int num_threads) override
65  {
66  _real_scheduler.set_num_threads(num_threads);
67  }
68 
69  void set_num_threads_with_affinity(unsigned int num_threads, BindFunc func) override
70  {
71  _real_scheduler.set_num_threads_with_affinity(num_threads, func);
72  }
73 
74  unsigned int num_threads() const override
75  {
76  return _real_scheduler.num_threads();
77  }
78 
79  void set_prefix(const std::string &prefix)
80  {
81  _prefix = prefix;
82  }
83 
84  void schedule(ICPPKernel *kernel, const Hints &hints) override
85  {
86  _timer.start();
87  _real_scheduler.schedule(kernel, hints);
88  _timer.stop();
89 
91  info.name = kernel->name();
92  info.prefix = _prefix;
93  info.measurements = _timer.measurements();
94  _kernels.push_back(std::move(info));
95  }
96 
97  void schedule_op(ICPPKernel *kernel, const Hints &hints, const Window &window, ITensorPack &tensors) override
98  {
99  _timer.start();
100  _real_scheduler.schedule_op(kernel, hints, window, tensors);
101  _timer.stop();
102 
104  info.name = kernel->name();
105  info.prefix = _prefix;
106  info.measurements = _timer.measurements();
107  _kernels.push_back(std::move(info));
108  }
109 
110  void run_tagged_workloads(std::vector<Workload> &workloads, const char *tag) override
111  {
112  _timer.start();
113  _real_scheduler.run_tagged_workloads(workloads, tag);
114  _timer.stop();
115 
117  info.name = tag != nullptr ? tag : "Unknown";
118  info.prefix = _prefix;
119  info.measurements = _timer.measurements();
120  _kernels.push_back(std::move(info));
121  }
122 
123 protected:
124  void run_workloads(std::vector<Workload> &workloads) override
125  {
126  ARM_COMPUTE_UNUSED(workloads);
127  ARM_COMPUTE_ERROR("Can't be reached");
128  }
129 
130 private:
131  std::list<struct SchedulerClock<output_timestamps>::kernel_info> &_kernels;
132  std::map<std::string, SchedulerTimer::LayerData> &_layer_data_map;
133  IScheduler &_real_scheduler;
135  std::string _prefix;
136 };
137 
138 template <bool output_timestamps>
140  : _kernels(),
141  _layer_data_map(),
142  _real_scheduler(nullptr),
143  _real_scheduler_type(),
144 #ifdef ARM_COMPUTE_GRAPH_ENABLED
145  _real_graph_function(nullptr),
146 #endif /* ARM_COMPUTE_GRAPH_ENABLED */
147  _scale_factor(scale_factor),
148  _interceptor(nullptr),
149  _scheduler_users()
150 {
151  if(instruments_info != nullptr)
152  {
153  _scheduler_users = instruments_info->_scheduler_users;
154  }
155 }
156 
157 template <bool output_timestamps>
159 {
160 #ifdef ARM_COMPUTE_GRAPH_ENABLED
161  // Start intercepting tasks:
162  ARM_COMPUTE_ERROR_ON(_real_graph_function != nullptr);
163  _real_graph_function = graph::TaskExecutor::get().execute_function;
164  auto task_interceptor = [this](graph::ExecutionTask & task)
165  {
166  Interceptor<output_timestamps> *scheduler = nullptr;
167  if(dynamic_cast<Interceptor<output_timestamps> *>(this->_interceptor.get()) != nullptr)
168  {
169  scheduler = arm_compute::utils::cast::polymorphic_downcast<Interceptor<output_timestamps> *>(_interceptor.get());
170  if(task.node != nullptr && !task.node->name().empty())
171  {
172  scheduler->set_prefix(task.node->name() + "/");
173 
174  if(_layer_data_map.find(task.node->name()) == _layer_data_map.end())
175  {
177  task.node->accept(dlv);
178  _layer_data_map[task.node->name()] = dlv.layer_data();
179  }
180  }
181  else
182  {
183  scheduler->set_prefix("");
184  }
185  }
186 
187  this->_real_graph_function(task);
188 
189  if(scheduler != nullptr)
190  {
191  scheduler->set_prefix("");
192  }
193  };
194 #endif /* ARM_COMPUTE_GRAPH_ENABLED */
195 
196  ARM_COMPUTE_ERROR_ON(_real_scheduler != nullptr);
197  _real_scheduler_type = Scheduler::get_type();
198  //Note: We can't currently replace a custom scheduler
199  if(_real_scheduler_type != Scheduler::Type::CUSTOM)
200  {
201  _real_scheduler = &Scheduler::get();
202  _interceptor = std::make_shared<Interceptor<output_timestamps>>(_kernels, _layer_data_map, *_real_scheduler, _scale_factor);
203  Scheduler::set(std::static_pointer_cast<IScheduler>(_interceptor));
204 #ifdef ARM_COMPUTE_GRAPH_ENABLED
205  graph::TaskExecutor::get().execute_function = task_interceptor;
206 #endif /* ARM_COMPUTE_GRAPH_ENABLED */
207 
208  // Create an interceptor for each scheduler
209  // TODO(COMPID-2638) : Allow multiple schedulers, now it assumes the same scheduler is used.
210  std::for_each(std::begin(_scheduler_users), std::end(_scheduler_users),
211  [&](ISchedulerUser * user)
212  {
213  if(user != nullptr && user->scheduler() != nullptr)
214  {
215  user->intercept_scheduler(std::make_unique<Interceptor<output_timestamps>>(_kernels, _layer_data_map, *user->scheduler(), _scale_factor));
216  }
217  });
218  }
219 }
220 
221 template <bool output_timestamps>
223 {
224  _kernels.clear();
225 }
226 
227 template <bool output_timestamps>
229 {
230  // Restore real scheduler
231  Scheduler::set(_real_scheduler_type);
232  _real_scheduler = nullptr;
233  _interceptor = nullptr;
234 #ifdef ARM_COMPUTE_GRAPH_ENABLED
235  graph::TaskExecutor::get().execute_function = _real_graph_function;
236  _real_graph_function = nullptr;
237 #endif /* ARM_COMPUTE_GRAPH_ENABLED */
238 
239  // Restore schedulers
240  std::for_each(std::begin(_scheduler_users), std::end(_scheduler_users),
241  [&](ISchedulerUser * user)
242  {
243  if(user != nullptr)
244  {
245  user->restore_scheduler();
246  }
247  });
248 }
249 
250 template <bool output_timestamps>
252 {
254  unsigned int kernel_number = 0;
255  for(auto kernel : _kernels)
256  {
257  std::string name = kernel.prefix + kernel.name + " #" + support::cpp11::to_string(kernel_number++);
258  if(output_timestamps)
259  {
260  ARM_COMPUTE_ERROR_ON(kernel.measurements.size() != 2);
261  for(auto const &m : kernel.measurements)
262  {
263  if(m.first.find("[start]") != std::string::npos)
264  {
265  measurements.emplace("[start]" + name, m.second);
266  }
267  else if(m.first.find("[end]") != std::string::npos)
268  {
269  measurements.emplace("[end]" + name, m.second);
270  }
271  else
272  {
273  ARM_COMPUTE_ERROR("Measurement not handled");
274  }
275  }
276  }
277  else
278  {
279  measurements.emplace(name, kernel.measurements.begin()->second);
280  }
281  }
282 
283  return measurements;
284 }
285 
286 template <bool output_timestamps>
288 {
289  std::string output{ "" };
290  output += R"("layer_data" : {)";
291  for(auto i_it = _layer_data_map.cbegin(), i_end = _layer_data_map.cend(); i_it != i_end; ++i_it)
292  {
293  output += "\"" + i_it->first + "\" : {";
294  if(i_it->second.size() != 0)
295  {
296  // Print for each entry in layer
297  for(auto entry_it = i_it->second.cbegin(), entry_end = i_it->second.cend(); entry_it != entry_end; ++entry_it)
298  {
299  output += "\"" + entry_it->first + "\" : \"" + entry_it->second + "\"";
300  if(std::next(entry_it) != entry_end)
301  {
302  output += ",";
303  }
304  }
305  }
306  output += "}";
307  if(std::next(i_it) != i_end)
308  {
309  output += ",";
310  }
311  }
312  output += "}";
313  return output;
314 }
315 
316 } // namespace framework
317 } // namespace test
318 } // namespace arm_compute
319 
virtual IScheduler * scheduler()=0
Real scheduler accessor.
std::string instrument_header() const override
Return JSON formatted instrument header string.
#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.
const LayerData & layer_data() const
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-2022 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
const char * name
static TaskExecutor & get()
Task executor accessor.
Definition: Workload.cpp:75
void for_each(F &&)
Base case of for_each.
Definition: Utility.h:110
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:39
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