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