xref: /aosp_15_r20/external/ComputeLibrary/tests/framework/instruments/SchedulerTimer.cpp (revision c217d954acce2dbc11938adb493fc0abd69584f3)
1*c217d954SCole Faust /*
2*c217d954SCole Faust  * Copyright (c) 2017-2021 Arm Limited.
3*c217d954SCole Faust  *
4*c217d954SCole Faust  * SPDX-License-Identifier: MIT
5*c217d954SCole Faust  *
6*c217d954SCole Faust  * Permission is hereby granted, free of charge, to any person obtaining a copy
7*c217d954SCole Faust  * of this software and associated documentation files (the "Software"), to
8*c217d954SCole Faust  * deal in the Software without restriction, including without limitation the
9*c217d954SCole Faust  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10*c217d954SCole Faust  * sell copies of the Software, and to permit persons to whom the Software is
11*c217d954SCole Faust  * furnished to do so, subject to the following conditions:
12*c217d954SCole Faust  *
13*c217d954SCole Faust  * The above copyright notice and this permission notice shall be included in all
14*c217d954SCole Faust  * copies or substantial portions of the Software.
15*c217d954SCole Faust  *
16*c217d954SCole Faust  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*c217d954SCole Faust  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*c217d954SCole Faust  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19*c217d954SCole Faust  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*c217d954SCole Faust  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21*c217d954SCole Faust  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22*c217d954SCole Faust  * SOFTWARE.
23*c217d954SCole Faust  */
24*c217d954SCole Faust #include "SchedulerTimer.h"
25*c217d954SCole Faust 
26*c217d954SCole Faust #include "Instruments.h"
27*c217d954SCole Faust #include "WallClockTimer.h"
28*c217d954SCole Faust #include "arm_compute/core/CPP/ICPPKernel.h"
29*c217d954SCole Faust #include "arm_compute/graph/DataLayerVisitor.h"
30*c217d954SCole Faust #include "arm_compute/graph/INode.h"
31*c217d954SCole Faust #include "support/Cast.h"
32*c217d954SCole Faust 
33*c217d954SCole Faust namespace arm_compute
34*c217d954SCole Faust {
35*c217d954SCole Faust namespace test
36*c217d954SCole Faust {
37*c217d954SCole Faust namespace framework
38*c217d954SCole Faust {
39*c217d954SCole Faust template <bool output_timestamps>
id() const40*c217d954SCole Faust std::string    SchedulerClock<output_timestamps>::id() const
41*c217d954SCole Faust {
42*c217d954SCole Faust     if(output_timestamps)
43*c217d954SCole Faust     {
44*c217d954SCole Faust         return "SchedulerTimestamps";
45*c217d954SCole Faust     }
46*c217d954SCole Faust     else
47*c217d954SCole Faust     {
48*c217d954SCole Faust         return "SchedulerTimer";
49*c217d954SCole Faust     }
50*c217d954SCole Faust }
51*c217d954SCole Faust 
52*c217d954SCole Faust template <bool    output_timestamps>
53*c217d954SCole Faust class Interceptor final : public IScheduler
54*c217d954SCole Faust {
55*c217d954SCole Faust public:
56*c217d954SCole Faust     /** 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*c217d954SCole Faust     Interceptor(std::list<struct SchedulerClock<output_timestamps>::kernel_info> &kernels,
58*c217d954SCole Faust                 std::map<std::string, SchedulerTimer::LayerData> &layers, IScheduler &real_scheduler,
59*c217d954SCole Faust                 ScaleFactor scale_factor)
60*c217d954SCole Faust         : _kernels(kernels), _layer_data_map(layers), _real_scheduler(real_scheduler), _timer(scale_factor), _prefix()
61*c217d954SCole Faust     {
62*c217d954SCole Faust     }
63*c217d954SCole Faust 
set_num_threads(unsigned int num_threads)64*c217d954SCole Faust     void set_num_threads(unsigned int num_threads) override
65*c217d954SCole Faust     {
66*c217d954SCole Faust         _real_scheduler.set_num_threads(num_threads);
67*c217d954SCole Faust     }
68*c217d954SCole Faust 
set_num_threads_with_affinity(unsigned int num_threads,BindFunc func)69*c217d954SCole Faust     void set_num_threads_with_affinity(unsigned int num_threads, BindFunc func) override
70*c217d954SCole Faust     {
71*c217d954SCole Faust         _real_scheduler.set_num_threads_with_affinity(num_threads, func);
72*c217d954SCole Faust     }
73*c217d954SCole Faust 
num_threads() const74*c217d954SCole Faust     unsigned int num_threads() const override
75*c217d954SCole Faust     {
76*c217d954SCole Faust         return _real_scheduler.num_threads();
77*c217d954SCole Faust     }
78*c217d954SCole Faust 
set_prefix(const std::string & prefix)79*c217d954SCole Faust     void set_prefix(const std::string &prefix)
80*c217d954SCole Faust     {
81*c217d954SCole Faust         _prefix = prefix;
82*c217d954SCole Faust     }
83*c217d954SCole Faust 
schedule(ICPPKernel * kernel,const Hints & hints)84*c217d954SCole Faust     void schedule(ICPPKernel *kernel, const Hints &hints) override
85*c217d954SCole Faust     {
86*c217d954SCole Faust         _timer.start();
87*c217d954SCole Faust         _real_scheduler.schedule(kernel, hints);
88*c217d954SCole Faust         _timer.stop();
89*c217d954SCole Faust 
90*c217d954SCole Faust         typename SchedulerClock<output_timestamps>::kernel_info info;
91*c217d954SCole Faust         info.name         = kernel->name();
92*c217d954SCole Faust         info.prefix       = _prefix;
93*c217d954SCole Faust         info.measurements = _timer.measurements();
94*c217d954SCole Faust         _kernels.push_back(std::move(info));
95*c217d954SCole Faust     }
96*c217d954SCole Faust 
schedule_op(ICPPKernel * kernel,const Hints & hints,const Window & window,ITensorPack & tensors)97*c217d954SCole Faust     void schedule_op(ICPPKernel *kernel, const Hints &hints, const Window &window, ITensorPack &tensors) override
98*c217d954SCole Faust     {
99*c217d954SCole Faust         _timer.start();
100*c217d954SCole Faust         _real_scheduler.schedule_op(kernel, hints, window, tensors);
101*c217d954SCole Faust         _timer.stop();
102*c217d954SCole Faust 
103*c217d954SCole Faust         typename SchedulerClock<output_timestamps>::kernel_info info;
104*c217d954SCole Faust         info.name         = kernel->name();
105*c217d954SCole Faust         info.prefix       = _prefix;
106*c217d954SCole Faust         info.measurements = _timer.measurements();
107*c217d954SCole Faust         _kernels.push_back(std::move(info));
108*c217d954SCole Faust     }
109*c217d954SCole Faust 
run_tagged_workloads(std::vector<Workload> & workloads,const char * tag)110*c217d954SCole Faust     void run_tagged_workloads(std::vector<Workload> &workloads, const char *tag) override
111*c217d954SCole Faust     {
112*c217d954SCole Faust         _timer.start();
113*c217d954SCole Faust         _real_scheduler.run_tagged_workloads(workloads, tag);
114*c217d954SCole Faust         _timer.stop();
115*c217d954SCole Faust 
116*c217d954SCole Faust         typename SchedulerClock<output_timestamps>::kernel_info info;
117*c217d954SCole Faust         info.name         = tag != nullptr ? tag : "Unknown";
118*c217d954SCole Faust         info.prefix       = _prefix;
119*c217d954SCole Faust         info.measurements = _timer.measurements();
120*c217d954SCole Faust         _kernels.push_back(std::move(info));
121*c217d954SCole Faust     }
122*c217d954SCole Faust 
123*c217d954SCole Faust protected:
run_workloads(std::vector<Workload> & workloads)124*c217d954SCole Faust     void run_workloads(std::vector<Workload> &workloads) override
125*c217d954SCole Faust     {
126*c217d954SCole Faust         ARM_COMPUTE_UNUSED(workloads);
127*c217d954SCole Faust         ARM_COMPUTE_ERROR("Can't be reached");
128*c217d954SCole Faust     }
129*c217d954SCole Faust 
130*c217d954SCole Faust private:
131*c217d954SCole Faust     std::list<struct SchedulerClock<output_timestamps>::kernel_info> &_kernels;
132*c217d954SCole Faust     std::map<std::string, SchedulerTimer::LayerData> &_layer_data_map;
133*c217d954SCole Faust     IScheduler                  &_real_scheduler;
134*c217d954SCole Faust     WallClock<output_timestamps> _timer;
135*c217d954SCole Faust     std::string                  _prefix;
136*c217d954SCole Faust };
137*c217d954SCole Faust 
138*c217d954SCole Faust template <bool output_timestamps>
SchedulerClock(ScaleFactor scale_factor)139*c217d954SCole Faust SchedulerClock<output_timestamps>::SchedulerClock(ScaleFactor scale_factor)
140*c217d954SCole Faust     : _kernels(),
141*c217d954SCole Faust       _layer_data_map(),
142*c217d954SCole Faust       _real_scheduler(nullptr),
143*c217d954SCole Faust       _real_scheduler_type(),
144*c217d954SCole Faust #ifdef ARM_COMPUTE_GRAPH_ENABLED
145*c217d954SCole Faust       _real_graph_function(nullptr),
146*c217d954SCole Faust #endif /* ARM_COMPUTE_GRAPH_ENABLED */
147*c217d954SCole Faust       _scale_factor(scale_factor),
148*c217d954SCole Faust       _interceptor(nullptr),
149*c217d954SCole Faust       _scheduler_users()
150*c217d954SCole Faust {
151*c217d954SCole Faust     if(instruments_info != nullptr)
152*c217d954SCole Faust     {
153*c217d954SCole Faust         _scheduler_users = instruments_info->_scheduler_users;
154*c217d954SCole Faust     }
155*c217d954SCole Faust }
156*c217d954SCole Faust 
157*c217d954SCole Faust template <bool output_timestamps>
test_start()158*c217d954SCole Faust void           SchedulerClock<output_timestamps>::test_start()
159*c217d954SCole Faust {
160*c217d954SCole Faust #ifdef ARM_COMPUTE_GRAPH_ENABLED
161*c217d954SCole Faust     // Start intercepting tasks:
162*c217d954SCole Faust     ARM_COMPUTE_ERROR_ON(_real_graph_function != nullptr);
163*c217d954SCole Faust     _real_graph_function  = graph::TaskExecutor::get().execute_function;
164*c217d954SCole Faust     auto task_interceptor = [this](graph::ExecutionTask & task)
165*c217d954SCole Faust     {
166*c217d954SCole Faust         Interceptor<output_timestamps> *scheduler = nullptr;
167*c217d954SCole Faust         if(dynamic_cast<Interceptor<output_timestamps> *>(this->_interceptor.get()) != nullptr)
168*c217d954SCole Faust         {
169*c217d954SCole Faust             scheduler = arm_compute::utils::cast::polymorphic_downcast<Interceptor<output_timestamps> *>(_interceptor.get());
170*c217d954SCole Faust             if(task.node != nullptr && !task.node->name().empty())
171*c217d954SCole Faust             {
172*c217d954SCole Faust                 scheduler->set_prefix(task.node->name() + "/");
173*c217d954SCole Faust 
174*c217d954SCole Faust                 if(_layer_data_map.find(task.node->name()) == _layer_data_map.end())
175*c217d954SCole Faust                 {
176*c217d954SCole Faust                     arm_compute::graph::DataLayerVisitor dlv = {};
177*c217d954SCole Faust                     task.node->accept(dlv);
178*c217d954SCole Faust                     _layer_data_map[task.node->name()] = dlv.layer_data();
179*c217d954SCole Faust                 }
180*c217d954SCole Faust             }
181*c217d954SCole Faust             else
182*c217d954SCole Faust             {
183*c217d954SCole Faust                 scheduler->set_prefix("");
184*c217d954SCole Faust             }
185*c217d954SCole Faust         }
186*c217d954SCole Faust 
187*c217d954SCole Faust         this->_real_graph_function(task);
188*c217d954SCole Faust 
189*c217d954SCole Faust         if(scheduler != nullptr)
190*c217d954SCole Faust         {
191*c217d954SCole Faust             scheduler->set_prefix("");
192*c217d954SCole Faust         }
193*c217d954SCole Faust     };
194*c217d954SCole Faust #endif /* ARM_COMPUTE_GRAPH_ENABLED */
195*c217d954SCole Faust 
196*c217d954SCole Faust     ARM_COMPUTE_ERROR_ON(_real_scheduler != nullptr);
197*c217d954SCole Faust     _real_scheduler_type = Scheduler::get_type();
198*c217d954SCole Faust     //Note: We can't currently replace a custom scheduler
199*c217d954SCole Faust     if(_real_scheduler_type != Scheduler::Type::CUSTOM)
200*c217d954SCole Faust     {
201*c217d954SCole Faust         _real_scheduler = &Scheduler::get();
202*c217d954SCole Faust         _interceptor    = std::make_shared<Interceptor<output_timestamps>>(_kernels, _layer_data_map, *_real_scheduler, _scale_factor);
203*c217d954SCole Faust         Scheduler::set(std::static_pointer_cast<IScheduler>(_interceptor));
204*c217d954SCole Faust #ifdef ARM_COMPUTE_GRAPH_ENABLED
205*c217d954SCole Faust         graph::TaskExecutor::get().execute_function = task_interceptor;
206*c217d954SCole Faust #endif /* ARM_COMPUTE_GRAPH_ENABLED */
207*c217d954SCole Faust 
208*c217d954SCole Faust         // Create an interceptor for each scheduler
209*c217d954SCole Faust         // TODO(COMPID-2638) : Allow multiple schedulers, now it assumes the same scheduler is used.
210*c217d954SCole Faust         std::for_each(std::begin(_scheduler_users), std::end(_scheduler_users),
211*c217d954SCole Faust                       [&](ISchedulerUser * user)
212*c217d954SCole Faust         {
213*c217d954SCole Faust             if(user != nullptr && user->scheduler() != nullptr)
214*c217d954SCole Faust             {
215*c217d954SCole Faust                 user->intercept_scheduler(std::make_unique<Interceptor<output_timestamps>>(_kernels, _layer_data_map, *user->scheduler(), _scale_factor));
216*c217d954SCole Faust             }
217*c217d954SCole Faust         });
218*c217d954SCole Faust     }
219*c217d954SCole Faust }
220*c217d954SCole Faust 
221*c217d954SCole Faust template <bool output_timestamps>
start()222*c217d954SCole Faust void           SchedulerClock<output_timestamps>::start()
223*c217d954SCole Faust {
224*c217d954SCole Faust     _kernels.clear();
225*c217d954SCole Faust }
226*c217d954SCole Faust 
227*c217d954SCole Faust template <bool output_timestamps>
test_stop()228*c217d954SCole Faust void           SchedulerClock<output_timestamps>::test_stop()
229*c217d954SCole Faust {
230*c217d954SCole Faust     // Restore real scheduler
231*c217d954SCole Faust     Scheduler::set(_real_scheduler_type);
232*c217d954SCole Faust     _real_scheduler = nullptr;
233*c217d954SCole Faust     _interceptor    = nullptr;
234*c217d954SCole Faust #ifdef ARM_COMPUTE_GRAPH_ENABLED
235*c217d954SCole Faust     graph::TaskExecutor::get().execute_function = _real_graph_function;
236*c217d954SCole Faust     _real_graph_function                        = nullptr;
237*c217d954SCole Faust #endif /* ARM_COMPUTE_GRAPH_ENABLED */
238*c217d954SCole Faust 
239*c217d954SCole Faust     // Restore schedulers
240*c217d954SCole Faust     std::for_each(std::begin(_scheduler_users), std::end(_scheduler_users),
241*c217d954SCole Faust                   [&](ISchedulerUser * user)
242*c217d954SCole Faust     {
243*c217d954SCole Faust         if(user != nullptr)
244*c217d954SCole Faust         {
245*c217d954SCole Faust             user->restore_scheduler();
246*c217d954SCole Faust         }
247*c217d954SCole Faust     });
248*c217d954SCole Faust }
249*c217d954SCole Faust 
250*c217d954SCole Faust template <bool              output_timestamps>
measurements() const251*c217d954SCole Faust Instrument::MeasurementsMap SchedulerClock<output_timestamps>::measurements() const
252*c217d954SCole Faust {
253*c217d954SCole Faust     MeasurementsMap measurements;
254*c217d954SCole Faust     unsigned int    kernel_number = 0;
255*c217d954SCole Faust     for(auto kernel : _kernels)
256*c217d954SCole Faust     {
257*c217d954SCole Faust         std::string name = kernel.prefix + kernel.name + " #" + support::cpp11::to_string(kernel_number++);
258*c217d954SCole Faust         if(output_timestamps)
259*c217d954SCole Faust         {
260*c217d954SCole Faust             ARM_COMPUTE_ERROR_ON(kernel.measurements.size() != 2);
261*c217d954SCole Faust             for(auto const &m : kernel.measurements)
262*c217d954SCole Faust             {
263*c217d954SCole Faust                 if(m.first.find("[start]") != std::string::npos)
264*c217d954SCole Faust                 {
265*c217d954SCole Faust                     measurements.emplace("[start]" + name, m.second);
266*c217d954SCole Faust                 }
267*c217d954SCole Faust                 else if(m.first.find("[end]") != std::string::npos)
268*c217d954SCole Faust                 {
269*c217d954SCole Faust                     measurements.emplace("[end]" + name, m.second);
270*c217d954SCole Faust                 }
271*c217d954SCole Faust                 else
272*c217d954SCole Faust                 {
273*c217d954SCole Faust                     ARM_COMPUTE_ERROR("Measurement not handled");
274*c217d954SCole Faust                 }
275*c217d954SCole Faust             }
276*c217d954SCole Faust         }
277*c217d954SCole Faust         else
278*c217d954SCole Faust         {
279*c217d954SCole Faust             measurements.emplace(name, kernel.measurements.begin()->second);
280*c217d954SCole Faust         }
281*c217d954SCole Faust     }
282*c217d954SCole Faust 
283*c217d954SCole Faust     return measurements;
284*c217d954SCole Faust }
285*c217d954SCole Faust 
286*c217d954SCole Faust template <bool output_timestamps>
instrument_header() const287*c217d954SCole Faust std::string    SchedulerClock<output_timestamps>::instrument_header() const
288*c217d954SCole Faust {
289*c217d954SCole Faust     std::string output{ "" };
290*c217d954SCole Faust     output += R"("layer_data" : {)";
291*c217d954SCole Faust     for(auto i_it = _layer_data_map.cbegin(), i_end = _layer_data_map.cend(); i_it != i_end; ++i_it)
292*c217d954SCole Faust     {
293*c217d954SCole Faust         output += "\"" + i_it->first + "\" : {";
294*c217d954SCole Faust         if(i_it->second.size() != 0)
295*c217d954SCole Faust         {
296*c217d954SCole Faust             // Print for each entry in layer
297*c217d954SCole Faust             for(auto entry_it = i_it->second.cbegin(), entry_end = i_it->second.cend(); entry_it != entry_end; ++entry_it)
298*c217d954SCole Faust             {
299*c217d954SCole Faust                 output += "\"" + entry_it->first + "\" : \"" + entry_it->second + "\"";
300*c217d954SCole Faust                 if(std::next(entry_it) != entry_end)
301*c217d954SCole Faust                 {
302*c217d954SCole Faust                     output += ",";
303*c217d954SCole Faust                 }
304*c217d954SCole Faust             }
305*c217d954SCole Faust         }
306*c217d954SCole Faust         output += "}";
307*c217d954SCole Faust         if(std::next(i_it) != i_end)
308*c217d954SCole Faust         {
309*c217d954SCole Faust             output += ",";
310*c217d954SCole Faust         }
311*c217d954SCole Faust     }
312*c217d954SCole Faust     output += "}";
313*c217d954SCole Faust     return output;
314*c217d954SCole Faust }
315*c217d954SCole Faust 
316*c217d954SCole Faust } // namespace framework
317*c217d954SCole Faust } // namespace test
318*c217d954SCole Faust } // namespace arm_compute
319*c217d954SCole Faust 
320*c217d954SCole Faust template class arm_compute::test::framework::SchedulerClock<true>;
321*c217d954SCole Faust template class arm_compute::test::framework::SchedulerClock<false>;
322