You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

profile.cc 12 kB

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