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