Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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_NQE_NETWORK_QUALITY_OBSERVATION_H_ | |
| 6 #define NET_NQE_NETWORK_QUALITY_OBSERVATION_H_ | |
| 7 | |
| 8 #include <float.h> | |
| 9 | |
| 10 #include <algorithm> | |
| 11 #include <deque> | |
| 12 #include <vector> | |
| 13 | |
| 14 #include "base/gtest_prod_util.h" | |
| 15 #include "base/macros.h" | |
| 16 #include "base/time/time.h" | |
| 17 #include "net/base/net_export.h" | |
| 18 #include "net/nqe/network_quality_observation_source.h" | |
| 19 | |
| 20 namespace net { | |
| 21 | |
| 22 namespace nqe { | |
| 23 | |
| 24 namespace internal { | |
| 25 | |
| 26 // Records observations of network quality metrics (such as round trip time | |
| 27 // or throughput), along with the time the observation was made. Observations | |
| 28 // can be made at several places in the network stack, thus the observation | |
| 29 // source is provided as well. ValueType must be numerical so that statistics | |
| 30 // such as median, average can be computed. | |
| 31 template <typename ValueType> | |
| 32 struct NET_EXPORT_PRIVATE Observation { | |
| 33 Observation(const ValueType& value, | |
| 34 base::TimeTicks timestamp, | |
| 35 NetworkQualityObservationSource source) | |
| 36 : value(value), timestamp(timestamp), source(source) { | |
| 37 DCHECK(!timestamp.is_null()); | |
| 38 } | |
| 39 ~Observation() {} | |
| 40 | |
| 41 // Value of the observation. | |
| 42 const ValueType value; | |
| 43 | |
| 44 // Time when the observation was taken. | |
| 45 const base::TimeTicks timestamp; | |
| 46 | |
| 47 // The source of the observation. | |
| 48 const NetworkQualityObservationSource source; | |
| 49 }; | |
| 50 | |
| 51 // Holds an observation and its weight. | |
| 52 template <typename ValueType> | |
| 53 struct NET_EXPORT_PRIVATE WeightedObservation { | |
| 54 WeightedObservation(ValueType value, double weight) | |
| 55 : value(value), weight(weight) {} | |
| 56 WeightedObservation(const WeightedObservation& other) | |
| 57 : WeightedObservation(other.value, other.weight) {} | |
| 58 | |
| 59 WeightedObservation& operator=(const WeightedObservation& other) { | |
| 60 value = other.value; | |
| 61 weight = other.weight; | |
| 62 return *this; | |
| 63 } | |
| 64 | |
| 65 // Required for sorting the samples in the ascending order of values. | |
| 66 bool operator<(const WeightedObservation& other) const { | |
| 67 return (value < other.value); | |
| 68 } | |
| 69 | |
| 70 // Value of the sample. | |
| 71 ValueType value; | |
| 72 | |
| 73 // Weight of the sample. This is computed based on how much time has passed | |
| 74 // since the sample was taken. | |
| 75 double weight; | |
| 76 }; | |
| 77 | |
| 78 // Stores observations sorted by time. | |
| 79 template <typename ValueType> | |
| 80 class NET_EXPORT ObservationBuffer { | |
|
bengr
2016/05/03 21:59:45
Why not move each of these structs/class to a sepa
tbansal1
2016/05/04 22:07:24
Done.
| |
| 81 public: | |
| 82 explicit ObservationBuffer(double weight_multiplier_per_second) | |
| 83 : weight_multiplier_per_second_(weight_multiplier_per_second) { | |
| 84 static_assert(kMaximumObservationsBufferSize > 0U, | |
| 85 "Minimum size of observation buffer must be > 0"); | |
| 86 DCHECK_GE(weight_multiplier_per_second_, 0.0); | |
| 87 DCHECK_LE(weight_multiplier_per_second_, 1.0); | |
| 88 } | |
| 89 | |
| 90 ~ObservationBuffer() {} | |
| 91 | |
| 92 // Adds |observation| to the buffer. The oldest observation in the buffer | |
| 93 // will be evicted to make room if the buffer is already full. | |
| 94 void AddObservation(const Observation<ValueType>& observation) { | |
| 95 DCHECK_LE(observations_.size(), | |
| 96 static_cast<size_t>(kMaximumObservationsBufferSize)); | |
| 97 // Evict the oldest element if the buffer is already full. | |
| 98 if (observations_.size() == kMaximumObservationsBufferSize) | |
| 99 observations_.pop_front(); | |
| 100 | |
| 101 observations_.push_back(observation); | |
| 102 DCHECK_LE(observations_.size(), | |
| 103 static_cast<size_t>(kMaximumObservationsBufferSize)); | |
| 104 } | |
| 105 | |
| 106 // Returns the number of observations in this buffer. | |
| 107 size_t Size() const { return observations_.size(); } | |
| 108 | |
| 109 // Returns the capacity of this buffer. | |
| 110 size_t Capacity() const { return kMaximumObservationsBufferSize; } | |
| 111 | |
| 112 // Clears the observations stored in this buffer. | |
| 113 void Clear() { observations_.clear(); } | |
| 114 | |
| 115 // Returns true iff the |percentile| value of the observations in this | |
| 116 // buffer is available. Sets |result| to the computed |percentile| | |
| 117 // value among all observations since |begin_timestamp|. If the value is | |
| 118 // unavailable, false is returned and |result| is not modified. Percentile | |
| 119 // value is unavailable if all the values in observation buffer are older | |
| 120 // than |begin_timestamp|. | |
| 121 // |result| must not be null. | |
| 122 bool GetPercentile(const base::TimeTicks& begin_timestamp, | |
| 123 ValueType* result, | |
| 124 int percentile, | |
| 125 const std::vector<NetworkQualityObservationSource>& | |
| 126 disallowed_observation_sources) const { | |
| 127 DCHECK(result); | |
| 128 DCHECK_GE(static_cast<size_t>(kMaximumObservationsBufferSize), Size()); | |
| 129 // Stores WeightedObservation in increasing order of value. | |
| 130 std::vector<WeightedObservation<ValueType>> weighted_observations; | |
| 131 | |
| 132 // Total weight of all observations in |weighted_observations|. | |
| 133 double total_weight = 0.0; | |
| 134 | |
| 135 ComputeWeightedObservations(begin_timestamp, weighted_observations, | |
| 136 &total_weight, disallowed_observation_sources); | |
| 137 if (weighted_observations.empty()) | |
| 138 return false; | |
| 139 | |
| 140 DCHECK(!weighted_observations.empty()); | |
| 141 DCHECK_GT(total_weight, 0.0); | |
| 142 | |
| 143 // |weighted_observations| may have a smaller size than observations_ since | |
| 144 // the former contains only the observations later than begin_timestamp. | |
| 145 DCHECK_GE(observations_.size(), weighted_observations.size()); | |
| 146 | |
| 147 double desired_weight = percentile / 100.0 * total_weight; | |
| 148 | |
| 149 double cumulative_weight_seen_so_far = 0.0; | |
| 150 for (const auto& weighted_observation : weighted_observations) { | |
| 151 cumulative_weight_seen_so_far += weighted_observation.weight; | |
| 152 | |
| 153 if (cumulative_weight_seen_so_far >= desired_weight) { | |
| 154 *result = weighted_observation.value; | |
| 155 return true; | |
| 156 } | |
| 157 } | |
| 158 | |
| 159 // Computation may reach here due to floating point errors. This may happen | |
| 160 // if |percentile| was 100 (or close to 100), and |desired_weight| was | |
| 161 // slightly larger than |total_weight| (due to floating point errors). | |
| 162 // In this case, we return the highest |value| among all observations. | |
| 163 // This is same as value of the last observation in the sorted vector. | |
| 164 *result = weighted_observations.at(weighted_observations.size() - 1).value; | |
| 165 return true; | |
| 166 } | |
| 167 | |
| 168 private: | |
| 169 // Maximum number of observations that can be held in the ObservationBuffer. | |
| 170 static const size_t kMaximumObservationsBufferSize = 300; | |
| 171 | |
| 172 // Computes the weighted observations and stores them in | |
| 173 // |weighted_observations| sorted by ascending |WeightedObservation.value|. | |
| 174 // Only the observations with timestamp later than |begin_timestamp| are | |
| 175 // considered. Also, sets |total_weight| to the total weight of all | |
| 176 // observations. Should be called only when there is at least one | |
| 177 // observation in the buffer. | |
| 178 void ComputeWeightedObservations( | |
| 179 const base::TimeTicks& begin_timestamp, | |
| 180 std::vector<WeightedObservation<ValueType>>& weighted_observations, | |
| 181 double* total_weight, | |
| 182 const std::vector<NetworkQualityObservationSource>& | |
| 183 disallowed_observation_sources) const { | |
| 184 DCHECK_GE(static_cast<size_t>(kMaximumObservationsBufferSize), Size()); | |
| 185 | |
| 186 weighted_observations.clear(); | |
| 187 double total_weight_observations = 0.0; | |
| 188 base::TimeTicks now = base::TimeTicks::Now(); | |
| 189 | |
| 190 for (const auto& observation : observations_) { | |
| 191 if (observation.timestamp < begin_timestamp) | |
| 192 continue; | |
| 193 bool disallowed = false; | |
| 194 for (const auto& disallowed_source : disallowed_observation_sources) { | |
| 195 if (disallowed_source == observation.source) | |
| 196 disallowed = true; | |
| 197 } | |
| 198 if (disallowed) | |
| 199 continue; | |
| 200 base::TimeDelta time_since_sample_taken = now - observation.timestamp; | |
| 201 double weight = pow(weight_multiplier_per_second_, | |
| 202 time_since_sample_taken.InSeconds()); | |
| 203 weight = std::max(DBL_MIN, std::min(1.0, weight)); | |
| 204 | |
| 205 weighted_observations.push_back( | |
| 206 WeightedObservation<ValueType>(observation.value, weight)); | |
| 207 total_weight_observations += weight; | |
| 208 } | |
| 209 | |
| 210 // Sort the samples by value in ascending order. | |
| 211 std::sort(weighted_observations.begin(), weighted_observations.end()); | |
| 212 *total_weight = total_weight_observations; | |
| 213 } | |
| 214 | |
| 215 // Holds observations sorted by time, with the oldest observation at the | |
| 216 // front of the queue. | |
| 217 std::deque<Observation<ValueType>> observations_; | |
| 218 | |
| 219 // The factor by which the weight of an observation reduces every second. | |
| 220 // For example, if an observation is 6 seconds old, its weight would be: | |
| 221 // weight_multiplier_per_second_ ^ 6 | |
| 222 // Calculated from |kHalfLifeSeconds| by solving the following equation: | |
| 223 // weight_multiplier_per_second_ ^ kHalfLifeSeconds = 0.5 | |
| 224 const double weight_multiplier_per_second_; | |
| 225 | |
| 226 DISALLOW_COPY_AND_ASSIGN(ObservationBuffer); | |
| 227 }; | |
| 228 | |
| 229 } // namespace internal | |
| 230 | |
| 231 } // namespace nqe | |
| 232 | |
| 233 } // namespace net | |
| 234 | |
| 235 #endif // NET_NQE_NETWORK_QUALITY_OBSERVATION_H_ | |
| OLD | NEW |