| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 | // Copyright 2019 The Abseil Authors.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at////      https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.#ifndef ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_#include <stdint.h>#include <atomic>#include "absl/base/internal/exponential_biased.h"#include "absl/base/optimization.h"namespace absl {ABSL_NAMESPACE_BEGINnamespace base_internal {// PeriodicSamplerBase provides the basic period sampler implementation.//// This is the base class for the templated PeriodicSampler class, which holds// a global std::atomic value identified by a user defined tag, such that// each specific PeriodSampler implementation holds its own global period.//// PeriodicSamplerBase is thread-compatible except where stated otherwise.class PeriodicSamplerBase { public:  // PeriodicSamplerBase is trivial / copyable / movable / destructible.  PeriodicSamplerBase() = default;  PeriodicSamplerBase(PeriodicSamplerBase&&) = default;  PeriodicSamplerBase(const PeriodicSamplerBase&) = default;  // Returns true roughly once every `period` calls. This is established by a  // randomly picked `stride` that is counted down on each call to `Sample`.  // This stride is picked such that the probability of `Sample()` returning  // true is 1 in `period`.  inline bool Sample() noexcept;  // The below methods are intended for optimized use cases where the  // size of the inlined fast path code is highly important. Applications  // should use the `Sample()` method unless they have proof that their  // specific use case requires the optimizations offered by these methods.  //  // An example of such a use case is SwissTable sampling. All sampling checks  // are in inlined SwissTable methods, and the number of call sites is huge.  // In this case, the inlined code size added to each translation unit calling  // SwissTable methods is non-trivial.  //  // The `SubtleMaybeSample()` function spuriously returns true even if the  // function should not be sampled, applications MUST match each call to  // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call,  // and use the result of the latter as the sampling decision.  // In other words: the code should logically be equivalent to:  //  //    if (SubtleMaybeSample() && SubtleConfirmSample()) {  //      // Sample this call  //    }  //  // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can  // be placed out of line, for example, the typical use case looks as follows:  //  //   // --- frobber.h -----------  //   void FrobberSampled();  //  //   inline void FrobberImpl() {  //     // ...  //   }  //  //   inline void Frobber() {  //     if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) {  //       FrobberSampled();  //     } else {  //       FrobberImpl();  //     }  //   }  //  //   // --- frobber.cc -----------  //   void FrobberSampled() {  //     if (!sampler.SubtleConfirmSample())) {  //       // Spurious false positive  //       FrobberImpl();  //       return;  //     }  //  //     // Sampled execution  //     // ...  //   }  inline bool SubtleMaybeSample() noexcept;  bool SubtleConfirmSample() noexcept; protected:  // We explicitly don't use a virtual destructor as this class is never  // virtually destroyed, and it keeps the class trivial, which avoids TLS  // prologue and epilogue code for our TLS instances.  ~PeriodicSamplerBase() = default;  // Returns the next stride for our sampler.  // This function is virtual for testing purposes only.  virtual int64_t GetExponentialBiased(int period) noexcept; private:  // Returns the current period of this sampler. Thread-safe.  virtual int period() const noexcept = 0;  // Keep and decrement stride_ as an unsigned integer, but compare the value  // to zero casted as a signed int. clang and msvc do not create optimum code  // if we use signed for the combined decrement and sign comparison.  //  // Below 3 alternative options, all compiles generate the best code  // using the unsigned increment <---> signed int comparison option.  //  // Option 1:  //   int64_t stride_;  //   if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... }  //  //   GCC   x64 (OK) : https://gcc.godbolt.org/z/R5MzzA  //   GCC   ppc (OK) : https://gcc.godbolt.org/z/z7NZAt  //   Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd  //   ICC   x64 (OK) : https://gcc.godbolt.org/z/rE6s8W  //   MSVC  x64 (OK) : https://gcc.godbolt.org/z/ARMXqS  //  // Option 2:  //   int64_t stride_ = 0;  //   if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... }  //  //   GCC   x64 (OK) : https://gcc.godbolt.org/z/jSQxYK  //   GCC   ppc (OK) : https://gcc.godbolt.org/z/VJdYaA  //   Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX  //   ICC   x64 (OK) : https://gcc.godbolt.org/z/4snaFd  //   MSVC  x64 (BAD): https://gcc.godbolt.org/z/BgnEKE  //  // Option 3:  //   uint64_t stride_;  //   if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) { ... }  //  //   GCC   x64 (OK) : https://gcc.godbolt.org/z/bFbfPy  //   GCC   ppc (OK) : https://gcc.godbolt.org/z/S9KkUE  //   Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4  //   ICC   x64 (OK) : https://gcc.godbolt.org/z/ptTNfD  //   MSVC  x64 (OK) : https://gcc.godbolt.org/z/76j4-5  uint64_t stride_ = 0;  ExponentialBiased rng_;};inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept {  // See comments on `stride_` for the unsigned increment / signed compare.  if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) {    return false;  }  return true;}inline bool PeriodicSamplerBase::Sample() noexcept {  return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample()                                                 : false;}// PeriodicSampler is a concreted periodic sampler implementation.// The user provided Tag identifies the implementation, and is required to// isolate the global state of this instance from other instances.//// Typical use case:////   struct HashTablezTag {};//   thread_local PeriodicSampler sampler;////   void HashTableSamplingLogic(...) {//     if (sampler.Sample()) {//       HashTableSlowSamplePath(...);//     }//   }//template <typename Tag, int default_period = 0>class PeriodicSampler final : public PeriodicSamplerBase { public:  ~PeriodicSampler() = default;  int period() const noexcept final {    return period_.load(std::memory_order_relaxed);  }  // Sets the global period for this sampler. Thread-safe.  // Setting a period of 0 disables the sampler, i.e., every call to Sample()  // will return false. Setting a period of 1 puts the sampler in 'always on'  // mode, i.e., every call to Sample() returns true.  static void SetGlobalPeriod(int period) {    period_.store(period, std::memory_order_relaxed);  } private:  static std::atomic<int> period_;};template <typename Tag, int default_period>std::atomic<int> PeriodicSampler<Tag, default_period>::period_(default_period);}  // namespace base_internalABSL_NAMESPACE_END}  // namespace absl#endif  // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
 |