Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2011 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/logging.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/time/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:
     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).
     64   double bandwidth() const {
     65     return data_sum_ / num_data_samples_;
     66   }
     67 
     68   // Record that we've started a stream.
     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.
     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         VLOG(1) << "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.
    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:
    123   ScopedBandwidthMetrics();
    124   ~ScopedBandwidthMetrics();
    125 
    126   void StartStream();
    127   void StopStream();
    128   void RecordBytes(int bytes);
    129 
    130  private:
    131   bool started_;
    132 };
    133 
    134 }  // namespace net
    135 
    136 #endif  // NET_BASE_BANDWIDTH_METRICS_H_
    137