xref: /aosp_15_r20/external/ComputeLibrary/tests/framework/instruments/SchedulerTimer.cpp (revision c217d954acce2dbc11938adb493fc0abd69584f3)
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"
28 #include "arm_compute/core/CPP/ICPPKernel.h"
29 #include "arm_compute/graph/DataLayerVisitor.h"
30 #include "arm_compute/graph/INode.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>
id() const40 std::string    SchedulerClock<output_timestamps>::id() const
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. */
Interceptor(std::list<struct SchedulerClock<output_timestamps>::kernel_info> & kernels,std::map<std::string,SchedulerTimer::LayerData> & layers,IScheduler & real_scheduler,ScaleFactor scale_factor)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 
set_num_threads(unsigned int num_threads)64     void set_num_threads(unsigned int num_threads) override
65     {
66         _real_scheduler.set_num_threads(num_threads);
67     }
68 
set_num_threads_with_affinity(unsigned int num_threads,BindFunc func)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 
num_threads() const74     unsigned int num_threads() const override
75     {
76         return _real_scheduler.num_threads();
77     }
78 
set_prefix(const std::string & prefix)79     void set_prefix(const std::string &prefix)
80     {
81         _prefix = prefix;
82     }
83 
schedule(ICPPKernel * kernel,const Hints & hints)84     void schedule(ICPPKernel *kernel, const Hints &hints) override
85     {
86         _timer.start();
87         _real_scheduler.schedule(kernel, hints);
88         _timer.stop();
89 
90         typename SchedulerClock<output_timestamps>::kernel_info info;
91         info.name         = kernel->name();
92         info.prefix       = _prefix;
93         info.measurements = _timer.measurements();
94         _kernels.push_back(std::move(info));
95     }
96 
schedule_op(ICPPKernel * kernel,const Hints & hints,const Window & window,ITensorPack & tensors)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 
103         typename SchedulerClock<output_timestamps>::kernel_info info;
104         info.name         = kernel->name();
105         info.prefix       = _prefix;
106         info.measurements = _timer.measurements();
107         _kernels.push_back(std::move(info));
108     }
109 
run_tagged_workloads(std::vector<Workload> & workloads,const char * tag)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 
116         typename SchedulerClock<output_timestamps>::kernel_info info;
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:
run_workloads(std::vector<Workload> & workloads)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;
134     WallClock<output_timestamps> _timer;
135     std::string                  _prefix;
136 };
137 
138 template <bool output_timestamps>
SchedulerClock(ScaleFactor scale_factor)139 SchedulerClock<output_timestamps>::SchedulerClock(ScaleFactor scale_factor)
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>
test_start()158 void           SchedulerClock<output_timestamps>::test_start()
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                 {
176                     arm_compute::graph::DataLayerVisitor dlv = {};
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>
start()222 void           SchedulerClock<output_timestamps>::start()
223 {
224     _kernels.clear();
225 }
226 
227 template <bool output_timestamps>
test_stop()228 void           SchedulerClock<output_timestamps>::test_stop()
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>
measurements() const251 Instrument::MeasurementsMap SchedulerClock<output_timestamps>::measurements() const
252 {
253     MeasurementsMap measurements;
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>
instrument_header() const287 std::string    SchedulerClock<output_timestamps>::instrument_header() const
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 
320 template class arm_compute::test::framework::SchedulerClock<true>;
321 template class arm_compute::test::framework::SchedulerClock<false>;
322