Skip to content

Commit

Permalink
Export of internal Abseil changes
Browse files Browse the repository at this point in the history
--
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
  • Loading branch information
Abseil Team authored and suertreus committed Nov 15, 2019
1 parent fa8c751 commit 3df7b52
Show file tree
Hide file tree
Showing 12 changed files with 581 additions and 19 deletions.
11 changes: 8 additions & 3 deletions absl/algorithm/algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ It RotateImpl(It first, It middle, It last, std::false_type) {

} // namespace algorithm_internal

// equal()
//
// Compares the equality of two ranges specified by pairs of iterators, using
// the given predicate, returning true iff for each corresponding iterator i1
// and i2 in the first and second range respectively, pred(*i1, *i2) == true
Expand All @@ -104,15 +106,17 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
typename std::iterator_traits<InputIter2>::iterator_category{});
}

// Performs comparison of two ranges specified by pairs of iterators using
// operator==.
// Overload of equal() that performs comparison of two ranges specified by pairs
// of iterators using operator==.
template <typename InputIter1, typename InputIter2>
bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
InputIter2 last2) {
return absl::equal(first1, last1, first2, last2,
algorithm_internal::EqualTo{});
}

// linear_search()
//
// Performs a linear search for `value` using the iterator `first` up to
// but not including `last`, returning true if [`first`, `last`) contains an
// element equal to `value`.
Expand All @@ -126,6 +130,8 @@ bool linear_search(InputIterator first, InputIterator last,
return std::find(first, last, value) != last;
}

// rotate()
//
// 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
// the first element before rotation. This function is exactly the same as
Expand All @@ -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
// `ForwardIterator` is not a random-access iterator, then `absl::rotate`
// performs an additional pass over the range to construct the return value.

template <typename ForwardIterator>
ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
ForwardIterator last) {
Expand Down
41 changes: 41 additions & 0 deletions absl/base/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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(
name = "scoped_set_env",
testonly = 1,
Expand Down
27 changes: 27 additions & 0 deletions absl/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,33 @@ absl_cc_test(
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(
NAME
scoped_set_env
Expand Down
50 changes: 50 additions & 0 deletions absl/base/internal/periodic_sampler.cc
Original file line number Diff line number Diff line change
@@ -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 changes: 193 additions & 0 deletions absl/base/internal/periodic_sampler.h
Original file line number Diff line number Diff line change
@@ -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_
Loading

0 comments on commit 3df7b52

Please sign in to comment.