Skip to content

Commit 0dffca4

Browse files
Abseil Teamzhangxy988
Abseil Team
authored andcommitted
Export of internal Abseil changes.
-- 5804cc13b413412988248835459b90cd15ec43d9 by Abseil Team <absl-team@google.com>: Mark raw_hash_set::clear() with the ABSL_ATTRIBUTE_REINITIALIZES attribute. This prevents false positives in the clang-tidy check bugprone-use-after-move; it allows reset() to be called on a moved-from raw_hash_set without any warnings, and the raw_hash_set will thereafter be regarded as initialized again. PiperOrigin-RevId: 230717196 -- ff5961a5600ae19b69a9cba6912126cdf2858f38 by CJ Johnson <johnsoncj@google.com>: Swaps DisableIfIntegral<> for EnableIfInputIterator<> for Iterator member functions of InlinedVector PiperOrigin-RevId: 230559521 -- 3f9754ccbeecbd40f235c6f2465279e045ff51d9 by Derek Mauro <dmauro@google.com>: Import GitHub PR 254 #254 Fixes warnings from -Wclass-memaccess (base_internal::ThreadIdentity? with no trivial copy-assignment). PiperOrigin-RevId: 230536048 -- 8af03a654ce9a4a7f55384bc7eb1ed64878ac2ec by Chris Kennelly <ckennelly@google.com>: absl: cap SpinLock backoff to 4ms The current backoff logic has 3 problems: 1. It can produce too high values (up to 256ms), which can negatively affect tail latency. The value was chosen long time ago and now it's a good idea to reconsider it. 2. It does not have low bound, so on any iteration it can produce a very small value that will lead to unnecessary cpu consumption. 3. It does not increase low bound with the number of iterations. So if the SpinLock is actually somehow locked for a very prolonged time, a waiter can still wake periodically. Rework the logic to solve these problems. Add lower bound of 128us, no code should rely on absence of episodic delays in this range as they can occur everywhere. Lower upper bound to 4ms. A thread sleeping for 4ms does not consume significant cpu time (see below). Grow lower bound with the number of iterations. This is cpu consumption of a process doing usleep(x) in a loop (sampled with ps): 64us -> 4.0% 128us -> 2.7% 256us -> 3.5% 512us -> 2.8% 1024us -> 1.6% 2048us -> 0.6% 4096us -> 0.3% 8192us -> 0.0% Few millisecond sleeps do not consume significant time. PiperOrigin-RevId: 230534015 -- 37ebba92289ca556cb2412cd8b3cb4c1ead3def7 by Samuel Benzaquen <sbenza@google.com>: Add override and dispose hooks to the hashtable sampler. PiperOrigin-RevId: 230353438 -- 89c8f90175233ce9964eb3412df04e8a3cff0c0f by Andy Getzendanner <durandal@google.com>: Fix a comment typo. PiperOrigin-RevId: 229986838 GitOrigin-RevId: 5804cc13b413412988248835459b90cd15ec43d9 Change-Id: Iedb5e2cc9c0b924635c1c87b537780ab6b5b899f
1 parent 6b4201f commit 0dffca4

13 files changed

+236
-29
lines changed

absl/base/internal/spinlock_linux.inc

+6-11
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,12 @@ extern "C" {
5151
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
5252
std::atomic<uint32_t> *w, uint32_t value, int loop,
5353
absl::base_internal::SchedulingMode) {
54-
if (loop != 0) {
55-
int save_errno = errno;
56-
struct timespec tm;
57-
tm.tv_sec = 0;
58-
// Increase the delay; we expect (but do not rely on) explicit wakeups.
59-
// We don't rely on explicit wakeups because we intentionally allow for
60-
// a race on the kSpinLockSleeper bit.
61-
tm.tv_nsec = 16 * absl::base_internal::SpinLockSuggestedDelayNS(loop);
62-
syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
63-
errno = save_errno;
64-
}
54+
int save_errno = errno;
55+
struct timespec tm;
56+
tm.tv_sec = 0;
57+
tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop);
58+
syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
59+
errno = save_errno;
6560
}
6661

6762
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w,

absl/base/internal/spinlock_wait.cc

+5-8
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,14 @@ int SpinLockSuggestedDelayNS(int loop) {
6565
r = 0x5deece66dLL * r + 0xb; // numbers from nrand48()
6666
delay_rand.store(r, std::memory_order_relaxed);
6767

68-
r <<= 16; // 48-bit random number now in top 48-bits.
6968
if (loop < 0 || loop > 32) { // limit loop to 0..32
7069
loop = 32;
7170
}
72-
// loop>>3 cannot exceed 4 because loop cannot exceed 32.
73-
// Select top 20..24 bits of lower 48 bits,
74-
// giving approximately 0ms to 16ms.
75-
// Mean is exponential in loop for first 32 iterations, then 8ms.
76-
// The futex path multiplies this by 16, since we expect explicit wakeups
77-
// almost always on that path.
78-
return static_cast<int>(r >> (44 - (loop >> 3)));
71+
const int kMinDelay = 128 << 10; // 128us
72+
// Double delay every 8 iterations, up to 16x (2ms).
73+
int delay = kMinDelay << (loop / 8);
74+
// Randomize in delay..2*delay range, for resulting 128us..4ms range.
75+
return delay | ((delay - 1) & static_cast<int>(r));
7976
}
8077

8178
} // namespace base_internal

absl/container/BUILD.bazel

+27-1
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,21 @@ cc_library(
446446
copts = ABSL_DEFAULT_COPTS,
447447
)
448448

449+
cc_library(
450+
name = "hashtablez_force_sampling",
451+
srcs = ["internal/hashtablez_force_sampling.cc"],
452+
copts = ABSL_DEFAULT_COPTS,
453+
deps = [
454+
":hashtablez_sampler",
455+
],
456+
)
457+
449458
cc_library(
450459
name = "hashtablez_sampler",
451-
srcs = ["internal/hashtablez_sampler.cc"],
460+
srcs = [
461+
"internal/hashtablez_sampler.cc",
462+
"internal/hashtablez_sampler_force_weak_definition.cc",
463+
],
452464
hdrs = ["internal/hashtablez_sampler.h"],
453465
copts = ABSL_DEFAULT_COPTS,
454466
deps = [
@@ -476,6 +488,20 @@ cc_test(
476488
],
477489
)
478490

491+
cc_test(
492+
name = "hashtablez_force_sampling_test",
493+
srcs = ["internal/hashtablez_force_sampling_test.cc"],
494+
tags = [
495+
"no_test_darwin_x86_64",
496+
"no_test_msvc_x64",
497+
],
498+
deps = [
499+
":hashtablez_force_sampling",
500+
":hashtablez_sampler",
501+
"@com_google_googletest//:gtest_main",
502+
],
503+
)
504+
479505
cc_library(
480506
name = "node_hash_policy",
481507
hdrs = ["internal/node_hash_policy.h"],

absl/container/CMakeLists.txt

+25
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ absl_cc_library(
444444
"internal/hashtablez_sampler.h"
445445
SRCS
446446
"internal/hashtablez_sampler.cc"
447+
"internal/hashtablez_sampler_force_weak_definition.cc"
447448
COPTS
448449
${ABSL_DEFAULT_COPTS}
449450
DEPS
@@ -463,6 +464,30 @@ absl_cc_test(
463464
gmock_main
464465
)
465466

467+
absl_cc_library(
468+
NAME
469+
hashtablez_force_sampling
470+
SRCS
471+
"internal/hashtablez_force_sampling.cc"
472+
COPTS
473+
${ABSL_DEFAULT_COPTS}
474+
DEPS
475+
absl::base
476+
absl::have_sse
477+
absl::synchronization
478+
)
479+
480+
absl_cc_test(
481+
NAME
482+
hashtablez_force_sampling_test
483+
SRCS
484+
"internal/hashtablez_force_sampling_test.cc"
485+
DEPS
486+
absl::hashtablez_force_sampling
487+
absl::hashtablez_sampler
488+
gmock_main
489+
)
490+
466491
absl_cc_library(
467492
NAME
468493
hashtable_debug

absl/container/inlined_vector.h

+4-6
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,6 @@ class InlinedVector {
8181
typename std::iterator_traits<Iterator>::iterator_category,
8282
std::forward_iterator_tag>;
8383

84-
template <typename Iterator>
85-
using DisableIfIntegral =
86-
absl::enable_if_t<!std::is_integral<Iterator>::value>;
87-
8884
template <typename Iterator>
8985
using EnableIfAtLeastInputIterator =
9086
absl::enable_if_t<IsAtLeastInputIterator<Iterator>::value>;
@@ -152,7 +148,8 @@ class InlinedVector {
152148
// NOTE: The `enable_if` prevents ambiguous interpretation between a call to
153149
// this constructor with two integral arguments and a call to the above
154150
// `InlinedVector(size_type, const_reference)` constructor.
155-
template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
151+
template <typename InputIterator,
152+
EnableIfAtLeastInputIterator<InputIterator>* = nullptr>
156153
InlinedVector(InputIterator first, InputIterator last,
157154
const allocator_type& alloc = allocator_type())
158155
: allocator_and_tag_(alloc) {
@@ -516,7 +513,8 @@ class InlinedVector {
516513

517514
// Overload of `InlinedVector::assign()` to replace the contents of the
518515
// inlined vector with values constructed from the range [`first`, `last`).
519-
template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
516+
template <typename InputIterator,
517+
EnableIfAtLeastInputIterator<InputIterator>* = nullptr>
520518
void assign(InputIterator first, InputIterator last) {
521519
AssignRange(first, last);
522520
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2018 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+
// http://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+
15+
#include "absl/container/internal/hashtablez_sampler.h"
16+
17+
namespace absl {
18+
namespace container_internal {
19+
20+
// See hashtablez_sampler.h for details.
21+
extern "C" const bool kAbslContainerInternalSampleEverything = true;
22+
23+
} // namespace container_internal
24+
} // namespace absl
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2018 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+
// http://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+
15+
#include <cstddef>
16+
17+
#include "gmock/gmock.h"
18+
#include "gtest/gtest.h"
19+
#include "absl/container/internal/hashtablez_sampler.h"
20+
21+
namespace absl {
22+
namespace container_internal {
23+
24+
class HashtablezInfoHandlePeer {
25+
public:
26+
static bool IsSampled(const HashtablezInfoHandle& h) {
27+
return h.info_ != nullptr;
28+
}
29+
};
30+
31+
namespace {
32+
33+
bool samples[3]{true, true, true};
34+
35+
// We do this test in a global object to test that this works even before main.
36+
struct Global {
37+
Global() {
38+
// By default it is sampled.
39+
samples[0] = HashtablezInfoHandlePeer::IsSampled(Sample());
40+
41+
// Even with a large parameter, it is sampled.
42+
SetHashtablezSampleParameter(100);
43+
samples[1] = HashtablezInfoHandlePeer::IsSampled(Sample());
44+
45+
// Even if we turn it off, it is still sampled.
46+
SetHashtablezEnabled(false);
47+
samples[2] = HashtablezInfoHandlePeer::IsSampled(Sample());
48+
}
49+
} global;
50+
51+
TEST(kAbslContainerInternalSampleEverything, Works) {
52+
EXPECT_THAT(samples, testing::Each(true));
53+
EXPECT_TRUE(kAbslContainerInternalSampleEverything);
54+
// One more after main()
55+
EXPECT_TRUE(HashtablezInfoHandlePeer::IsSampled(Sample()));
56+
}
57+
58+
} // namespace
59+
} // namespace container_internal
60+
} // namespace absl

absl/container/internal/hashtablez_sampler.cc

+15-1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ HashtablezSampler& HashtablezSampler::Global() {
116116
return *sampler;
117117
}
118118

119+
HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback(
120+
DisposeCallback f) {
121+
return dispose_.exchange(f, std::memory_order_relaxed);
122+
}
123+
119124
HashtablezInfo::HashtablezInfo() { PrepareForSampling(); }
120125
HashtablezInfo::~HashtablezInfo() = default;
121126

@@ -138,7 +143,7 @@ void HashtablezInfo::PrepareForSampling() {
138143
}
139144

140145
HashtablezSampler::HashtablezSampler()
141-
: dropped_samples_(0), size_estimate_(0), all_(nullptr) {
146+
: dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) {
142147
absl::MutexLock l(&graveyard_.init_mu);
143148
graveyard_.dead = &graveyard_;
144149
}
@@ -161,6 +166,10 @@ void HashtablezSampler::PushNew(HashtablezInfo* sample) {
161166
}
162167

163168
void HashtablezSampler::PushDead(HashtablezInfo* sample) {
169+
if (auto* dispose = dispose_.load(std::memory_order_relaxed)) {
170+
dispose(*sample);
171+
}
172+
164173
absl::MutexLock graveyard_lock(&graveyard_.init_mu);
165174
absl::MutexLock sample_lock(&sample->init_mu);
166175
sample->dead = graveyard_.dead;
@@ -220,6 +229,11 @@ int64_t HashtablezSampler::Iterate(
220229
}
221230

222231
HashtablezInfo* SampleSlow(int64_t* next_sample) {
232+
if (kAbslContainerInternalSampleEverything) {
233+
*next_sample = 1;
234+
return HashtablezSampler::Global().Register();
235+
}
236+
223237
bool first = *next_sample < 0;
224238
*next_sample = GetGeometricVariable(
225239
g_hashtablez_sample_parameter.load(std::memory_order_relaxed));

absl/container/internal/hashtablez_sampler.h

+16
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,13 @@ class HashtablezSampler {
183183
// Unregisters the sample.
184184
void Unregister(HashtablezInfo* sample);
185185

186+
// The dispose callback will be called on all samples the moment they are
187+
// being unregistered. Only affects samples that are unregistered after the
188+
// callback has been set.
189+
// Returns the previous callback.
190+
using DisposeCallback = void (*)(const HashtablezInfo&);
191+
DisposeCallback SetDisposeCallback(DisposeCallback f);
192+
186193
// Iterates over all the registered `StackInfo`s. Returning the number of
187194
// samples that have been dropped.
188195
int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f);
@@ -222,6 +229,8 @@ class HashtablezSampler {
222229
//
223230
std::atomic<HashtablezInfo*> all_;
224231
HashtablezInfo graveyard_;
232+
233+
std::atomic<DisposeCallback> dispose_;
225234
};
226235

227236
// Enables or disables sampling for Swiss tables.
@@ -233,6 +242,13 @@ void SetHashtablezSampleParameter(int32_t rate);
233242
// Sets a soft max for the number of samples that will be kept.
234243
void SetHashtablezMaxSamples(int32_t max);
235244

245+
// Configuration override.
246+
// This allows process-wide sampling without depending on order of
247+
// initialization of static storage duration objects.
248+
// The definition of this constant is weak, which allows us to inject a
249+
// different value for it at link time.
250+
extern "C" const bool kAbslContainerInternalSampleEverything;
251+
236252
} // namespace container_internal
237253
} // namespace absl
238254

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2018 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+
// http://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+
15+
#include "absl/container/internal/hashtablez_sampler.h"
16+
17+
#include "absl/base/attributes.h"
18+
19+
namespace absl {
20+
namespace container_internal {
21+
22+
// See hashtablez_sampler.h for details.
23+
extern "C" ABSL_ATTRIBUTE_WEAK const bool
24+
kAbslContainerInternalSampleEverything = false;
25+
26+
} // namespace container_internal
27+
} // namespace absl

absl/container/internal/hashtablez_sampler_test.cc

+25
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,31 @@ TEST(HashtablezSamplerTest, MultiThreaded) {
302302
stop.Notify();
303303
}
304304

305+
TEST(HashtablezSamplerTest, Callback) {
306+
HashtablezSampler sampler;
307+
308+
auto* info1 = Register(&sampler, 1);
309+
auto* info2 = Register(&sampler, 2);
310+
311+
static const HashtablezInfo* expected;
312+
313+
auto callback = [](const HashtablezInfo& info) {
314+
// We can't use `info` outside of this callback because the object will be
315+
// disposed as soon as we return from here.
316+
EXPECT_EQ(&info, expected);
317+
};
318+
319+
// Set the callback.
320+
EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
321+
expected = info1;
322+
sampler.Unregister(info1);
323+
324+
// Unset the callback.
325+
EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
326+
expected = nullptr; // no more calls.
327+
sampler.Unregister(info2);
328+
}
329+
305330
} // namespace
306331
} // namespace container_internal
307332
} // namespace absl

absl/container/internal/raw_hash_set.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ class raw_hash_set {
10351035
size_t capacity() const { return capacity_; }
10361036
size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
10371037

1038-
void clear() {
1038+
ABSL_ATTRIBUTE_REINITIALIZES void clear() {
10391039
// Iterating over this container is O(bucket_count()). When bucket_count()
10401040
// is much greater than size(), iteration becomes prohibitively expensive.
10411041
// For clear() it is more important to reuse the allocated array when the

0 commit comments

Comments
 (0)