Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / runtime / onert / core / src / util / MDTableEventWriter.cc
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "EventWriter.h"
18
19 #include <cassert>
20 #include <map>
21 #include <set>
22 #include <sstream>
23 #include <stdint.h>
24 #include <unordered_map>
25 #include <utility>
26 #include <vector>
27
28 // md table type
29 namespace
30 {
31
32 void writeMDTableRow(std::ostream &os, const std::vector<std::string> &list)
33 {
34   os << "| ";
35   for (const auto &key : list)
36   {
37     os << key << " | ";
38   }
39   os << "\n";
40 }
41
42 struct MDContent
43 {
44   std::string name;
45   uint64_t begin_ts;
46   uint64_t end_ts;
47   uint32_t min_rss;
48   uint32_t max_rss;
49   uint32_t min_page_reclaims;
50   uint32_t max_page_reclaims;
51
52   MDContent()
53     : begin_ts(0), end_ts(0), min_rss(UINT32_MAX), max_rss(0), min_page_reclaims(UINT32_MAX),
54       max_page_reclaims(0)
55   {
56     // DO NOTHING
57   }
58
59   virtual ~MDContent() = default;
60
61   void updateRss(uint32_t rss)
62   {
63     if (min_rss == UINT32_MAX)
64       min_rss = rss;
65     if (max_rss == 0)
66       max_rss = rss;
67
68     if (min_rss > rss)
69       min_rss = rss;
70     else if (max_rss < rss)
71       max_rss = rss;
72   }
73
74   void updateMinflt(uint32_t minflt)
75   {
76     if (min_page_reclaims == UINT32_MAX)
77       min_page_reclaims = minflt;
78     if (max_page_reclaims == 0)
79       max_page_reclaims = minflt;
80
81     if (min_page_reclaims > minflt)
82       min_page_reclaims = minflt;
83     else if (max_page_reclaims < minflt)
84       max_page_reclaims = minflt;
85   }
86
87   virtual void write(std::ostream &os) const = 0;
88 };
89
90 struct Operation : public MDContent
91 {
92   std::string backend;
93   uint64_t graph_latency;
94
95   struct OperationCmp
96   {
97     bool operator()(const Operation &lhs, const Operation &rhs) const
98     {
99       return lhs.begin_ts < rhs.begin_ts;
100     }
101     bool operator()(const Operation &lhs, const Operation &rhs)
102     {
103       return lhs.begin_ts < rhs.begin_ts;
104     }
105     bool operator()(Operation &lhs, Operation &rhs) { return lhs.begin_ts < rhs.begin_ts; }
106   };
107
108   void write(std::ostream &os) const override
109   {
110     uint64_t op_latency = end_ts - begin_ts;
111     double op_per = static_cast<double>(op_latency) / graph_latency * 100.0;
112     writeMDTableRow(os, {name, backend, std::to_string(op_latency), std::to_string(op_per),
113                          std::to_string(min_rss), std::to_string(max_rss),
114                          std::to_string(min_page_reclaims), std::to_string(max_page_reclaims)});
115   }
116 };
117
118 struct Graph : public MDContent
119 {
120   std::set<Operation, Operation::OperationCmp> ops;
121   std::string session_index;
122   std::string subgraph_index;
123
124   void setOperations(const std::map<std::string, Operation> &name_to_op)
125   {
126     uint64_t graph_latency = end_ts - begin_ts;
127     for (auto &&it : name_to_op)
128     {
129       auto op = it.second;
130       op.graph_latency = graph_latency;
131
132       ops.insert(op);
133
134       updateRss(op.min_rss);
135       updateRss(op.max_rss);
136       updateMinflt(op.min_page_reclaims);
137       updateMinflt(op.max_page_reclaims);
138     }
139   }
140
141   void write(std::ostream &os) const override
142   {
143     static std::vector<std::string> graph_headers{"latency(us)", "rss_min(kb)", "rss_max(kb)",
144                                                   "page_reclaims_min", "page_reclaims_max"};
145
146     static std::vector<std::string> graph_headers_line{"-----------", "-------", "-------",
147                                                        "-----------------", "-----------------"};
148
149     // Graph's Header
150     writeMDTableRow(os, graph_headers);
151     writeMDTableRow(os, graph_headers_line);
152
153     // Graph's contents
154     writeMDTableRow(os, {std::to_string(end_ts - begin_ts), std::to_string(min_rss),
155                          std::to_string(max_rss), std::to_string(min_page_reclaims),
156                          std::to_string(max_page_reclaims)});
157
158     os << "\n";
159
160     static std::vector<std::string> op_headers{
161       "Op name",     "backend",     "latency(us)",       "latency(%)",
162       "rss_min(kb)", "rss_max(kb)", "page_reclaims_min", "page_reclaims_max"};
163
164     static std::vector<std::string> op_headers_line{
165       "-------", "-------", "-----------",       "-----------",
166       "-------", "-------", "-----------------", "-----------------"};
167
168     os << "## Op \n";
169
170     // Operation's Header
171     writeMDTableRow(os, op_headers);
172     writeMDTableRow(os, op_headers_line);
173
174     // Operation's contents
175     for (auto &&op : ops)
176     {
177       op.write(os);
178     }
179
180     os << "\n";
181   }
182 };
183
184 std::string getLabel(const OpSeqDurationEvent &evt)
185 {
186   std::string subg_label("$" + std::to_string(evt.subg_index) + " subgraph");
187   std::string op_label("@" + std::to_string(evt.op_index) + " " + evt.op_name);
188
189   return subg_label + " " + op_label;
190 }
191
192 struct MDTableBuilder
193 {
194   MDTableBuilder(const std::vector<std::unique_ptr<DurationEvent>> &duration_events,
195                  const std::vector<CounterEvent> &counter_events)
196     : _duration_events(duration_events), _counter_events(counter_events)
197   {
198 // when ready with low overhead in release build
199 #ifdef DEBUG
200     for (const auto &evt : _counter_events)
201     {
202       uint64_t ts = std::stoull(evt.ts);
203       auto &name = evt.name;
204       assert(name.compare("maxrss") == 0 || name.compare("minflt") == 0);
205       assert(evt.values.size() == 1);
206       auto &val = evt.values.begin()->second;
207       if (_ts_to_values.find(ts) == _ts_to_values.end())
208       {
209         std::pair<uint32_t, uint32_t> values;
210         if (name.compare("maxrss") == 0)
211           values.first = std::stoul(val);
212         else
213           values.second = std::stoul(val);
214         _ts_to_values.insert({ts, values});
215       }
216       else
217       {
218         auto &values = _ts_to_values.at(ts);
219         if (name.compare("maxrss") == 0)
220           values.first = std::stoul(val);
221         else
222           values.second = std::stoul(val);
223       }
224     }
225 #endif
226   }
227
228   MDTableBuilder &build()
229   {
230     for (const auto &it : divideGraph())
231     {
232       size_t begin_idx = it.first;
233       size_t end_idx = it.second;
234       std::map<std::string, Operation> name_to_op;
235       for (size_t i = begin_idx + 1; i < end_idx; ++i)
236       {
237         const auto *evt = dynamic_cast<const OpSeqDurationEvent *>(_duration_events[i].get());
238         if (evt == nullptr)
239           continue;
240
241         const std::string evt_name = getLabel(*evt);
242         assert(evt->ph.compare("B") == 0 || evt->ph.compare("E") == 0);
243         if (evt->ph.compare("B") == 0)
244         {
245           assert(name_to_op.find(evt_name) == name_to_op.end());
246           name_to_op.insert({evt_name, makeOperation(*evt)});
247         }
248         else
249         {
250           assert(name_to_op.find(evt_name) != name_to_op.end());
251           auto &op = name_to_op.at(evt_name);
252           updateOperation(op, *evt);
253         }
254       }
255
256       _graphs.emplace_back(makeGraph(begin_idx, end_idx, name_to_op));
257     }
258
259     return *this;
260   }
261
262   std::vector<std::pair<size_t, size_t>> divideGraph()
263   {
264     std::vector<std::pair<size_t, size_t>> graph_idx_list; // pair<begin_idx, end_idx>
265     for (size_t i = 0, begin_idx = 0; i < _duration_events.size(); ++i)
266     {
267       const auto subg_evt = dynamic_cast<const SubgDurationEvent *>(_duration_events.at(i).get());
268       if (subg_evt == nullptr)
269         continue;
270
271       if (subg_evt->ph.compare("B") == 0)
272         begin_idx = i;
273       else
274         graph_idx_list.emplace_back(begin_idx, i);
275     }
276     return graph_idx_list;
277   }
278
279   Operation makeOperation(const OpSeqDurationEvent &evt)
280   {
281     Operation op;
282     const std::string &evt_name = getLabel(evt);
283     op.name = evt_name;
284     op.begin_ts = std::stoull(evt.ts);
285     op.backend = evt.backend;
286 #ifdef DEBUG
287     op.updateRss(_ts_to_values.at(op.begin_ts).first);
288     op.updateMinflt(_ts_to_values.at(op.begin_ts).second);
289 #else
290     op.updateRss(0);
291     op.updateMinflt(0);
292 #endif
293     return op;
294   }
295
296   void updateOperation(Operation &op, const DurationEvent &evt)
297   {
298     op.end_ts = std::stoull(evt.ts);
299 #ifdef DEBUG
300     op.updateRss(_ts_to_values.at(op.end_ts).first);
301     op.updateMinflt(_ts_to_values.at(op.end_ts).second);
302 #else
303     op.updateRss(0);
304     op.updateMinflt(0);
305 #endif
306   }
307
308   Graph makeGraph(size_t begin_idx, size_t end_idx,
309                   const std::map<std::string, Operation> &name_to_op)
310   {
311     Graph graph;
312     graph.name = "Subgraph";
313     graph.begin_ts = std::stoull(_duration_events[begin_idx]->ts);
314     graph.end_ts = std::stoull(_duration_events[end_idx]->ts);
315     graph.setOperations(name_to_op);
316
317     for (const auto &arg : _duration_events[end_idx]->args)
318     {
319       if (arg.first == "session")
320         graph.session_index = arg.second;
321       if (arg.first == "subgraph")
322         graph.subgraph_index = arg.second;
323     }
324
325 #ifdef DEBUG
326     graph.updateRss(_ts_to_values.at(graph.begin_ts).first);
327     graph.updateMinflt(_ts_to_values.at(graph.begin_ts).second);
328     graph.updateRss(_ts_to_values.at(graph.end_ts).first);
329     graph.updateMinflt(_ts_to_values.at(graph.end_ts).second);
330 #else
331     graph.updateRss(0);
332     graph.updateMinflt(0);
333 #endif
334     return graph;
335   }
336
337   void write(std::ostream &os)
338   {
339     // Write contents
340     for (size_t i = 0; i < _graphs.size(); ++i)
341     {
342       auto &graph = _graphs.at(i);
343       os << "# Session: " << graph.session_index << ", Subgraph: " << graph.subgraph_index
344          << ", Running count: " << i << "\n";
345       _graphs.at(i).write(os);
346     }
347   }
348
349   const std::vector<std::unique_ptr<DurationEvent>> &_duration_events;
350   const std::vector<CounterEvent> &_counter_events;
351
352   // timestamp to std::pair<maxrss, minflt>
353   std::unordered_map<uint64_t, std::pair<uint32_t, uint32_t>> _ts_to_values;
354   std::vector<Graph> _graphs;
355 };
356
357 } // namespace
358
359 void MDTableWriter::flush(const std::vector<std::unique_ptr<EventRecorder>> &records)
360 {
361   for (const auto &recorder : records)
362   {
363     MDTableBuilder(recorder->duration_events(), recorder->counter_events()).build().write(_os);
364   }
365 }