• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright 2019 Huawei Technologies Co., Ltd
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 "utils/profile.h"
18 #ifdef _MSC_VER
19 #include <time.h>
20 #else
21 #include <sys/time.h>
22 #include <unistd.h>
23 #endif
24 #include <numeric>
25 #include <cstdio>
26 #include <sstream>
27 #include <iomanip>
28 #include <vector>
29 #include <list>
30 #include <utility>
31 #include <cfloat>
32 #include "utils/log_adapter.h"
33 
34 namespace mindspore {
35 namespace {
36 constexpr size_t TIME_INFO_PREFIX_NUM_LEN = 4;
37 const char KEY_PROF_TOTAL[] = "__total__";
38 
39 void PrintProfile(std::ostringstream &oss, const TimeInfo &time_info, int indent = 0,
40                   std::map<std::string, double> *sums = nullptr, const std::string &prefix = "");
41 
PrintTimeInfoMap(std::ostringstream & oss,const TimeInfoMap & dict,int indent=0,std::map<std::string,double> * sums=nullptr,const std::string & prefix="")42 void PrintTimeInfoMap(std::ostringstream &oss, const TimeInfoMap &dict, int indent = 0,
43                       std::map<std::string, double> *sums = nullptr, const std::string &prefix = "") {
44   size_t count = 0;
45   for (const auto &iter : dict) {
46     count++;
47     if (iter.second == nullptr) {
48       continue;
49     }
50     // indent by multiples of 4 spaces.
51     if (iter.first.size() < TIME_INFO_PREFIX_NUM_LEN) {
52       MS_LOG(EXCEPTION) << "In TimeInfoMap, the " << count << "th string key is " << iter.first
53                         << ", but the length is less than " << TIME_INFO_PREFIX_NUM_LEN;
54     }
55     auto name = iter.first.substr(TIME_INFO_PREFIX_NUM_LEN);
56     oss << std::setw(indent * 4) << ""
57         << "[" << name << "]: " << iter.second->time_;
58     if (iter.second->dict_ != nullptr) {
59       oss << ", [" << iter.second->dict_->size() << "]";
60     }
61     oss << "\n";
62 
63     std::string newPrefix = prefix;
64     if (iter.first.find("Cycle ") == std::string::npos) {
65       newPrefix = prefix.empty() ? iter.first : prefix + "." + iter.first;
66     }
67     PrintProfile(oss, *iter.second, indent + 1, sums, newPrefix);
68     if (iter.second->dict_ == nullptr) {
69       (*sums)[newPrefix] += iter.second->time_;
70     }
71   }
72 }
73 
PrintProfile(std::ostringstream & oss,const TimeInfo & time_info,int indent,std::map<std::string,double> * sums,const std::string & prefix)74 void PrintProfile(std::ostringstream &oss, const TimeInfo &time_info, int indent, std::map<std::string, double> *sums,
75                   const std::string &prefix) {
76   bool need_free = false;
77   if (sums == nullptr) {
78     sums = new (std::nothrow) std::map<std::string, double>();
79     if (sums == nullptr) {
80       MS_LOG(ERROR) << "memory allocation failed";
81       return;
82     }
83     need_free = true;
84   }
85 
86   // indent by multiples of 4 spaces.
87   if (indent == 0) {
88     oss << "TotalTime = " << time_info.time_;
89     if (time_info.dict_ != nullptr) {
90       oss << ", [" << time_info.dict_->size() << "]";
91     }
92     oss << "\n";
93   }
94 
95   if (time_info.dict_ != nullptr) {
96     PrintTimeInfoMap(oss, *time_info.dict_, indent, sums, prefix);
97   }
98 
99   // print time percentage info
100   if (need_free) {
101     double total = 0.0;
102     for (auto iter = sums->begin(); iter != sums->end(); ++iter) {
103       total += iter->second;
104     }
105     oss << "Sums\n";
106     if (total >= 0.0 + DBL_EPSILON) {
107       for (auto &iter : *sums) {
108         std::string name = iter.first;
109         name.erase(0, TIME_INFO_PREFIX_NUM_LEN);
110         std::size_t pos = 0;
111         while ((pos = name.find('.', pos)) != std::string::npos) {
112           pos++;
113           name.erase(pos, TIME_INFO_PREFIX_NUM_LEN);
114         }
115         oss << "    " << std::left << std::setw(36) << name << " : " << std::right << std::setw(12) << std::fixed
116             << std::setprecision(6) << iter.second << "s : " << std::right << std::setw(5) << std::fixed
117             << std::setprecision(2) << (iter.second / total) * 100 << "%\n";
118       }
119     }
120     delete sums;
121   }
122 }
123 }  // namespace
124 
GetTime(void)125 double GetTime(void) {
126 #ifdef _MSC_VER
127   return time(NULL);
128 #else
129   struct timeval tv = {0, 0};
130   (void)gettimeofday(&tv, nullptr);
131   return tv.tv_sec + tv.tv_usec * 1.0e-6;
132 #endif
133 }
134 
~TimeInfo()135 TimeInfo::~TimeInfo() {
136   if (dict_ == nullptr) {
137     return;
138   }
139   for (auto iter = dict_->begin(); iter != dict_->end(); ++iter) {
140     delete iter->second;
141     iter->second = nullptr;
142   }
143   delete dict_;
144   dict_ = nullptr;
145 }
146 
ProfileBase()147 ProfileBase::ProfileBase() : context_("", this) {
148   ctx_ptr_ = &context_;
149   context_.parent_ = nullptr;
150 }
151 
~ProfileBase()152 ProfileBase::~ProfileBase() {
153   context_.parent_ = nullptr;
154   if (context_.time_info_ != nullptr) {
155     delete context_.time_info_;
156     context_.time_info_ = nullptr;
157   }
158   ctx_ptr_ = nullptr;
159 }
160 
Print(void)161 void Profile::Print(void) {
162   if (ctx_ptr_ == nullptr || ctx_ptr_->time_info_ == nullptr) {
163     return;
164   }
165   std::ostringstream oss;
166   PrintProfile(oss, *ctx_ptr_->time_info_);
167   std::string text = oss.str();
168   // here use printf to output profile info, not use MS_LOG(INFO) since when open log, it affects performance
169   (void)printf("%s", text.c_str());
170   (void)fflush(stdout);
171 }
172 
173 // Start a step in the current context with the given name.
174 // Nomes must be unique otherwise the previous record will be overwritten.
Step(const std::string & name)175 ProfContext *Profile::Step(const std::string &name) {
176   ctx_ptr_ = new (std::nothrow) ProfContext(name, this);
177   if (ctx_ptr_ == nullptr) {
178     MS_LOG(ERROR) << "memory allocation failed";
179     return nullptr;
180   }
181   return ctx_ptr_;
182 }
183 
184 // Creates subcontext for a repeated action.
185 // Count should be monotonically increasing.
Lap(int count)186 ProfContext *Profile::Lap(int count) {
187   std::ostringstream oss;
188   oss << "Cycle " << count;
189   ctx_ptr_ = new (std::nothrow) ProfContext(oss.str(), this);
190   if (ctx_ptr_ == nullptr) {
191     MS_LOG(ERROR) << "memory allocation failed";
192     return nullptr;
193   }
194   return ctx_ptr_;
195 }
196 
Pop(void)197 void Profile::Pop(void) noexcept {
198   if (ctx_ptr_ == nullptr) {
199     return;
200   }
201   ctx_ptr_ = ctx_ptr_->parent_;
202 }
203 
ProfContext(const std::string & name,ProfileBase * const prof)204 ProfContext::ProfContext(const std::string &name, ProfileBase *const prof) : name_(name), prof_(prof) {
205   // Initialize a subcontext.
206   time_info_ = nullptr;
207   if (prof == nullptr || IsTopContext()) {
208     parent_ = nullptr;
209   } else {
210     parent_ = prof->ctx_ptr_;
211   }
212 }
213 
~ProfContext()214 ProfContext::~ProfContext() {
215   // top level context
216   if (parent_ == nullptr || IsTopContext()) {
217     if (time_info_ != nullptr) {
218       delete time_info_;
219     }
220   } else {
221     parent_->Insert(name_, time_info_);
222     if (prof_ != nullptr) {
223       prof_->Pop();
224     }
225   }
226 
227   time_info_ = nullptr;
228   prof_ = nullptr;
229   parent_ = nullptr;
230 }
231 
SetTime(double time)232 void ProfContext::SetTime(double time) noexcept {
233   if (time_info_ == nullptr) {
234     time_info_ = new (std::nothrow) TimeInfo(time);
235     if (time_info_ == nullptr) {
236       MS_LOG(ERROR) << "memory allocation failed";
237       return;
238     }
239   }
240   time_info_->time_ = time;
241 }
242 
Insert(const std::string & name,const TimeInfo * time)243 void ProfContext::Insert(const std::string &name, const TimeInfo *time) noexcept {
244   if (time_info_ == nullptr) {
245     time_info_ = new (std::nothrow) TimeInfo();
246     if (time_info_ == nullptr) {
247       MS_LOG(ERROR) << "memory allocation failed";
248       delete time;
249       time = nullptr;
250       return;
251     }
252   }
253 
254   if (time_info_->dict_ == nullptr) {
255     time_info_->dict_ = new (std::nothrow) TimeInfoMap();
256     if (time_info_->dict_ == nullptr) {
257       MS_LOG(ERROR) << "memory allocation failed";
258       delete time;
259       time = nullptr;
260       delete time_info_;
261       time_info_ = nullptr;
262       return;
263     }
264   }
265 
266   std::stringstream ss;
267   ss << std::setw(TIME_INFO_PREFIX_NUM_LEN) << std::setfill('0') << time_info_->actionNum_;
268   std::string sorted_name(ss.str() + name);
269   time_info_->actionNum_++;
270   auto iter = time_info_->dict_->find(sorted_name);
271   // if contains item with same name, delete it
272   if (iter != time_info_->dict_->end()) {
273     delete iter->second;
274     iter->second = nullptr;
275     (void)time_info_->dict_->erase(iter);
276   }
277   (*time_info_->dict_)[sorted_name] = time;
278 }
279 
IsTopContext() const280 bool ProfContext::IsTopContext() const noexcept { return (prof_ != nullptr) && (this == &prof_->context_); }
281 
ProfTransaction(const ProfileBase * prof)282 ProfTransaction::ProfTransaction(const ProfileBase *prof) { ctx_ = (prof != nullptr ? prof->ctx_ptr_ : nullptr); }
283 
~ProfTransaction()284 ProfTransaction::~ProfTransaction() {
285   if (ctx_ != nullptr && !ctx_->IsTopContext()) {
286     delete ctx_;
287   }
288   ctx_ = nullptr;
289 }
290 
Record(const std::string & step_name,const double time,const bool is_start)291 void DumpTime::Record(const std::string &step_name, const double time, const bool is_start) {
292   file_ss_ << "    {" << std::endl;
293   file_ss_ << "        \"name\": "
294            << "\"" << step_name << "\"," << std::endl;
295   file_ss_ << "        \"cat\": "
296            << "\"FUNCTION\"," << std::endl;
297   if (is_start) {
298     file_ss_ << "        \"ph\": "
299              << "\"B\"," << std::endl;
300   } else {
301     file_ss_ << "        \"ph\": "
302              << "\"E\"," << std::endl;
303   }
304   file_ss_ << "        \"ts\": " << std::setprecision(16) << time * 1000000 << "," << std::endl;
305   file_ss_ << "        \"pid\": "
306            << "1" << std::endl;
307   file_ss_ << "    }" << std::endl;
308   file_ss_ << "    ," << std::endl;
309 }
310 
Save()311 void DumpTime::Save() {
312   try {
313     file_out_.open(file_path_, std::ios::trunc | std::ios::out);
314   } catch (const std::exception &e) {
315     MS_LOG(EXCEPTION) << "Cannot open file in " << (file_path_);
316   }
317   file_out_ << "{\n";
318   file_out_ << "    \"traceEvents\": [" << std::endl;
319   file_ss_ >> file_out_.rdbuf();
320   constexpr int offset = -7;
321   (void)file_out_.seekp(offset, std::ios::end);
322   file_out_ << "    ]" << std::endl << "    ,\n";
323   file_out_ << "    \"displayTimeUnit\": \"ms\"" << std::endl;
324   file_out_ << "}";
325   file_out_.close();
326 }
327 
328 struct TimeInfoGroup {
329   double total_time = 0.0;
330   int total_count = 0;
331   std::list<std::map<std::string, TimeStat>::const_iterator> items;
332 };
333 
PrintTimeStat(std::ostringstream & oss,const TimeInfoGroup & group,const std::string & prefix)334 static void PrintTimeStat(std::ostringstream &oss, const TimeInfoGroup &group, const std::string &prefix) {
335   oss << "------[" << prefix << "] " << std::setw(10) << std::fixed << std::setprecision(6) << group.total_time
336       << std::setw(6) << group.total_count << "\n";
337   for (const auto &iter : group.items) {
338     oss << std::setw(5) << std::fixed << std::setprecision(2) << iter->second.time_ / group.total_time * 100
339         << "% : " << std::setw(12) << std::fixed << std::setprecision(6) << iter->second.time_ << "s : " << std::setw(6)
340         << iter->second.count_ << ": " << iter->first << "\n";
341   }
342 }
343 
Print()344 void MsProfile::Print() {
345   GetProfile()->Print();
346   std::vector<std::string> items = {"substitution.",          "renormalize.", "replace.", "match.",
347                                     "func_graph_cloner_run.", "meta_graph.",  "manager.", "pynative"};
348   std::vector<TimeInfoGroup> groups(items.size() + 1);
349   const auto &stat = GetSingleton().time_stat_;
350   // group all time infos
351   for (auto iter = stat.cbegin(); iter != stat.cend(); ++iter) {
352     auto matched_idx = items.size();
353     for (size_t i = 0; i < items.size(); ++i) {
354       if (iter->first.find(items[i]) != std::string::npos) {
355         matched_idx = i;
356         break;
357       }
358     }
359     groups[matched_idx].total_time += iter->second.time_;
360     groups[matched_idx].total_count += iter->second.count_;
361     groups[matched_idx].items.push_back(iter);
362   }
363   std::ostringstream oss;
364   for (size_t i = 0; i < groups.size(); ++i) {
365     std::string prefix = (i < items.size() ? items[i] : std::string("others."));
366     PrintTimeStat(oss, groups[i], prefix);
367   }
368   std::string text = oss.str();
369   // here use printf to output profile info, not use MS_LOG(INFO) since when open log, it affects performance
370   (void)printf("\nTime group info:\n%s", text.c_str());
371   (void)fflush(stdout);
372 }
373 }  // namespace mindspore
374