• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef NET_BASE_BANDWIDTH_METRICS_H_
6 #define NET_BASE_BANDWIDTH_METRICS_H_
7 
8 #include <list>
9 
10 #include "base/histogram.h"
11 #include "base/logging.h"
12 #include "base/time.h"
13 
14 namespace net {
15 
16 // Tracks statistics about the bandwidth metrics over time.  In order to
17 // measure, this class needs to know when individual streams are in progress,
18 // so that it can know when to discount idle time.  The BandwidthMetrics
19 // is unidirectional - it should only be used to record upload or download
20 // bandwidth, but not both.
21 //
22 // Note, the easiest thing to do is to just measure each stream and average
23 // them or add them.  However, this does not work.  If multiple streams are in
24 // progress concurrently, you have to look at the aggregate bandwidth at any
25 // point in time.
26 //
27 //   Example:
28 //      Imagine 4 streams opening and closing with overlapping time.
29 //      We can't measure bandwidth by looking at any individual stream.
30 //      We can only measure actual bandwidth by looking at the bandwidth
31 //      across all open streams.
32 //
33 //         Time --------------------------------------->
34 //         s1 +----------------+
35 //         s2              +----------------+
36 //         s3                            +--------------+
37 //         s4                            +--------------+
38 //
39 // Example usage:
40 //
41 //   BandwidthMetrics tracker;
42 //
43 //   // When a stream is created
44 //   tracker.StartStream();
45 //
46 //   // When data is transferred on any stream
47 //   tracker.RecordSample(bytes);
48 //
49 //   // When the stream is finished
50 //   tracker.StopStream();
51 //
52 // NOTE: This class is not thread safe.
53 //
54 class BandwidthMetrics {
55  public:
BandwidthMetrics()56   BandwidthMetrics()
57       : num_streams_in_progress_(0),
58         num_data_samples_(0),
59         data_sum_(0.0),
60         bytes_since_last_start_(0) {
61   }
62 
63   // Get the bandwidth.  Returns Kbps (kilo-bits-per-second).
bandwidth()64   double bandwidth() const {
65     return data_sum_ / num_data_samples_;
66   }
67 
68   // Record that we've started a stream.
StartStream()69   void StartStream() {
70     // If we're the only stream, we've finished some idle time.  Record a new
71     // timestamp to indicate the start of data flow.
72     if (++num_streams_in_progress_ == 1) {
73       last_start_ = base::TimeTicks::HighResNow();
74       bytes_since_last_start_ = 0;
75     }
76   }
77 
78   // Track that we've completed a stream.
StopStream()79   void StopStream() {
80     if (--num_streams_in_progress_ == 0) {
81       // We don't use small streams when tracking bandwidth because they are not
82       // precise; imagine a 25 byte stream.  The sample is too small to make
83       // a good measurement.
84       // 20KB is an arbitrary value.  We might want to use a lesser value.
85       static const int64 kRecordSizeThreshold = 20 * 1024;
86       if (bytes_since_last_start_ < kRecordSizeThreshold)
87         return;
88 
89       base::TimeDelta delta = base::TimeTicks::HighResNow() - last_start_;
90       double ms = delta.InMillisecondsF();
91       if (ms > 0.0) {
92         double kbps = static_cast<double>(bytes_since_last_start_) * 8 / ms;
93         ++num_data_samples_;
94         data_sum_ += kbps;
95         LOG(INFO) << "Bandwidth: " << kbps
96                   << "Kbps (avg " << bandwidth() << "Kbps)";
97         int kbps_int = static_cast<int>(kbps);
98         UMA_HISTOGRAM_COUNTS_10000("Net.DownloadBandwidth", kbps_int);
99       }
100     }
101   }
102 
103   // Add a sample of the number of bytes read from the network into the tracker.
RecordBytes(int bytes)104   void RecordBytes(int bytes) {
105     DCHECK(num_streams_in_progress_);
106     bytes_since_last_start_ += static_cast<int64>(bytes);
107   }
108 
109  private:
110   int num_streams_in_progress_;   // The number of streams in progress.
111   // TODO(mbelshe): Use a rolling buffer of 30 samples instead of an average.
112   int num_data_samples_;          // The number of samples collected.
113   double data_sum_;               // The sum of all samples collected.
114   int64 bytes_since_last_start_;  // Bytes tracked during this "session".
115   base::TimeTicks last_start_;    // Timestamp of the begin of this "session".
116 };
117 
118 // A utility class for managing the lifecycle of a measured stream.
119 // It is important that we not leave unclosed streams, and this class helps
120 // ensure we always stop them.
121 class ScopedBandwidthMetrics {
122  public:
ScopedBandwidthMetrics(BandwidthMetrics * metrics)123   explicit ScopedBandwidthMetrics(BandwidthMetrics* metrics)
124       : metrics_(metrics), started_(false) {
125   }
126 
~ScopedBandwidthMetrics()127   ~ScopedBandwidthMetrics() {
128     if (started_)
129       metrics_->StopStream();
130   }
131 
StartStream()132   void StartStream() {
133     started_ = true;
134     metrics_->StartStream();
135   }
136 
StopStream()137   void StopStream() {
138     started_ = false;
139     metrics_->StopStream();
140   }
141 
RecordBytes(int bytes)142   void RecordBytes(int bytes) { metrics_->RecordBytes(bytes); }
143 
144  private:
145   BandwidthMetrics* metrics_;
146   bool started_;
147 };
148 
149 }  // namespace net
150 
151 #endif  // NET_BASE_BANDWIDTH_METRICS_H_
152 
153