diff options
Diffstat (limited to 'media/webrtc/trunk/webrtc/base/profiler.cc')
-rw-r--r-- | media/webrtc/trunk/webrtc/base/profiler.cc | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/media/webrtc/trunk/webrtc/base/profiler.cc b/media/webrtc/trunk/webrtc/base/profiler.cc new file mode 100644 index 000000000..e0bd431cf --- /dev/null +++ b/media/webrtc/trunk/webrtc/base/profiler.cc @@ -0,0 +1,192 @@ +/* + * 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 |