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