MicroProfiler.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #include <time.h>
  8. #include <algorithm>
  9. #include <chrono>
  10. #include <mutex>
  11. #include <sstream>
  12. #include <vector>
  13. #include <glog/logging.h>
  14. #include "MicroProfiler.h"
  15. // iOS doesn't support 'thread_local'. If we reimplement this to use
  16. // pthread_setspecific we can get rid of this
  17. #if defined(__APPLE__)
  18. #define MICRO_PROFILER_STUB_IMPLEMENTATION 1
  19. #elif !defined(MICRO_PROFILER_STUB_IMPLEMENTATION)
  20. #define MICRO_PROFILER_STUB_IMPLEMENTATION 0
  21. #endif
  22. namespace facebook {
  23. namespace react {
  24. #if !MICRO_PROFILER_STUB_IMPLEMENTATION
  25. struct TraceData {
  26. TraceData();
  27. ~TraceData();
  28. void addTime(
  29. MicroProfilerName name,
  30. uint_fast64_t time,
  31. uint_fast32_t internalClockCalls);
  32. std::thread::id threadId_;
  33. uint_fast64_t startTime_;
  34. std::atomic_uint_fast64_t times_[MicroProfilerName::__LENGTH__] = {};
  35. std::atomic_uint_fast32_t calls_[MicroProfilerName::__LENGTH__] = {};
  36. std::atomic_uint_fast32_t
  37. childProfileSections_[MicroProfilerName::__LENGTH__] = {};
  38. };
  39. struct ProfilingImpl {
  40. std::mutex mutex_;
  41. std::vector<TraceData *> allTraceData_;
  42. bool isProfiling_ = false;
  43. uint_fast64_t startTime_;
  44. uint_fast64_t endTime_;
  45. uint_fast64_t clockOverhead_;
  46. uint_fast64_t profileSectionOverhead_;
  47. };
  48. static ProfilingImpl profiling;
  49. thread_local TraceData myTraceData;
  50. thread_local uint_fast32_t profileSections = 0;
  51. static uint_fast64_t nowNs() {
  52. struct timespec time;
  53. clock_gettime(CLOCK_REALTIME, &time);
  54. return uint_fast64_t(1000000000) * time.tv_sec + time.tv_nsec;
  55. }
  56. static uint_fast64_t diffNs(uint_fast64_t start, uint_fast64_t end) {
  57. return end - start;
  58. }
  59. static std::string formatTimeNs(uint_fast64_t timeNs) {
  60. std::ostringstream out;
  61. out.precision(2);
  62. if (timeNs < 1000) {
  63. out << timeNs << "ns";
  64. } else if (timeNs < 1000000) {
  65. out << timeNs / 1000.0 << "us";
  66. } else {
  67. out << std::fixed << timeNs / 1000000.0 << "ms";
  68. }
  69. return out.str();
  70. }
  71. MicroProfilerSection::MicroProfilerSection(MicroProfilerName name)
  72. : isProfiling_(profiling.isProfiling_),
  73. name_(name),
  74. startNumProfileSections_(profileSections) {
  75. if (!isProfiling_) {
  76. return;
  77. }
  78. profileSections++;
  79. startTime_ = nowNs();
  80. }
  81. MicroProfilerSection::~MicroProfilerSection() {
  82. if (!isProfiling_ || !profiling.isProfiling_) {
  83. return;
  84. }
  85. auto endTime = nowNs();
  86. auto endNumProfileSections = profileSections;
  87. myTraceData.addTime(
  88. name_,
  89. endTime - startTime_,
  90. endNumProfileSections - startNumProfileSections_ - 1);
  91. }
  92. TraceData::TraceData() : threadId_(std::this_thread::get_id()) {
  93. std::lock_guard<std::mutex> lock(profiling.mutex_);
  94. profiling.allTraceData_.push_back(this);
  95. }
  96. TraceData::~TraceData() {
  97. std::lock_guard<std::mutex> lock(profiling.mutex_);
  98. auto &infos = profiling.allTraceData_;
  99. infos.erase(std::remove(infos.begin(), infos.end(), this), infos.end());
  100. }
  101. void TraceData::addTime(
  102. MicroProfilerName name,
  103. uint_fast64_t time,
  104. uint_fast32_t childprofileSections) {
  105. times_[name] += time;
  106. calls_[name]++;
  107. childProfileSections_[name] += childprofileSections;
  108. }
  109. static void printReport() {
  110. LOG(ERROR) << "======= MICRO PROFILER REPORT =======";
  111. LOG(ERROR) << "- Total Time: "
  112. << formatTimeNs(diffNs(profiling.startTime_, profiling.endTime_));
  113. LOG(ERROR) << "- Clock Overhead: " << formatTimeNs(profiling.clockOverhead_);
  114. LOG(ERROR) << "- Profiler Section Overhead: "
  115. << formatTimeNs(profiling.profileSectionOverhead_);
  116. for (auto info : profiling.allTraceData_) {
  117. LOG(ERROR) << "--- Thread ID 0x" << std::hex << info->threadId_ << " ---";
  118. for (int i = 0; i < MicroProfilerName::__LENGTH__; i++) {
  119. if (info->times_[i] > 0) {
  120. auto totalTime = info->times_[i].load();
  121. auto calls = info->calls_[i].load();
  122. auto clockOverhead = profiling.clockOverhead_ * calls +
  123. profiling.profileSectionOverhead_ *
  124. info->childProfileSections_[i].load();
  125. if (totalTime < clockOverhead) {
  126. LOG(ERROR) << "- "
  127. << MicroProfiler::profilingNameToString(
  128. static_cast<MicroProfilerName>(i))
  129. << ": "
  130. << "ERROR: Total time was " << totalTime
  131. << "ns but clock overhead was calculated to be "
  132. << clockOverhead << "ns!";
  133. } else {
  134. auto correctedTime = totalTime - clockOverhead;
  135. auto timePerCall = correctedTime / calls;
  136. LOG(ERROR) << "- "
  137. << MicroProfiler::profilingNameToString(
  138. static_cast<MicroProfilerName>(i))
  139. << ": " << formatTimeNs(correctedTime) << " (" << calls
  140. << " calls, " << formatTimeNs(timePerCall) << "/call)";
  141. }
  142. }
  143. }
  144. }
  145. }
  146. static void clearProfiling() {
  147. CHECK(!profiling.isProfiling_)
  148. << "Trying to clear profiling but profiling was already started!";
  149. for (auto info : profiling.allTraceData_) {
  150. for (unsigned int i = 0; i < MicroProfilerName::__LENGTH__; i++) {
  151. info->times_[i] = 0;
  152. info->calls_[i] = 0;
  153. info->childProfileSections_[i] = 0;
  154. }
  155. }
  156. }
  157. static uint_fast64_t calculateClockOverhead() {
  158. int numCalls = 1000000;
  159. uint_fast64_t start = nowNs();
  160. for (int i = 0; i < numCalls; i++) {
  161. nowNs();
  162. }
  163. uint_fast64_t end = nowNs();
  164. return (end - start) / numCalls;
  165. }
  166. static uint_fast64_t calculateProfileSectionOverhead() {
  167. int numCalls = 1000000;
  168. uint_fast64_t start = nowNs();
  169. profiling.isProfiling_ = true;
  170. for (int i = 0; i < numCalls; i++) {
  171. MICRO_PROFILER_SECTION(static_cast<MicroProfilerName>(0));
  172. }
  173. uint_fast64_t end = nowNs();
  174. profiling.isProfiling_ = false;
  175. return (end - start) / numCalls;
  176. }
  177. void MicroProfiler::startProfiling() {
  178. CHECK(!profiling.isProfiling_)
  179. << "Trying to start profiling but profiling was already started!";
  180. profiling.clockOverhead_ = calculateClockOverhead();
  181. profiling.profileSectionOverhead_ = calculateProfileSectionOverhead();
  182. std::lock_guard<std::mutex> lock(profiling.mutex_);
  183. clearProfiling();
  184. profiling.startTime_ = nowNs();
  185. profiling.isProfiling_ = true;
  186. }
  187. void MicroProfiler::stopProfiling() {
  188. CHECK(profiling.isProfiling_)
  189. << "Trying to stop profiling but profiling hasn't been started!";
  190. profiling.isProfiling_ = false;
  191. profiling.endTime_ = nowNs();
  192. std::lock_guard<std::mutex> lock(profiling.mutex_);
  193. printReport();
  194. clearProfiling();
  195. }
  196. bool MicroProfiler::isProfiling() {
  197. return profiling.isProfiling_;
  198. }
  199. void MicroProfiler::runInternalBenchmark() {
  200. MicroProfiler::startProfiling();
  201. for (int i = 0; i < 1000000; i++) {
  202. MICRO_PROFILER_SECTION_NAMED(outer, __INTERNAL_BENCHMARK_OUTER);
  203. { MICRO_PROFILER_SECTION_NAMED(inner, __INTERNAL_BENCHMARK_INNER); }
  204. }
  205. MicroProfiler::stopProfiling();
  206. }
  207. #else
  208. void MicroProfiler::startProfiling() {
  209. CHECK(false)
  210. << "This platform has a stub implementation of the micro profiler and cannot collect traces";
  211. }
  212. void MicroProfiler::stopProfiling() {}
  213. bool MicroProfiler::isProfiling() {
  214. return false;
  215. }
  216. void MicroProfiler::runInternalBenchmark() {}
  217. #endif
  218. } // namespace react
  219. } // namespace facebook