123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- /*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- #include <time.h>
- #include <algorithm>
- #include <chrono>
- #include <mutex>
- #include <sstream>
- #include <vector>
- #include <glog/logging.h>
- #include "MicroProfiler.h"
- // iOS doesn't support 'thread_local'. If we reimplement this to use
- // pthread_setspecific we can get rid of this
- #if defined(__APPLE__)
- #define MICRO_PROFILER_STUB_IMPLEMENTATION 1
- #elif !defined(MICRO_PROFILER_STUB_IMPLEMENTATION)
- #define MICRO_PROFILER_STUB_IMPLEMENTATION 0
- #endif
- namespace facebook {
- namespace react {
- #if !MICRO_PROFILER_STUB_IMPLEMENTATION
- struct TraceData {
- TraceData();
- ~TraceData();
- void addTime(
- MicroProfilerName name,
- uint_fast64_t time,
- uint_fast32_t internalClockCalls);
- std::thread::id threadId_;
- uint_fast64_t startTime_;
- std::atomic_uint_fast64_t times_[MicroProfilerName::__LENGTH__] = {};
- std::atomic_uint_fast32_t calls_[MicroProfilerName::__LENGTH__] = {};
- std::atomic_uint_fast32_t
- childProfileSections_[MicroProfilerName::__LENGTH__] = {};
- };
- struct ProfilingImpl {
- std::mutex mutex_;
- std::vector<TraceData *> allTraceData_;
- bool isProfiling_ = false;
- uint_fast64_t startTime_;
- uint_fast64_t endTime_;
- uint_fast64_t clockOverhead_;
- uint_fast64_t profileSectionOverhead_;
- };
- static ProfilingImpl profiling;
- thread_local TraceData myTraceData;
- thread_local uint_fast32_t profileSections = 0;
- static uint_fast64_t nowNs() {
- struct timespec time;
- clock_gettime(CLOCK_REALTIME, &time);
- return uint_fast64_t(1000000000) * time.tv_sec + time.tv_nsec;
- }
- static uint_fast64_t diffNs(uint_fast64_t start, uint_fast64_t end) {
- return end - start;
- }
- static std::string formatTimeNs(uint_fast64_t timeNs) {
- std::ostringstream out;
- out.precision(2);
- if (timeNs < 1000) {
- out << timeNs << "ns";
- } else if (timeNs < 1000000) {
- out << timeNs / 1000.0 << "us";
- } else {
- out << std::fixed << timeNs / 1000000.0 << "ms";
- }
- return out.str();
- }
- MicroProfilerSection::MicroProfilerSection(MicroProfilerName name)
- : isProfiling_(profiling.isProfiling_),
- name_(name),
- startNumProfileSections_(profileSections) {
- if (!isProfiling_) {
- return;
- }
- profileSections++;
- startTime_ = nowNs();
- }
- MicroProfilerSection::~MicroProfilerSection() {
- if (!isProfiling_ || !profiling.isProfiling_) {
- return;
- }
- auto endTime = nowNs();
- auto endNumProfileSections = profileSections;
- myTraceData.addTime(
- name_,
- endTime - startTime_,
- endNumProfileSections - startNumProfileSections_ - 1);
- }
- TraceData::TraceData() : threadId_(std::this_thread::get_id()) {
- std::lock_guard<std::mutex> lock(profiling.mutex_);
- profiling.allTraceData_.push_back(this);
- }
- TraceData::~TraceData() {
- std::lock_guard<std::mutex> lock(profiling.mutex_);
- auto &infos = profiling.allTraceData_;
- infos.erase(std::remove(infos.begin(), infos.end(), this), infos.end());
- }
- void TraceData::addTime(
- MicroProfilerName name,
- uint_fast64_t time,
- uint_fast32_t childprofileSections) {
- times_[name] += time;
- calls_[name]++;
- childProfileSections_[name] += childprofileSections;
- }
- static void printReport() {
- LOG(ERROR) << "======= MICRO PROFILER REPORT =======";
- LOG(ERROR) << "- Total Time: "
- << formatTimeNs(diffNs(profiling.startTime_, profiling.endTime_));
- LOG(ERROR) << "- Clock Overhead: " << formatTimeNs(profiling.clockOverhead_);
- LOG(ERROR) << "- Profiler Section Overhead: "
- << formatTimeNs(profiling.profileSectionOverhead_);
- for (auto info : profiling.allTraceData_) {
- LOG(ERROR) << "--- Thread ID 0x" << std::hex << info->threadId_ << " ---";
- for (int i = 0; i < MicroProfilerName::__LENGTH__; i++) {
- if (info->times_[i] > 0) {
- auto totalTime = info->times_[i].load();
- auto calls = info->calls_[i].load();
- auto clockOverhead = profiling.clockOverhead_ * calls +
- profiling.profileSectionOverhead_ *
- info->childProfileSections_[i].load();
- if (totalTime < clockOverhead) {
- LOG(ERROR) << "- "
- << MicroProfiler::profilingNameToString(
- static_cast<MicroProfilerName>(i))
- << ": "
- << "ERROR: Total time was " << totalTime
- << "ns but clock overhead was calculated to be "
- << clockOverhead << "ns!";
- } else {
- auto correctedTime = totalTime - clockOverhead;
- auto timePerCall = correctedTime / calls;
- LOG(ERROR) << "- "
- << MicroProfiler::profilingNameToString(
- static_cast<MicroProfilerName>(i))
- << ": " << formatTimeNs(correctedTime) << " (" << calls
- << " calls, " << formatTimeNs(timePerCall) << "/call)";
- }
- }
- }
- }
- }
- static void clearProfiling() {
- CHECK(!profiling.isProfiling_)
- << "Trying to clear profiling but profiling was already started!";
- for (auto info : profiling.allTraceData_) {
- for (unsigned int i = 0; i < MicroProfilerName::__LENGTH__; i++) {
- info->times_[i] = 0;
- info->calls_[i] = 0;
- info->childProfileSections_[i] = 0;
- }
- }
- }
- static uint_fast64_t calculateClockOverhead() {
- int numCalls = 1000000;
- uint_fast64_t start = nowNs();
- for (int i = 0; i < numCalls; i++) {
- nowNs();
- }
- uint_fast64_t end = nowNs();
- return (end - start) / numCalls;
- }
- static uint_fast64_t calculateProfileSectionOverhead() {
- int numCalls = 1000000;
- uint_fast64_t start = nowNs();
- profiling.isProfiling_ = true;
- for (int i = 0; i < numCalls; i++) {
- MICRO_PROFILER_SECTION(static_cast<MicroProfilerName>(0));
- }
- uint_fast64_t end = nowNs();
- profiling.isProfiling_ = false;
- return (end - start) / numCalls;
- }
- void MicroProfiler::startProfiling() {
- CHECK(!profiling.isProfiling_)
- << "Trying to start profiling but profiling was already started!";
- profiling.clockOverhead_ = calculateClockOverhead();
- profiling.profileSectionOverhead_ = calculateProfileSectionOverhead();
- std::lock_guard<std::mutex> lock(profiling.mutex_);
- clearProfiling();
- profiling.startTime_ = nowNs();
- profiling.isProfiling_ = true;
- }
- void MicroProfiler::stopProfiling() {
- CHECK(profiling.isProfiling_)
- << "Trying to stop profiling but profiling hasn't been started!";
- profiling.isProfiling_ = false;
- profiling.endTime_ = nowNs();
- std::lock_guard<std::mutex> lock(profiling.mutex_);
- printReport();
- clearProfiling();
- }
- bool MicroProfiler::isProfiling() {
- return profiling.isProfiling_;
- }
- void MicroProfiler::runInternalBenchmark() {
- MicroProfiler::startProfiling();
- for (int i = 0; i < 1000000; i++) {
- MICRO_PROFILER_SECTION_NAMED(outer, __INTERNAL_BENCHMARK_OUTER);
- { MICRO_PROFILER_SECTION_NAMED(inner, __INTERNAL_BENCHMARK_INNER); }
- }
- MicroProfiler::stopProfiling();
- }
- #else
- void MicroProfiler::startProfiling() {
- CHECK(false)
- << "This platform has a stub implementation of the micro profiler and cannot collect traces";
- }
- void MicroProfiler::stopProfiling() {}
- bool MicroProfiler::isProfiling() {
- return false;
- }
- void MicroProfiler::runInternalBenchmark() {}
- #endif
- } // namespace react
- } // namespace facebook
|