tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

histogram.cc (5942B)


      1 /*
      2 *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/audio_coding/neteq/histogram.h"
     12 
     13 #include <algorithm>
     14 #include <cstdint>
     15 #include <cstdlib>
     16 #include <optional>
     17 
     18 #include "rtc_base/checks.h"
     19 
     20 namespace webrtc {
     21 
     22 Histogram::Histogram(size_t num_buckets,
     23                     int forget_factor,
     24                     std::optional<double> start_forget_weight)
     25    : buckets_(num_buckets, 0),
     26      forget_factor_(0),
     27      base_forget_factor_(forget_factor),
     28      add_count_(0),
     29      start_forget_weight_(start_forget_weight) {
     30  RTC_DCHECK_LT(base_forget_factor_, 1 << 15);
     31 }
     32 
     33 Histogram::~Histogram() {}
     34 
     35 // Each element in the vector is first multiplied by the forgetting factor
     36 // `forget_factor_`. Then the vector element indicated by `iat_packets` is then
     37 // increased (additive) by 1 - `forget_factor_`. This way, the probability of
     38 // `value` is slightly increased, while the sum of the histogram remains
     39 // constant (=1).
     40 // Due to inaccuracies in the fixed-point arithmetic, the histogram may no
     41 // longer sum up to 1 (in Q30) after the update. To correct this, a correction
     42 // term is added or subtracted from the first element (or elements) of the
     43 // vector.
     44 // The forgetting factor `forget_factor_` is also updated. When the DelayManager
     45 // is reset, the factor is set to 0 to facilitate rapid convergence in the
     46 // beginning. With each update of the histogram, the factor is increased towards
     47 // the steady-state value `base_forget_factor_`.
     48 void Histogram::Add(int value) {
     49  RTC_DCHECK(value >= 0);
     50  RTC_DCHECK(value < static_cast<int>(buckets_.size()));
     51  int vector_sum = 0;  // Sum up the vector elements as they are processed.
     52  // Multiply each element in `buckets_` with `forget_factor_`.
     53  for (int& bucket : buckets_) {
     54    bucket = (static_cast<int64_t>(bucket) * forget_factor_) >> 15;
     55    vector_sum += bucket;
     56  }
     57 
     58  // Increase the probability for the currently observed inter-arrival time
     59  // by 1 - `forget_factor_`. The factor is in Q15, `buckets_` in Q30.
     60  // Thus, left-shift 15 steps to obtain result in Q30.
     61  buckets_[value] += (32768 - forget_factor_) << 15;
     62  vector_sum += (32768 - forget_factor_) << 15;  // Add to vector sum.
     63 
     64  // `buckets_` should sum up to 1 (in Q30), but it may not due to
     65  // fixed-point rounding errors.
     66  vector_sum -= 1 << 30;  // Should be zero. Compensate if not.
     67  if (vector_sum != 0) {
     68    // Modify a few values early in `buckets_`.
     69    int flip_sign = vector_sum > 0 ? -1 : 1;
     70    for (int& bucket : buckets_) {
     71      // Add/subtract 1/16 of the element, but not more than `vector_sum`.
     72      int correction = flip_sign * std::min(std::abs(vector_sum), bucket >> 4);
     73      bucket += correction;
     74      vector_sum += correction;
     75      if (std::abs(vector_sum) == 0) {
     76        break;
     77      }
     78    }
     79  }
     80  RTC_DCHECK(vector_sum == 0);  // Verify that the above is correct.
     81 
     82  ++add_count_;
     83 
     84  // Update `forget_factor_` (changes only during the first seconds after a
     85  // reset). The factor converges to `base_forget_factor_`.
     86  if (start_forget_weight_) {
     87    if (forget_factor_ != base_forget_factor_) {
     88      int old_forget_factor = forget_factor_;
     89      int forget_factor =
     90          (1 << 15) * (1 - start_forget_weight_.value() / (add_count_ + 1));
     91      forget_factor_ =
     92          std::max(0, std::min(base_forget_factor_, forget_factor));
     93      // The histogram is updated recursively by forgetting the old histogram
     94      // with `forget_factor_` and adding a new sample multiplied by |1 -
     95      // forget_factor_|. We need to make sure that the effective weight on the
     96      // new sample is no smaller than those on the old samples, i.e., to
     97      // satisfy the following DCHECK.
     98      RTC_DCHECK_GE((1 << 15) - forget_factor_,
     99                    ((1 << 15) - old_forget_factor) * forget_factor_ >> 15);
    100    }
    101  } else {
    102    forget_factor_ += (base_forget_factor_ - forget_factor_ + 3) >> 2;
    103  }
    104 }
    105 
    106 int Histogram::Quantile(int probability) {
    107  // Find the bucket for which the probability of observing an
    108  // inter-arrival time larger than or equal to `index` is larger than or
    109  // equal to `probability`. The sought probability is estimated using
    110  // the histogram as the reverse cumulant PDF, i.e., the sum of elements from
    111  // the end up until `index`. Now, since the sum of all elements is 1
    112  // (in Q30) by definition, and since the solution is often a low value for
    113  // `iat_index`, it is more efficient to start with `sum` = 1 and subtract
    114  // elements from the start of the histogram.
    115  int inverse_probability = (1 << 30) - probability;
    116  size_t index = 0;   // Start from the beginning of `buckets_`.
    117  int sum = 1 << 30;  // Assign to 1 in Q30.
    118  sum -= buckets_[index];
    119 
    120  while ((sum > inverse_probability) && (index < buckets_.size() - 1)) {
    121    // Subtract the probabilities one by one until the sum is no longer greater
    122    // than `inverse_probability`.
    123    ++index;
    124    sum -= buckets_[index];
    125  }
    126  return static_cast<int>(index);
    127 }
    128 
    129 // Set the histogram vector to an exponentially decaying distribution
    130 // buckets_[i] = 0.5^(i+1), i = 0, 1, 2, ...
    131 // buckets_ is in Q30.
    132 void Histogram::Reset() {
    133  // Set temp_prob to (slightly more than) 1 in Q14. This ensures that the sum
    134  // of buckets_ is 1.
    135  uint16_t temp_prob = 0x4002;  // 16384 + 2 = 100000000000010 binary.
    136  for (int& bucket : buckets_) {
    137    temp_prob >>= 1;
    138    bucket = temp_prob << 16;
    139  }
    140  forget_factor_ = 0;  // Adapt the histogram faster for the first few packets.
    141  add_count_ = 0;
    142 }
    143 
    144 int Histogram::NumBuckets() const {
    145  return buckets_.size();
    146 }
    147 
    148 }  // namespace webrtc