| /* |
| * Copyright 2013 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/base/profiler.h" |
| |
| #include <math.h> |
| #include <algorithm> |
| |
| #include "webrtc/base/timeutils.h" |
| |
| namespace { |
| |
| // When written to an ostream, FormattedTime chooses an appropriate scale and |
| // suffix for a time value given in seconds. |
| class FormattedTime { |
| public: |
| explicit FormattedTime(double t) : time_(t) {} |
| double time() const { return time_; } |
| private: |
| double time_; |
| }; |
| |
| std::ostream& operator<<(std::ostream& stream, const FormattedTime& time) { |
| if (time.time() < 1.0) { |
| stream << (time.time() * 1000.0) << "ms"; |
| } else { |
| stream << time.time() << 's'; |
| } |
| return stream; |
| } |
| |
| } // namespace |
| |
| namespace rtc { |
| |
| ProfilerEvent::ProfilerEvent() |
| : total_time_(0.0), |
| mean_(0.0), |
| sum_of_squared_differences_(0.0), |
| start_count_(0), |
| event_count_(0) { |
| } |
| |
| void ProfilerEvent::Start() { |
| if (start_count_ == 0) { |
| current_start_time_ = TimeNanos(); |
| } |
| ++start_count_; |
| } |
| |
| void ProfilerEvent::Stop(uint64 stop_time) { |
| --start_count_; |
| ASSERT(start_count_ >= 0); |
| if (start_count_ == 0) { |
| double elapsed = static_cast<double>(stop_time - current_start_time_) / |
| kNumNanosecsPerSec; |
| total_time_ += elapsed; |
| if (event_count_ == 0) { |
| minimum_ = maximum_ = elapsed; |
| } else { |
| minimum_ = std::min(minimum_, elapsed); |
| maximum_ = std::max(maximum_, elapsed); |
| } |
| // Online variance and mean algorithm: http://en.wikipedia.org/wiki/ |
| // Algorithms_for_calculating_variance#Online_algorithm |
| ++event_count_; |
| double delta = elapsed - mean_; |
| mean_ = mean_ + delta / event_count_; |
| sum_of_squared_differences_ += delta * (elapsed - mean_); |
| } |
| } |
| |
| void ProfilerEvent::Stop() { |
| Stop(TimeNanos()); |
| } |
| |
| double ProfilerEvent::standard_deviation() const { |
| if (event_count_ <= 1) return 0.0; |
| return sqrt(sum_of_squared_differences_ / (event_count_ - 1.0)); |
| } |
| |
| Profiler::~Profiler() = default; |
| |
| Profiler* Profiler::Instance() { |
| LIBJINGLE_DEFINE_STATIC_LOCAL(Profiler, instance, ()); |
| return &instance; |
| } |
| |
| Profiler::Profiler() { |
| } |
| |
| void Profiler::StartEvent(const std::string& event_name) { |
| lock_.LockShared(); |
| EventMap::iterator it = events_.find(event_name); |
| bool needs_insert = (it == events_.end()); |
| lock_.UnlockShared(); |
| |
| if (needs_insert) { |
| // Need an exclusive lock to modify the map. |
| ExclusiveScope scope(&lock_); |
| it = events_.insert( |
| EventMap::value_type(event_name, ProfilerEvent())).first; |
| } |
| |
| it->second.Start(); |
| } |
| |
| void Profiler::StopEvent(const std::string& event_name) { |
| // Get the time ASAP, then wait for the lock. |
| uint64 stop_time = TimeNanos(); |
| SharedScope scope(&lock_); |
| EventMap::iterator it = events_.find(event_name); |
| if (it != events_.end()) { |
| it->second.Stop(stop_time); |
| } |
| } |
| |
| void Profiler::ReportToLog(const char* file, int line, |
| LoggingSeverity severity_to_use, |
| const std::string& event_prefix) { |
| if (!LogMessage::Loggable(severity_to_use)) { |
| return; |
| } |
| |
| SharedScope scope(&lock_); |
| |
| { // Output first line. |
| LogMessage msg(file, line, severity_to_use); |
| msg.stream() << "=== Profile report "; |
| if (event_prefix.empty()) { |
| msg.stream() << "(prefix: '" << event_prefix << "') "; |
| } |
| msg.stream() << "==="; |
| } |
| for (EventMap::const_iterator it = events_.begin(); |
| it != events_.end(); ++it) { |
| if (event_prefix.empty() || it->first.find(event_prefix) == 0) { |
| LogMessage(file, line, severity_to_use).stream() |
| << it->first << " " << it->second; |
| } |
| } |
| LogMessage(file, line, severity_to_use).stream() |
| << "=== End profile report ==="; |
| } |
| |
| void Profiler::ReportAllToLog(const char* file, int line, |
| LoggingSeverity severity_to_use) { |
| ReportToLog(file, line, severity_to_use, ""); |
| } |
| |
| const ProfilerEvent* Profiler::GetEvent(const std::string& event_name) const { |
| SharedScope scope(&lock_); |
| EventMap::const_iterator it = |
| events_.find(event_name); |
| return (it == events_.end()) ? NULL : &it->second; |
| } |
| |
| bool Profiler::Clear() { |
| ExclusiveScope scope(&lock_); |
| bool result = true; |
| // Clear all events that aren't started. |
| EventMap::iterator it = events_.begin(); |
| while (it != events_.end()) { |
| if (it->second.is_started()) { |
| ++it; // Can't clear started events. |
| result = false; |
| } else { |
| events_.erase(it++); |
| } |
| } |
| return result; |
| } |
| |
| std::ostream& operator<<(std::ostream& stream, |
| const ProfilerEvent& profiler_event) { |
| stream << "count=" << profiler_event.event_count() |
| << " total=" << FormattedTime(profiler_event.total_time()) |
| << " mean=" << FormattedTime(profiler_event.mean()) |
| << " min=" << FormattedTime(profiler_event.minimum()) |
| << " max=" << FormattedTime(profiler_event.maximum()) |
| << " sd=" << profiler_event.standard_deviation(); |
| return stream; |
| } |
| |
| } // namespace rtc |