00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "absl/container/internal/hashtablez_sampler.h"
00016
00017 #include <atomic>
00018 #include <limits>
00019 #include <random>
00020
00021 #include "gmock/gmock.h"
00022 #include "gtest/gtest.h"
00023 #include "absl/base/attributes.h"
00024 #include "absl/container/internal/have_sse.h"
00025 #include "absl/synchronization/blocking_counter.h"
00026 #include "absl/synchronization/internal/thread_pool.h"
00027 #include "absl/synchronization/mutex.h"
00028 #include "absl/synchronization/notification.h"
00029 #include "absl/time/clock.h"
00030 #include "absl/time/time.h"
00031
00032 #if SWISSTABLE_HAVE_SSE2
00033 constexpr int kProbeLength = 16;
00034 #else
00035 constexpr int kProbeLength = 8;
00036 #endif
00037
00038 namespace absl {
00039 namespace container_internal {
00040 class HashtablezInfoHandlePeer {
00041 public:
00042 static bool IsSampled(const HashtablezInfoHandle& h) {
00043 return h.info_ != nullptr;
00044 }
00045
00046 static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; }
00047 };
00048
00049 namespace {
00050 using ::absl::synchronization_internal::ThreadPool;
00051 using ::testing::IsEmpty;
00052 using ::testing::UnorderedElementsAre;
00053
00054 std::vector<size_t> GetSizes(HashtablezSampler* s) {
00055 std::vector<size_t> res;
00056 s->Iterate([&](const HashtablezInfo& info) {
00057 res.push_back(info.size.load(std::memory_order_acquire));
00058 });
00059 return res;
00060 }
00061
00062 HashtablezInfo* Register(HashtablezSampler* s, size_t size) {
00063 auto* info = s->Register();
00064 assert(info != nullptr);
00065 info->size.store(size);
00066 return info;
00067 }
00068
00069 TEST(HashtablezInfoTest, PrepareForSampling) {
00070 absl::Time test_start = absl::Now();
00071 HashtablezInfo info;
00072 absl::MutexLock l(&info.init_mu);
00073 info.PrepareForSampling();
00074
00075 EXPECT_EQ(info.capacity.load(), 0);
00076 EXPECT_EQ(info.size.load(), 0);
00077 EXPECT_EQ(info.num_erases.load(), 0);
00078 EXPECT_EQ(info.max_probe_length.load(), 0);
00079 EXPECT_EQ(info.total_probe_length.load(), 0);
00080 EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
00081 EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
00082 EXPECT_GE(info.create_time, test_start);
00083
00084 info.capacity.store(1, std::memory_order_relaxed);
00085 info.size.store(1, std::memory_order_relaxed);
00086 info.num_erases.store(1, std::memory_order_relaxed);
00087 info.max_probe_length.store(1, std::memory_order_relaxed);
00088 info.total_probe_length.store(1, std::memory_order_relaxed);
00089 info.hashes_bitwise_or.store(1, std::memory_order_relaxed);
00090 info.hashes_bitwise_and.store(1, std::memory_order_relaxed);
00091 info.create_time = test_start - absl::Hours(20);
00092
00093 info.PrepareForSampling();
00094 EXPECT_EQ(info.capacity.load(), 0);
00095 EXPECT_EQ(info.size.load(), 0);
00096 EXPECT_EQ(info.num_erases.load(), 0);
00097 EXPECT_EQ(info.max_probe_length.load(), 0);
00098 EXPECT_EQ(info.total_probe_length.load(), 0);
00099 EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
00100 EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
00101 EXPECT_GE(info.create_time, test_start);
00102 }
00103
00104 TEST(HashtablezInfoTest, RecordStorageChanged) {
00105 HashtablezInfo info;
00106 absl::MutexLock l(&info.init_mu);
00107 info.PrepareForSampling();
00108 RecordStorageChangedSlow(&info, 17, 47);
00109 EXPECT_EQ(info.size.load(), 17);
00110 EXPECT_EQ(info.capacity.load(), 47);
00111 RecordStorageChangedSlow(&info, 20, 20);
00112 EXPECT_EQ(info.size.load(), 20);
00113 EXPECT_EQ(info.capacity.load(), 20);
00114 }
00115
00116 TEST(HashtablezInfoTest, RecordInsert) {
00117 HashtablezInfo info;
00118 absl::MutexLock l(&info.init_mu);
00119 info.PrepareForSampling();
00120 EXPECT_EQ(info.max_probe_length.load(), 0);
00121 RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
00122 EXPECT_EQ(info.max_probe_length.load(), 6);
00123 EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00);
00124 EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00);
00125 RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength);
00126 EXPECT_EQ(info.max_probe_length.load(), 6);
00127 EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000);
00128 EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00);
00129 RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength);
00130 EXPECT_EQ(info.max_probe_length.load(), 12);
00131 EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000);
00132 EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00);
00133 }
00134
00135 TEST(HashtablezInfoTest, RecordErase) {
00136 HashtablezInfo info;
00137 absl::MutexLock l(&info.init_mu);
00138 info.PrepareForSampling();
00139 EXPECT_EQ(info.num_erases.load(), 0);
00140 EXPECT_EQ(info.size.load(), 0);
00141 RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
00142 EXPECT_EQ(info.size.load(), 1);
00143 RecordEraseSlow(&info);
00144 EXPECT_EQ(info.size.load(), 0);
00145 EXPECT_EQ(info.num_erases.load(), 1);
00146 }
00147
00148 TEST(HashtablezInfoTest, RecordRehash) {
00149 HashtablezInfo info;
00150 absl::MutexLock l(&info.init_mu);
00151 info.PrepareForSampling();
00152 RecordInsertSlow(&info, 0x1, 0);
00153 RecordInsertSlow(&info, 0x2, kProbeLength);
00154 RecordInsertSlow(&info, 0x4, kProbeLength);
00155 RecordInsertSlow(&info, 0x8, 2 * kProbeLength);
00156 EXPECT_EQ(info.size.load(), 4);
00157 EXPECT_EQ(info.total_probe_length.load(), 4);
00158
00159 RecordEraseSlow(&info);
00160 RecordEraseSlow(&info);
00161 EXPECT_EQ(info.size.load(), 2);
00162 EXPECT_EQ(info.total_probe_length.load(), 4);
00163 EXPECT_EQ(info.num_erases.load(), 2);
00164
00165 RecordRehashSlow(&info, 3 * kProbeLength);
00166 EXPECT_EQ(info.size.load(), 2);
00167 EXPECT_EQ(info.total_probe_length.load(), 3);
00168 EXPECT_EQ(info.num_erases.load(), 0);
00169 }
00170
00171 TEST(HashtablezSamplerTest, SmallSampleParameter) {
00172 SetHashtablezEnabled(true);
00173 SetHashtablezSampleParameter(100);
00174
00175 for (int i = 0; i < 1000; ++i) {
00176 int64_t next_sample = 0;
00177 HashtablezInfo* sample = SampleSlow(&next_sample);
00178 EXPECT_GT(next_sample, 0);
00179 EXPECT_NE(sample, nullptr);
00180 UnsampleSlow(sample);
00181 }
00182 }
00183
00184 TEST(HashtablezSamplerTest, LargeSampleParameter) {
00185 SetHashtablezEnabled(true);
00186 SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
00187
00188 for (int i = 0; i < 1000; ++i) {
00189 int64_t next_sample = 0;
00190 HashtablezInfo* sample = SampleSlow(&next_sample);
00191 EXPECT_GT(next_sample, 0);
00192 EXPECT_NE(sample, nullptr);
00193 UnsampleSlow(sample);
00194 }
00195 }
00196
00197 TEST(HashtablezSamplerTest, Sample) {
00198 SetHashtablezEnabled(true);
00199 SetHashtablezSampleParameter(100);
00200 int64_t num_sampled = 0;
00201 int64_t total = 0;
00202 double sample_rate;
00203 for (int i = 0; i < 1000000; ++i) {
00204 HashtablezInfoHandle h = Sample();
00205 ++total;
00206 if (HashtablezInfoHandlePeer::IsSampled(h)) {
00207 ++num_sampled;
00208 }
00209 sample_rate = static_cast<double>(num_sampled) / total;
00210 if (0.005 < sample_rate && sample_rate < 0.015) break;
00211 }
00212 EXPECT_NEAR(sample_rate, 0.01, 0.005);
00213 }
00214
00215 TEST(HashtablezSamplerTest, Handle) {
00216 auto& sampler = HashtablezSampler::Global();
00217 HashtablezInfoHandle h(sampler.Register());
00218 auto* info = HashtablezInfoHandlePeer::GetInfo(&h);
00219 info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed);
00220
00221 bool found = false;
00222 sampler.Iterate([&](const HashtablezInfo& h) {
00223 if (&h == info) {
00224 EXPECT_EQ(h.hashes_bitwise_and.load(), 0x12345678);
00225 found = true;
00226 }
00227 });
00228 EXPECT_TRUE(found);
00229
00230 h = HashtablezInfoHandle();
00231 found = false;
00232 sampler.Iterate([&](const HashtablezInfo& h) {
00233 if (&h == info) {
00234
00235
00236 if (h.hashes_bitwise_and.load() == 0x12345678) {
00237 found = true;
00238 }
00239 }
00240 });
00241 EXPECT_FALSE(found);
00242 }
00243
00244 TEST(HashtablezSamplerTest, Registration) {
00245 HashtablezSampler sampler;
00246 auto* info1 = Register(&sampler, 1);
00247 EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1));
00248
00249 auto* info2 = Register(&sampler, 2);
00250 EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2));
00251 info1->size.store(3);
00252 EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2));
00253
00254 sampler.Unregister(info1);
00255 sampler.Unregister(info2);
00256 }
00257
00258 TEST(HashtablezSamplerTest, Unregistration) {
00259 HashtablezSampler sampler;
00260 std::vector<HashtablezInfo*> infos;
00261 for (size_t i = 0; i < 3; ++i) {
00262 infos.push_back(Register(&sampler, i));
00263 }
00264 EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2));
00265
00266 sampler.Unregister(infos[1]);
00267 EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2));
00268
00269 infos.push_back(Register(&sampler, 3));
00270 infos.push_back(Register(&sampler, 4));
00271 EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4));
00272 sampler.Unregister(infos[3]);
00273 EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4));
00274
00275 sampler.Unregister(infos[0]);
00276 sampler.Unregister(infos[2]);
00277 sampler.Unregister(infos[4]);
00278 EXPECT_THAT(GetSizes(&sampler), IsEmpty());
00279 }
00280
00281 TEST(HashtablezSamplerTest, MultiThreaded) {
00282 HashtablezSampler sampler;
00283 Notification stop;
00284 ThreadPool pool(10);
00285
00286 for (int i = 0; i < 10; ++i) {
00287 pool.Schedule([&sampler, &stop]() {
00288 std::random_device rd;
00289 std::mt19937 gen(rd());
00290
00291 std::vector<HashtablezInfo*> infoz;
00292 while (!stop.HasBeenNotified()) {
00293 if (infoz.empty()) {
00294 infoz.push_back(sampler.Register());
00295 }
00296 switch (std::uniform_int_distribution<>(0, 2)(gen)) {
00297 case 0: {
00298 infoz.push_back(sampler.Register());
00299 break;
00300 }
00301 case 1: {
00302 size_t p =
00303 std::uniform_int_distribution<>(0, infoz.size() - 1)(gen);
00304 HashtablezInfo* info = infoz[p];
00305 infoz[p] = infoz.back();
00306 infoz.pop_back();
00307 sampler.Unregister(info);
00308 break;
00309 }
00310 case 2: {
00311 absl::Duration oldest = absl::ZeroDuration();
00312 sampler.Iterate([&](const HashtablezInfo& info) {
00313 oldest = std::max(oldest, absl::Now() - info.create_time);
00314 });
00315 ASSERT_GE(oldest, absl::ZeroDuration());
00316 break;
00317 }
00318 }
00319 }
00320 });
00321 }
00322
00323
00324 absl::SleepFor(absl::Seconds(3));
00325 stop.Notify();
00326 }
00327
00328 TEST(HashtablezSamplerTest, Callback) {
00329 HashtablezSampler sampler;
00330
00331 auto* info1 = Register(&sampler, 1);
00332 auto* info2 = Register(&sampler, 2);
00333
00334 static const HashtablezInfo* expected;
00335
00336 auto callback = [](const HashtablezInfo& info) {
00337
00338
00339 EXPECT_EQ(&info, expected);
00340 };
00341
00342
00343 EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
00344 expected = info1;
00345 sampler.Unregister(info1);
00346
00347
00348 EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
00349 expected = nullptr;
00350 sampler.Unregister(info2);
00351 }
00352
00353 }
00354 }
00355 }