Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/absl/profiling/internal/periodic_sampler.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // Copyright 2019 The Abseil Authors.
0002 //
0003 // Licensed under the Apache License, Version 2.0 (the "License");
0004 // you may not use this file except in compliance with the License.
0005 // You may obtain a copy of the License at
0006 //
0007 //      https://www.apache.org/licenses/LICENSE-2.0
0008 //
0009 // Unless required by applicable law or agreed to in writing, software
0010 // distributed under the License is distributed on an "AS IS" BASIS,
0011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012 // See the License for the specific language governing permissions and
0013 // limitations under the License.
0014 
0015 #ifndef ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_
0016 #define ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_
0017 
0018 #include <stdint.h>
0019 
0020 #include <atomic>
0021 
0022 #include "absl/base/optimization.h"
0023 #include "absl/profiling/internal/exponential_biased.h"
0024 
0025 namespace absl {
0026 ABSL_NAMESPACE_BEGIN
0027 namespace profiling_internal {
0028 
0029 // PeriodicSamplerBase provides the basic period sampler implementation.
0030 //
0031 // This is the base class for the templated PeriodicSampler class, which holds
0032 // a global std::atomic value identified by a user defined tag, such that
0033 // each specific PeriodSampler implementation holds its own global period.
0034 //
0035 // PeriodicSamplerBase is thread-compatible except where stated otherwise.
0036 class PeriodicSamplerBase {
0037  public:
0038   // PeriodicSamplerBase is trivial / copyable / movable / destructible.
0039   PeriodicSamplerBase() = default;
0040   PeriodicSamplerBase(PeriodicSamplerBase&&) = default;
0041   PeriodicSamplerBase(const PeriodicSamplerBase&) = default;
0042 
0043   // Returns true roughly once every `period` calls. This is established by a
0044   // randomly picked `stride` that is counted down on each call to `Sample`.
0045   // This stride is picked such that the probability of `Sample()` returning
0046   // true is 1 in `period`.
0047   inline bool Sample() noexcept;
0048 
0049   // The below methods are intended for optimized use cases where the
0050   // size of the inlined fast path code is highly important. Applications
0051   // should use the `Sample()` method unless they have proof that their
0052   // specific use case requires the optimizations offered by these methods.
0053   //
0054   // An example of such a use case is SwissTable sampling. All sampling checks
0055   // are in inlined SwissTable methods, and the number of call sites is huge.
0056   // In this case, the inlined code size added to each translation unit calling
0057   // SwissTable methods is non-trivial.
0058   //
0059   // The `SubtleMaybeSample()` function spuriously returns true even if the
0060   // function should not be sampled, applications MUST match each call to
0061   // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call,
0062   // and use the result of the latter as the sampling decision.
0063   // In other words: the code should logically be equivalent to:
0064   //
0065   //    if (SubtleMaybeSample() && SubtleConfirmSample()) {
0066   //      // Sample this call
0067   //    }
0068   //
0069   // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can
0070   // be placed out of line, for example, the typical use case looks as follows:
0071   //
0072   //   // --- frobber.h -----------
0073   //   void FrobberSampled();
0074   //
0075   //   inline void FrobberImpl() {
0076   //     // ...
0077   //   }
0078   //
0079   //   inline void Frobber() {
0080   //     if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) {
0081   //       FrobberSampled();
0082   //     } else {
0083   //       FrobberImpl();
0084   //     }
0085   //   }
0086   //
0087   //   // --- frobber.cc -----------
0088   //   void FrobberSampled() {
0089   //     if (!sampler.SubtleConfirmSample())) {
0090   //       // Spurious false positive
0091   //       FrobberImpl();
0092   //       return;
0093   //     }
0094   //
0095   //     // Sampled execution
0096   //     // ...
0097   //   }
0098   inline bool SubtleMaybeSample() noexcept;
0099   bool SubtleConfirmSample() noexcept;
0100 
0101  protected:
0102   // We explicitly don't use a virtual destructor as this class is never
0103   // virtually destroyed, and it keeps the class trivial, which avoids TLS
0104   // prologue and epilogue code for our TLS instances.
0105   ~PeriodicSamplerBase() = default;
0106 
0107   // Returns the next stride for our sampler.
0108   // This function is virtual for testing purposes only.
0109   virtual int64_t GetExponentialBiased(int period) noexcept;
0110 
0111  private:
0112   // Returns the current period of this sampler. Thread-safe.
0113   virtual int period() const noexcept = 0;
0114 
0115   // Keep and decrement stride_ as an unsigned integer, but compare the value
0116   // to zero casted as a signed int. clang and msvc do not create optimum code
0117   // if we use signed for the combined decrement and sign comparison.
0118   //
0119   // Below 3 alternative options, all compiles generate the best code
0120   // using the unsigned increment <---> signed int comparison option.
0121   //
0122   // Option 1:
0123   //   int64_t stride_;
0124   //   if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... }
0125   //
0126   //   GCC   x64 (OK) : https://gcc.godbolt.org/z/R5MzzA
0127   //   GCC   ppc (OK) : https://gcc.godbolt.org/z/z7NZAt
0128   //   Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd
0129   //   ICC   x64 (OK) : https://gcc.godbolt.org/z/rE6s8W
0130   //   MSVC  x64 (OK) : https://gcc.godbolt.org/z/ARMXqS
0131   //
0132   // Option 2:
0133   //   int64_t stride_ = 0;
0134   //   if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... }
0135   //
0136   //   GCC   x64 (OK) : https://gcc.godbolt.org/z/jSQxYK
0137   //   GCC   ppc (OK) : https://gcc.godbolt.org/z/VJdYaA
0138   //   Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX
0139   //   ICC   x64 (OK) : https://gcc.godbolt.org/z/4snaFd
0140   //   MSVC  x64 (BAD): https://gcc.godbolt.org/z/BgnEKE
0141   //
0142   // Option 3:
0143   //   uint64_t stride_;
0144   //   if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) { ... }
0145   //
0146   //   GCC   x64 (OK) : https://gcc.godbolt.org/z/bFbfPy
0147   //   GCC   ppc (OK) : https://gcc.godbolt.org/z/S9KkUE
0148   //   Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4
0149   //   ICC   x64 (OK) : https://gcc.godbolt.org/z/ptTNfD
0150   //   MSVC  x64 (OK) : https://gcc.godbolt.org/z/76j4-5
0151   uint64_t stride_ = 0;
0152   absl::profiling_internal::ExponentialBiased rng_;
0153 };
0154 
0155 inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept {
0156   // See comments on `stride_` for the unsigned increment / signed compare.
0157   if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) {
0158     return false;
0159   }
0160   return true;
0161 }
0162 
0163 inline bool PeriodicSamplerBase::Sample() noexcept {
0164   return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample()
0165                                                  : false;
0166 }
0167 
0168 // PeriodicSampler is a concreted periodic sampler implementation.
0169 // The user provided Tag identifies the implementation, and is required to
0170 // isolate the global state of this instance from other instances.
0171 //
0172 // Typical use case:
0173 //
0174 //   struct HashTablezTag {};
0175 //   thread_local PeriodicSampler<HashTablezTag, 100> sampler;
0176 //
0177 //   void HashTableSamplingLogic(...) {
0178 //     if (sampler.Sample()) {
0179 //       HashTableSlowSamplePath(...);
0180 //     }
0181 //   }
0182 //
0183 template <typename Tag, int default_period = 0>
0184 class PeriodicSampler final : public PeriodicSamplerBase {
0185  public:
0186   ~PeriodicSampler() = default;
0187 
0188   int period() const noexcept final {
0189     return period_.load(std::memory_order_relaxed);
0190   }
0191 
0192   // Sets the global period for this sampler. Thread-safe.
0193   // Setting a period of 0 disables the sampler, i.e., every call to Sample()
0194   // will return false. Setting a period of 1 puts the sampler in 'always on'
0195   // mode, i.e., every call to Sample() returns true.
0196   static void SetGlobalPeriod(int period) {
0197     period_.store(period, std::memory_order_relaxed);
0198   }
0199 
0200  private:
0201   static std::atomic<int> period_;
0202 };
0203 
0204 template <typename Tag, int default_period>
0205 std::atomic<int> PeriodicSampler<Tag, default_period>::period_(default_period);
0206 
0207 }  // namespace profiling_internal
0208 ABSL_NAMESPACE_END
0209 }  // namespace absl
0210 
0211 #endif  // ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_