You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

periodic_sampler.h 9.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // Copyright 2019 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_
  15. #define ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_
  16. #include <stdint.h>
  17. #include <atomic>
  18. #include "absl/base/optimization.h"
  19. #include "absl/profiling/internal/exponential_biased.h"
  20. namespace absl
  21. {
  22. ABSL_NAMESPACE_BEGIN
  23. namespace profiling_internal
  24. {
  25. // PeriodicSamplerBase provides the basic period sampler implementation.
  26. //
  27. // This is the base class for the templated PeriodicSampler class, which holds
  28. // a global std::atomic value identified by a user defined tag, such that
  29. // each specific PeriodSampler implementation holds its own global period.
  30. //
  31. // PeriodicSamplerBase is thread-compatible except where stated otherwise.
  32. class PeriodicSamplerBase
  33. {
  34. public:
  35. // PeriodicSamplerBase is trivial / copyable / movable / destructible.
  36. PeriodicSamplerBase() = default;
  37. PeriodicSamplerBase(PeriodicSamplerBase&&) = default;
  38. PeriodicSamplerBase(const PeriodicSamplerBase&) = default;
  39. // Returns true roughly once every `period` calls. This is established by a
  40. // randomly picked `stride` that is counted down on each call to `Sample`.
  41. // This stride is picked such that the probability of `Sample()` returning
  42. // true is 1 in `period`.
  43. inline bool Sample() noexcept;
  44. // The below methods are intended for optimized use cases where the
  45. // size of the inlined fast path code is highly important. Applications
  46. // should use the `Sample()` method unless they have proof that their
  47. // specific use case requires the optimizations offered by these methods.
  48. //
  49. // An example of such a use case is SwissTable sampling. All sampling checks
  50. // are in inlined SwissTable methods, and the number of call sites is huge.
  51. // In this case, the inlined code size added to each translation unit calling
  52. // SwissTable methods is non-trivial.
  53. //
  54. // The `SubtleMaybeSample()` function spuriously returns true even if the
  55. // function should not be sampled, applications MUST match each call to
  56. // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call,
  57. // and use the result of the latter as the sampling decision.
  58. // In other words: the code should logically be equivalent to:
  59. //
  60. // if (SubtleMaybeSample() && SubtleConfirmSample()) {
  61. // // Sample this call
  62. // }
  63. //
  64. // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can
  65. // be placed out of line, for example, the typical use case looks as follows:
  66. //
  67. // // --- frobber.h -----------
  68. // void FrobberSampled();
  69. //
  70. // inline void FrobberImpl() {
  71. // // ...
  72. // }
  73. //
  74. // inline void Frobber() {
  75. // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) {
  76. // FrobberSampled();
  77. // } else {
  78. // FrobberImpl();
  79. // }
  80. // }
  81. //
  82. // // --- frobber.cc -----------
  83. // void FrobberSampled() {
  84. // if (!sampler.SubtleConfirmSample())) {
  85. // // Spurious false positive
  86. // FrobberImpl();
  87. // return;
  88. // }
  89. //
  90. // // Sampled execution
  91. // // ...
  92. // }
  93. inline bool SubtleMaybeSample() noexcept;
  94. bool SubtleConfirmSample() noexcept;
  95. protected:
  96. // We explicitly don't use a virtual destructor as this class is never
  97. // virtually destroyed, and it keeps the class trivial, which avoids TLS
  98. // prologue and epilogue code for our TLS instances.
  99. ~PeriodicSamplerBase() = default;
  100. // Returns the next stride for our sampler.
  101. // This function is virtual for testing purposes only.
  102. virtual int64_t GetExponentialBiased(int period) noexcept;
  103. private:
  104. // Returns the current period of this sampler. Thread-safe.
  105. virtual int period() const noexcept = 0;
  106. // Keep and decrement stride_ as an unsigned integer, but compare the value
  107. // to zero casted as a signed int. clang and msvc do not create optimum code
  108. // if we use signed for the combined decrement and sign comparison.
  109. //
  110. // Below 3 alternative options, all compiles generate the best code
  111. // using the unsigned increment <---> signed int comparison option.
  112. //
  113. // Option 1:
  114. // int64_t stride_;
  115. // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... }
  116. //
  117. // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA
  118. // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt
  119. // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd
  120. // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W
  121. // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS
  122. //
  123. // Option 2:
  124. // int64_t stride_ = 0;
  125. // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... }
  126. //
  127. // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK
  128. // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA
  129. // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX
  130. // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd
  131. // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE
  132. //
  133. // Option 3:
  134. // uint64_t stride_;
  135. // if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) { ... }
  136. //
  137. // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy
  138. // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE
  139. // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4
  140. // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD
  141. // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5
  142. uint64_t stride_ = 0;
  143. absl::profiling_internal::ExponentialBiased rng_;
  144. };
  145. inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept
  146. {
  147. // See comments on `stride_` for the unsigned increment / signed compare.
  148. if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0))
  149. {
  150. return false;
  151. }
  152. return true;
  153. }
  154. inline bool PeriodicSamplerBase::Sample() noexcept
  155. {
  156. return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample() : false;
  157. }
  158. // PeriodicSampler is a concreted periodic sampler implementation.
  159. // The user provided Tag identifies the implementation, and is required to
  160. // isolate the global state of this instance from other instances.
  161. //
  162. // Typical use case:
  163. //
  164. // struct HashTablezTag {};
  165. // thread_local PeriodicSampler sampler;
  166. //
  167. // void HashTableSamplingLogic(...) {
  168. // if (sampler.Sample()) {
  169. // HashTableSlowSamplePath(...);
  170. // }
  171. // }
  172. //
  173. template<typename Tag, int default_period = 0>
  174. class PeriodicSampler final : public PeriodicSamplerBase
  175. {
  176. public:
  177. ~PeriodicSampler() = default;
  178. int period() const noexcept final
  179. {
  180. return period_.load(std::memory_order_relaxed);
  181. }
  182. // Sets the global period for this sampler. Thread-safe.
  183. // Setting a period of 0 disables the sampler, i.e., every call to Sample()
  184. // will return false. Setting a period of 1 puts the sampler in 'always on'
  185. // mode, i.e., every call to Sample() returns true.
  186. static void SetGlobalPeriod(int period)
  187. {
  188. period_.store(period, std::memory_order_relaxed);
  189. }
  190. private:
  191. static std::atomic<int> period_;
  192. };
  193. template<typename Tag, int default_period>
  194. std::atomic<int> PeriodicSampler<Tag, default_period>::period_(default_period);
  195. } // namespace profiling_internal
  196. ABSL_NAMESPACE_END
  197. } // namespace absl
  198. #endif // ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_