hashtablez_sampler_test.cc
Go to the documentation of this file.
00001 // Copyright 2018 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
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       // this will only happen if some other thread has resurrected the info
00235       // the old handle was using.
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   // The threads will hammer away.  Give it a little bit of time for tsan to
00323   // spot errors.
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     // We can't use `info` outside of this callback because the object will be
00338     // disposed as soon as we return from here.
00339     EXPECT_EQ(&info, expected);
00340   };
00341 
00342   // Set the callback.
00343   EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
00344   expected = info1;
00345   sampler.Unregister(info1);
00346 
00347   // Unset the callback.
00348   EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
00349   expected = nullptr;  // no more calls.
00350   sampler.Unregister(info2);
00351 }
00352 
00353 }  // namespace
00354 }  // namespace container_internal
00355 }  // namespace absl


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:14