summaryrefslogtreecommitdiffstats
path: root/net/base/bandwidth_metrics.h
blob: c290ca95ae87136e438f30c9e5d6a248e68de1cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 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_