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: BandwidthMetrics()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). bandwidth()65 double bandwidth() const { 66 return data_sum_ / num_data_samples_; 67 } 68 69 // Record that we've started a stream. StartStream()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. StopStream()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. RecordBytes(int bytes)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