Export of internal Abseil changes
-- 3a871e2cd854a46770a9416189953b2b38980bcf by Andy Getzendanner <durandal@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 280660534 -- f5a155fb056dcf68b089c21aebb5bf5159dc0eb3 by Tom Manshreck <shreck@google.com>: Nit: fix format of algorithm.h comments PiperOrigin-RevId: 280474139 -- 08b117a18353c0daf51b33b45100e5a007e5ab0c by Abseil Team <absl-team@google.com>: Allow absl::Notification::HasBeenNotified to be inlined. PiperOrigin-RevId: 280453271 -- 26d33dd2bad72945f1987f7b2669dbec3a6247e6 by Derek Mauro <dmauro@google.com>: Fix unused variable warnings on Windows PiperOrigin-RevId: 280324417 -- 907e9b23029be683af3b443bafd6a577a595922f by Abseil Team <absl-team@google.com>: PeriodicSampler implementation (for internal-use only) PiperOrigin-RevId: 280257168 GitOrigin-RevId: 3a871e2cd854a46770a9416189953b2b38980bcf Change-Id: I6e2978cae22a6adc41c0577e5c21355e9db7c8f5
This commit is contained in:
parent
fa8c75182f
commit
3df7b52a6a
12 changed files with 581 additions and 19 deletions
|
@ -84,6 +84,8 @@ It RotateImpl(It first, It middle, It last, std::false_type) {
|
||||||
|
|
||||||
} // namespace algorithm_internal
|
} // namespace algorithm_internal
|
||||||
|
|
||||||
|
// equal()
|
||||||
|
//
|
||||||
// Compares the equality of two ranges specified by pairs of iterators, using
|
// Compares the equality of two ranges specified by pairs of iterators, using
|
||||||
// the given predicate, returning true iff for each corresponding iterator i1
|
// the given predicate, returning true iff for each corresponding iterator i1
|
||||||
// and i2 in the first and second range respectively, pred(*i1, *i2) == true
|
// and i2 in the first and second range respectively, pred(*i1, *i2) == true
|
||||||
|
@ -104,8 +106,8 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
|
||||||
typename std::iterator_traits<InputIter2>::iterator_category{});
|
typename std::iterator_traits<InputIter2>::iterator_category{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs comparison of two ranges specified by pairs of iterators using
|
// Overload of equal() that performs comparison of two ranges specified by pairs
|
||||||
// operator==.
|
// of iterators using operator==.
|
||||||
template <typename InputIter1, typename InputIter2>
|
template <typename InputIter1, typename InputIter2>
|
||||||
bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
|
bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
|
||||||
InputIter2 last2) {
|
InputIter2 last2) {
|
||||||
|
@ -113,6 +115,8 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
|
||||||
algorithm_internal::EqualTo{});
|
algorithm_internal::EqualTo{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// linear_search()
|
||||||
|
//
|
||||||
// Performs a linear search for `value` using the iterator `first` up to
|
// Performs a linear search for `value` using the iterator `first` up to
|
||||||
// but not including `last`, returning true if [`first`, `last`) contains an
|
// but not including `last`, returning true if [`first`, `last`) contains an
|
||||||
// element equal to `value`.
|
// element equal to `value`.
|
||||||
|
@ -126,6 +130,8 @@ bool linear_search(InputIterator first, InputIterator last,
|
||||||
return std::find(first, last, value) != last;
|
return std::find(first, last, value) != last;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rotate()
|
||||||
|
//
|
||||||
// Performs a left rotation on a range of elements (`first`, `last`) such that
|
// Performs a left rotation on a range of elements (`first`, `last`) such that
|
||||||
// `middle` is now the first element. `rotate()` returns an iterator pointing to
|
// `middle` is now the first element. `rotate()` returns an iterator pointing to
|
||||||
// the first element before rotation. This function is exactly the same as
|
// the first element before rotation. This function is exactly the same as
|
||||||
|
@ -135,7 +141,6 @@ bool linear_search(InputIterator first, InputIterator last,
|
||||||
// The complexity of this algorithm is the same as that of `std::rotate`, but if
|
// The complexity of this algorithm is the same as that of `std::rotate`, but if
|
||||||
// `ForwardIterator` is not a random-access iterator, then `absl::rotate`
|
// `ForwardIterator` is not a random-access iterator, then `absl::rotate`
|
||||||
// performs an additional pass over the range to construct the return value.
|
// performs an additional pass over the range to construct the return value.
|
||||||
|
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
|
ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
|
||||||
ForwardIterator last) {
|
ForwardIterator last) {
|
||||||
|
|
|
@ -585,6 +585,47 @@ cc_test(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "periodic_sampler",
|
||||||
|
srcs = ["internal/periodic_sampler.cc"],
|
||||||
|
hdrs = ["internal/periodic_sampler.h"],
|
||||||
|
copts = ABSL_DEFAULT_COPTS,
|
||||||
|
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||||
|
deps = [
|
||||||
|
":core_headers",
|
||||||
|
":exponential_biased",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "periodic_sampler_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = ["internal/periodic_sampler_test.cc"],
|
||||||
|
copts = ABSL_TEST_COPTS,
|
||||||
|
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
deps = [
|
||||||
|
":core_headers",
|
||||||
|
":periodic_sampler",
|
||||||
|
"@com_google_googletest//:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "periodic_sampler_benchmark",
|
||||||
|
testonly = 1,
|
||||||
|
srcs = ["internal/periodic_sampler_benchmark.cc"],
|
||||||
|
copts = ABSL_TEST_COPTS,
|
||||||
|
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||||
|
tags = ["benchmark"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
deps = [
|
||||||
|
":core_headers",
|
||||||
|
":periodic_sampler",
|
||||||
|
"@com_github_google_benchmark//:benchmark_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "scoped_set_env",
|
name = "scoped_set_env",
|
||||||
testonly = 1,
|
testonly = 1,
|
||||||
|
|
|
@ -534,6 +534,33 @@ absl_cc_test(
|
||||||
gmock_main
|
gmock_main
|
||||||
)
|
)
|
||||||
|
|
||||||
|
absl_cc_library(
|
||||||
|
NAME
|
||||||
|
periodic_sampler
|
||||||
|
SRCS
|
||||||
|
"internal/periodic_sampler.cc"
|
||||||
|
HDRS
|
||||||
|
"internal/periodic_sampler.h"
|
||||||
|
COPTS
|
||||||
|
${ABSL_DEFAULT_COPTS}
|
||||||
|
DEPS
|
||||||
|
absl::core_headers
|
||||||
|
absl::exponential_biased
|
||||||
|
)
|
||||||
|
|
||||||
|
absl_cc_test(
|
||||||
|
NAME
|
||||||
|
periodic_sampler_test
|
||||||
|
SRCS
|
||||||
|
"internal/periodic_sampler_test.cc"
|
||||||
|
COPTS
|
||||||
|
${ABSL_TEST_COPTS}
|
||||||
|
DEPS
|
||||||
|
absl::core_headers
|
||||||
|
absl::periodic_sampler
|
||||||
|
gmock_main
|
||||||
|
)
|
||||||
|
|
||||||
absl_cc_library(
|
absl_cc_library(
|
||||||
NAME
|
NAME
|
||||||
scoped_set_env
|
scoped_set_env
|
||||||
|
|
50
absl/base/internal/periodic_sampler.cc
Normal file
50
absl/base/internal/periodic_sampler.cc
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "absl/base/internal/periodic_sampler.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "absl/base/internal/exponential_biased.h"
|
||||||
|
|
||||||
|
namespace absl {
|
||||||
|
namespace base_internal {
|
||||||
|
|
||||||
|
int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept {
|
||||||
|
return rng_.Get(period);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeriodicSamplerBase::SubtleConfirmSample() noexcept {
|
||||||
|
int current_period = period();
|
||||||
|
|
||||||
|
// Deal with period case 0 (always off) and 1 (always on)
|
||||||
|
if (ABSL_PREDICT_FALSE(current_period < 2)) {
|
||||||
|
stride_ = 0;
|
||||||
|
return current_period == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is the first call to Sample()
|
||||||
|
if (ABSL_PREDICT_FALSE(stride_ == 1)) {
|
||||||
|
stride_ = -1 - GetExponentialBiased(current_period);
|
||||||
|
if (stride_ < -1) {
|
||||||
|
++stride_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stride_ = -1 - GetExponentialBiased(current_period);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace base_internal
|
||||||
|
} // namespace absl
|
193
absl/base/internal/periodic_sampler.h
Normal file
193
absl/base/internal/periodic_sampler.h
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
// 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 {
|
||||||
|
namespace 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;
|
||||||
|
|
||||||
|
int64_t stride_ = 0;
|
||||||
|
ExponentialBiased rng_;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept {
|
||||||
|
// We explicitly count up and not down, as the compiler does not generate
|
||||||
|
// ideal code for counting down. See also https://gcc.godbolt.org/z/FTPDSM
|
||||||
|
//
|
||||||
|
// With `if (ABSL_PREDICT_FALSE(++stride_ < 0))`
|
||||||
|
// add QWORD PTR fs:sampler@tpoff+8, 1
|
||||||
|
// jns .L15
|
||||||
|
// ret
|
||||||
|
//
|
||||||
|
// With `if (ABSL_PREDICT_FALSE(--stride_ > 0))`
|
||||||
|
// mov rax, QWORD PTR fs:sampler@tpoff+8
|
||||||
|
// sub rax, 1
|
||||||
|
// mov QWORD PTR fs:sampler@tpoff+8, rax
|
||||||
|
// test rax, rax
|
||||||
|
// jle .L15
|
||||||
|
// ret
|
||||||
|
// add QWORD PTR fs:sampler@tpoff+8, 1
|
||||||
|
// jns .L15
|
||||||
|
// ret
|
||||||
|
//
|
||||||
|
// --stride >= 0 does work, but makes our logic slightly harder as in that
|
||||||
|
// case we have less convenient zero-init and over-run values.
|
||||||
|
if (ABSL_PREDICT_FALSE(++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_internal
|
||||||
|
} // namespace absl
|
||||||
|
|
||||||
|
#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
|
70
absl/base/internal/periodic_sampler_benchmark.cc
Normal file
70
absl/base/internal/periodic_sampler_benchmark.cc
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "absl/base/internal/periodic_sampler.h"
|
||||||
|
|
||||||
|
namespace absl {
|
||||||
|
namespace base_internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename Sampler>
|
||||||
|
void BM_Sample(Sampler* sampler, benchmark::State& state) {
|
||||||
|
for (auto _ : state) {
|
||||||
|
benchmark::DoNotOptimize(sampler);
|
||||||
|
benchmark::DoNotOptimize(sampler->Sample());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Sampler>
|
||||||
|
void BM_SampleMinunumInlined(Sampler* sampler, benchmark::State& state) {
|
||||||
|
for (auto _ : state) {
|
||||||
|
benchmark::DoNotOptimize(sampler);
|
||||||
|
if (ABSL_PREDICT_FALSE(sampler->SubtleMaybeSample())) {
|
||||||
|
benchmark::DoNotOptimize(sampler->SubtleConfirmSample());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BM_PeriodicSampler_ShortSample(benchmark::State& state) {
|
||||||
|
struct Tag {};
|
||||||
|
PeriodicSampler<Tag, 1024> sampler;
|
||||||
|
BM_Sample(&sampler, state);
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_PeriodicSampler_ShortSample);
|
||||||
|
|
||||||
|
void BM_PeriodicSampler_LongSample(benchmark::State& state) {
|
||||||
|
struct Tag {};
|
||||||
|
PeriodicSampler<Tag, 1024 * 1024> sampler;
|
||||||
|
BM_Sample(&sampler, state);
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_PeriodicSampler_LongSample);
|
||||||
|
|
||||||
|
void BM_PeriodicSampler_LongSampleMinunumInlined(benchmark::State& state) {
|
||||||
|
struct Tag {};
|
||||||
|
PeriodicSampler<Tag, 1024 * 1024> sampler;
|
||||||
|
BM_SampleMinunumInlined(&sampler, state);
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_PeriodicSampler_LongSampleMinunumInlined);
|
||||||
|
|
||||||
|
void BM_PeriodicSampler_Disabled(benchmark::State& state) {
|
||||||
|
struct Tag {};
|
||||||
|
PeriodicSampler<Tag, 0> sampler;
|
||||||
|
BM_Sample(&sampler, state);
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_PeriodicSampler_Disabled);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace base_internal
|
||||||
|
} // namespace absl
|
175
absl/base/internal/periodic_sampler_test.cc
Normal file
175
absl/base/internal/periodic_sampler_test.cc
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "absl/base/internal/periodic_sampler.h"
|
||||||
|
|
||||||
|
#include <thread> // NOLINT(build/c++11)
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "absl/base/attributes.h"
|
||||||
|
#include "absl/base/macros.h"
|
||||||
|
|
||||||
|
namespace absl {
|
||||||
|
namespace base_internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using testing::Eq;
|
||||||
|
using testing::Return;
|
||||||
|
using testing::StrictMock;
|
||||||
|
|
||||||
|
class MockPeriodicSampler : public PeriodicSamplerBase {
|
||||||
|
public:
|
||||||
|
virtual ~MockPeriodicSampler() = default;
|
||||||
|
|
||||||
|
MOCK_METHOD(int, period, (), (const, noexcept));
|
||||||
|
MOCK_METHOD(int64_t, GetExponentialBiased, (int), (noexcept));
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerBaseTest, Sample) {
|
||||||
|
StrictMock<MockPeriodicSampler> sampler;
|
||||||
|
|
||||||
|
EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(16));
|
||||||
|
EXPECT_CALL(sampler, GetExponentialBiased(16))
|
||||||
|
.WillOnce(Return(1))
|
||||||
|
.WillOnce(Return(2))
|
||||||
|
.WillOnce(Return(3));
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_TRUE(sampler.Sample());
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_TRUE(sampler.Sample());
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerBaseTest, ImmediatelySample) {
|
||||||
|
StrictMock<MockPeriodicSampler> sampler;
|
||||||
|
|
||||||
|
EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16));
|
||||||
|
EXPECT_CALL(sampler, GetExponentialBiased(16))
|
||||||
|
.WillOnce(Return(0))
|
||||||
|
.WillOnce(Return(1))
|
||||||
|
.WillOnce(Return(2));
|
||||||
|
|
||||||
|
EXPECT_TRUE(sampler.Sample());
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_TRUE(sampler.Sample());
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerBaseTest, Disabled) {
|
||||||
|
StrictMock<MockPeriodicSampler> sampler;
|
||||||
|
|
||||||
|
EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(0));
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerBaseTest, AlwaysOn) {
|
||||||
|
StrictMock<MockPeriodicSampler> sampler;
|
||||||
|
|
||||||
|
EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(1));
|
||||||
|
|
||||||
|
EXPECT_TRUE(sampler.Sample());
|
||||||
|
EXPECT_TRUE(sampler.Sample());
|
||||||
|
EXPECT_TRUE(sampler.Sample());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerBaseTest, Disable) {
|
||||||
|
StrictMock<MockPeriodicSampler> sampler;
|
||||||
|
|
||||||
|
EXPECT_CALL(sampler, period()).WillOnce(Return(16));
|
||||||
|
EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(2));
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
|
||||||
|
EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(0));
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerBaseTest, Enable) {
|
||||||
|
StrictMock<MockPeriodicSampler> sampler;
|
||||||
|
|
||||||
|
EXPECT_CALL(sampler, period()).WillOnce(Return(0));
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
|
||||||
|
EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16));
|
||||||
|
EXPECT_CALL(sampler, GetExponentialBiased(16))
|
||||||
|
.Times(2)
|
||||||
|
.WillRepeatedly(Return(2));
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_TRUE(sampler.Sample());
|
||||||
|
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
EXPECT_FALSE(sampler.Sample());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerTest, ConstructConstInit) {
|
||||||
|
struct Tag {};
|
||||||
|
ABSL_CONST_INIT static PeriodicSampler<Tag> sampler;
|
||||||
|
(void)sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerTest, DefaultPeriod0) {
|
||||||
|
struct Tag {};
|
||||||
|
PeriodicSampler<Tag> sampler;
|
||||||
|
EXPECT_THAT(sampler.period(), Eq(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerTest, DefaultPeriod) {
|
||||||
|
struct Tag {};
|
||||||
|
PeriodicSampler<Tag, 100> sampler;
|
||||||
|
EXPECT_THAT(sampler.period(), Eq(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PeriodicSamplerTest, SetGlobalPeriod) {
|
||||||
|
struct Tag1 {};
|
||||||
|
struct Tag2 {};
|
||||||
|
PeriodicSampler<Tag1, 25> sampler1;
|
||||||
|
PeriodicSampler<Tag2, 50> sampler2;
|
||||||
|
|
||||||
|
EXPECT_THAT(sampler1.period(), Eq(25));
|
||||||
|
EXPECT_THAT(sampler2.period(), Eq(50));
|
||||||
|
|
||||||
|
std::thread thread([] {
|
||||||
|
PeriodicSampler<Tag1, 25> sampler1;
|
||||||
|
PeriodicSampler<Tag2, 50> sampler2;
|
||||||
|
EXPECT_THAT(sampler1.period(), Eq(25));
|
||||||
|
EXPECT_THAT(sampler2.period(), Eq(50));
|
||||||
|
sampler1.SetGlobalPeriod(10);
|
||||||
|
sampler2.SetGlobalPeriod(20);
|
||||||
|
});
|
||||||
|
thread.join();
|
||||||
|
|
||||||
|
EXPECT_THAT(sampler1.period(), Eq(10));
|
||||||
|
EXPECT_THAT(sampler2.period(), Eq(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace base_internal
|
||||||
|
} // namespace absl
|
|
@ -54,7 +54,7 @@ static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
|
||||||
|
|
||||||
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
||||||
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
|
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
|
||||||
const void *ucp, int *min_dropped_frames) {
|
const void*, int* min_dropped_frames) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (!RtlCaptureStackBackTrace_fn) {
|
if (!RtlCaptureStackBackTrace_fn) {
|
||||||
// can't find a stacktrace with no function to call
|
// can't find a stacktrace with no function to call
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace absl {
|
||||||
|
|
||||||
static HANDLE process = NULL;
|
static HANDLE process = NULL;
|
||||||
|
|
||||||
void InitializeSymbolizer(const char *argv0) {
|
void InitializeSymbolizer(const char*) {
|
||||||
if (process != nullptr) {
|
if (process != nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,12 @@ void InitializeSymbolizer(const char *argv0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Symbolize(const void *pc, char *out, int out_size) {
|
bool Symbolize(const void* pc, char* out, int out_size) {
|
||||||
if (out_size <= 0) {
|
if (out_size <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
|
alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
|
||||||
SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(buf);
|
SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(buf);
|
||||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||||
if (!SymFromAddr(process, reinterpret_cast<DWORD64>(pc), nullptr, symbol)) {
|
if (!SymFromAddr(process, reinterpret_cast<DWORD64>(pc), nullptr, symbol)) {
|
||||||
|
|
|
@ -44,15 +44,6 @@ Notification::~Notification() {
|
||||||
MutexLock l(&this->mutex_);
|
MutexLock l(&this->mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool HasBeenNotifiedInternal(
|
|
||||||
const std::atomic<bool> *notified_yet) {
|
|
||||||
return notified_yet->load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Notification::HasBeenNotified() const {
|
|
||||||
return HasBeenNotifiedInternal(&this->notified_yet_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Notification::WaitForNotification() const {
|
void Notification::WaitForNotification() const {
|
||||||
if (!HasBeenNotifiedInternal(&this->notified_yet_)) {
|
if (!HasBeenNotifiedInternal(&this->notified_yet_)) {
|
||||||
this->mutex_.LockWhen(Condition(&HasBeenNotifiedInternal,
|
this->mutex_.LockWhen(Condition(&HasBeenNotifiedInternal,
|
||||||
|
|
|
@ -73,7 +73,9 @@ class Notification {
|
||||||
// Notification::HasBeenNotified()
|
// Notification::HasBeenNotified()
|
||||||
//
|
//
|
||||||
// Returns the value of the notification's internal "notified" state.
|
// Returns the value of the notification's internal "notified" state.
|
||||||
bool HasBeenNotified() const;
|
bool HasBeenNotified() const {
|
||||||
|
return HasBeenNotifiedInternal(&this->notified_yet_);
|
||||||
|
}
|
||||||
|
|
||||||
// Notification::WaitForNotification()
|
// Notification::WaitForNotification()
|
||||||
//
|
//
|
||||||
|
@ -105,6 +107,11 @@ class Notification {
|
||||||
void Notify();
|
void Notify();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static inline bool HasBeenNotifiedInternal(
|
||||||
|
const std::atomic<bool>* notified_yet) {
|
||||||
|
return notified_yet->load(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
mutable Mutex mutex_;
|
mutable Mutex mutex_;
|
||||||
std::atomic<bool> notified_yet_; // written under mutex_
|
std::atomic<bool> notified_yet_; // written under mutex_
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,10 +49,13 @@ std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory(
|
||||||
#if !defined(__has_attribute)
|
#if !defined(__has_attribute)
|
||||||
#define __has_attribute(x) 0
|
#define __has_attribute(x) 0
|
||||||
#endif
|
#endif
|
||||||
#if __has_attribute(weak) || defined(__GNUC__)
|
// MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the
|
||||||
|
// Windows linker cannot handle that. Nor does the MinGW compiler know how to
|
||||||
|
// pass "#pragma comment(linker, ...)" to the Windows linker.
|
||||||
|
#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__)
|
||||||
ZoneInfoSourceFactory zone_info_source_factory
|
ZoneInfoSourceFactory zone_info_source_factory
|
||||||
__attribute__((weak)) = DefaultFactory;
|
__attribute__((weak)) = DefaultFactory;
|
||||||
#elif defined(_MSC_VER) && !defined(_LIBCPP_VERSION)
|
#elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION)
|
||||||
extern ZoneInfoSourceFactory zone_info_source_factory;
|
extern ZoneInfoSourceFactory zone_info_source_factory;
|
||||||
extern ZoneInfoSourceFactory default_factory;
|
extern ZoneInfoSourceFactory default_factory;
|
||||||
ZoneInfoSourceFactory default_factory = DefaultFactory;
|
ZoneInfoSourceFactory default_factory = DefaultFactory;
|
||||||
|
|
Loading…
Reference in a new issue