Abseil Common Libraries (C++) (grcp 依赖)
https://abseil.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
7.5 KiB
211 lines
7.5 KiB
// 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_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_ |
|
#define ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_ |
|
|
|
#include <stdint.h> |
|
|
|
#include <atomic> |
|
|
|
#include "absl/base/optimization.h" |
|
#include "absl/profiling/internal/exponential_biased.h" |
|
|
|
namespace absl { |
|
ABSL_NAMESPACE_BEGIN |
|
namespace profiling_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; |
|
absl::profiling_internal::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 profiling_internal |
|
ABSL_NAMESPACE_END |
|
} // namespace absl |
|
|
|
#endif // ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_
|
|
|