// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_BASE_BANDWIDTH_METRICS_H_
#define NET_BASE_BANDWIDTH_METRICS_H_
#include <list>
#include "base/histogram.h"
#include "base/logging.h"
#include "base/time.h"
namespace net {
// Tracks statistics about the bandwidth metrics over time. In order to
// measure, this class needs to know when individual streams are in progress,
// so that it can know when to discount idle time. The BandwidthMetrics
// is unidirectional - it should only be used to record upload or download
// bandwidth, but not both.
//
// Note, the easiest thing to do is to just measure each stream and average
// them or add them. However, this does not work. If multiple streams are in
// progress concurrently, you have to look at the aggregate bandwidth at any
// point in time.
//
// Example:
// Imagine 4 streams opening and closing with overlapping time.
// We can't measure bandwidth by looking at any individual stream.
// We can only measure actual bandwidth by looking at the bandwidth
// across all open streams.
//
// Time --------------------------------------->
// s1 +----------------+
// s2 +----------------+
// s3 +--------------+
// s4 +--------------+
//
// Example usage:
//
// BandwidthMetrics tracker;
//
// // When a stream is created
// tracker.StartStream();
//
// // When data is transferred on any stream
// tracker.RecordSample(bytes);
//
// // When the stream is finished
// tracker.StopStream();
//
// NOTE: This class is not thread safe.
//
class BandwidthMetrics {
public:
BandwidthMetrics()
: num_streams_in_progress_(0),
num_data_samples_(0),
data_sum_(0.0),
bytes_since_last_start_(0) {
}
// Get the bandwidth. Returns Kbps (kilo-bits-per-second).
double bandwidth() const {
return data_sum_ / num_data_samples_;
}
// Record that we've started a stream.
void StartStream() {
// If we're the only stream, we've finished some idle time. Record a new
// timestamp to indicate the start of data flow.
if (++num_streams_in_progress_ == 1) {
last_start_ = base::TimeTicks::HighResNow();
bytes_since_last_start_ = 0;
}
}
// Track that we've completed a stream.
void StopStream() {
if (--num_streams_in_progress_ == 0) {
// We don't use small streams when tracking bandwidth because they are not
// precise; imagine a 25 byte stream. The sample is too small to make
// a good measurement.
// 20KB is an arbitrary value. We might want to use a lesser value.
static const int64 kRecordSizeThreshold = 20 * 1024;
if (bytes_since_last_start_ < kRecordSizeThreshold)
return;
base::TimeDelta delta = base::TimeTicks::HighResNow() - last_start_;
double ms = delta.InMillisecondsF();
if (ms > 0.0) {
double kbps = static_cast<double>(bytes_since_last_start_) * 8 / ms;
++num_data_samples_;
data_sum_ += kbps;
LOG(INFO) << "Bandwidth: " << kbps
<< "Kbps (avg " << bandwidth() << "Kbps)";
int kbps_int = static_cast<int>(kbps);
UMA_HISTOGRAM_COUNTS_10000("Net.DownloadBandwidth", kbps_int);
}
}
}
// Add a sample of the number of bytes read from the network into the tracker.
void RecordBytes(int bytes) {
DCHECK(num_streams_in_progress_);
bytes_since_last_start_ += static_cast<int64>(bytes);
}
private:
int num_streams_in_progress_; // The number of streams in progress.
// TODO(mbelshe): Use a rolling buffer of 30 samples instead of an average.
int num_data_samples_; // The number of samples collected.
double data_sum_; // The sum of all samples collected.
int64 bytes_since_last_start_; // Bytes tracked during this "session".
base::TimeTicks last_start_; // Timestamp of the begin of this "session".
};
// A utility class for managing the lifecycle of a measured stream.
// It is important that we not leave unclosed streams, and this class helps
// ensure we always stop them.
class ScopedBandwidthMetrics {
public:
explicit ScopedBandwidthMetrics(BandwidthMetrics* metrics)
: metrics_(metrics), started_(false) {
}
~ScopedBandwidthMetrics() {
if (started_)
metrics_->StopStream();
}
void StartStream() {
started_ = true;
metrics_->StartStream();
}
void StopStream() {
started_ = false;
metrics_->StopStream();
}
void RecordBytes(int bytes) { metrics_->RecordBytes(bytes); }
private:
BandwidthMetrics* metrics_;
bool started_;
};
} // namespace net
#endif // NET_BASE_BANDWIDTH_METRICS_H_