hashtablez_sampler_test.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. // https://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. #include "absl/container/internal/hashtablez_sampler.h"
  15. #include <atomic>
  16. #include <limits>
  17. #include <random>
  18. #include "gmock/gmock.h"
  19. #include "gtest/gtest.h"
  20. #include "absl/base/attributes.h"
  21. #include "absl/container/internal/have_sse.h"
  22. #include "absl/profiling/internal/sample_recorder.h"
  23. #include "absl/synchronization/blocking_counter.h"
  24. #include "absl/synchronization/internal/thread_pool.h"
  25. #include "absl/synchronization/mutex.h"
  26. #include "absl/synchronization/notification.h"
  27. #include "absl/time/clock.h"
  28. #include "absl/time/time.h"
  29. #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
  30. constexpr int kProbeLength = 16;
  31. #else
  32. constexpr int kProbeLength = 8;
  33. #endif
  34. namespace absl {
  35. ABSL_NAMESPACE_BEGIN
  36. namespace container_internal {
  37. #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  38. class HashtablezInfoHandlePeer {
  39. public:
  40. static bool IsSampled(const HashtablezInfoHandle& h) {
  41. return h.info_ != nullptr;
  42. }
  43. static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; }
  44. };
  45. #else
  46. class HashtablezInfoHandlePeer {
  47. public:
  48. static bool IsSampled(const HashtablezInfoHandle&) { return false; }
  49. static HashtablezInfo* GetInfo(HashtablezInfoHandle*) { return nullptr; }
  50. };
  51. #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  52. namespace {
  53. using ::absl::synchronization_internal::ThreadPool;
  54. using ::testing::IsEmpty;
  55. using ::testing::UnorderedElementsAre;
  56. std::vector<size_t> GetSizes(HashtablezSampler* s) {
  57. std::vector<size_t> res;
  58. s->Iterate([&](const HashtablezInfo& info) {
  59. res.push_back(info.size.load(std::memory_order_acquire));
  60. });
  61. return res;
  62. }
  63. HashtablezInfo* Register(HashtablezSampler* s, size_t size) {
  64. auto* info = s->Register();
  65. assert(info != nullptr);
  66. info->size.store(size);
  67. return info;
  68. }
  69. TEST(HashtablezInfoTest, PrepareForSampling) {
  70. absl::Time test_start = absl::Now();
  71. const size_t test_element_size = 17;
  72. HashtablezInfo info;
  73. absl::MutexLock l(&info.init_mu);
  74. info.PrepareForSampling();
  75. info.inline_element_size = test_element_size;
  76. EXPECT_EQ(info.capacity.load(), 0);
  77. EXPECT_EQ(info.size.load(), 0);
  78. EXPECT_EQ(info.num_erases.load(), 0);
  79. EXPECT_EQ(info.num_rehashes.load(), 0);
  80. EXPECT_EQ(info.max_probe_length.load(), 0);
  81. EXPECT_EQ(info.total_probe_length.load(), 0);
  82. EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
  83. EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
  84. EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
  85. EXPECT_EQ(info.max_reserve.load(), 0);
  86. EXPECT_GE(info.create_time, test_start);
  87. EXPECT_EQ(info.inline_element_size, test_element_size);
  88. info.capacity.store(1, std::memory_order_relaxed);
  89. info.size.store(1, std::memory_order_relaxed);
  90. info.num_erases.store(1, std::memory_order_relaxed);
  91. info.max_probe_length.store(1, std::memory_order_relaxed);
  92. info.total_probe_length.store(1, std::memory_order_relaxed);
  93. info.hashes_bitwise_or.store(1, std::memory_order_relaxed);
  94. info.hashes_bitwise_and.store(1, std::memory_order_relaxed);
  95. info.hashes_bitwise_xor.store(1, std::memory_order_relaxed);
  96. info.max_reserve.store(1, std::memory_order_relaxed);
  97. info.create_time = test_start - absl::Hours(20);
  98. info.PrepareForSampling();
  99. EXPECT_EQ(info.capacity.load(), 0);
  100. EXPECT_EQ(info.size.load(), 0);
  101. EXPECT_EQ(info.num_erases.load(), 0);
  102. EXPECT_EQ(info.num_rehashes.load(), 0);
  103. EXPECT_EQ(info.max_probe_length.load(), 0);
  104. EXPECT_EQ(info.total_probe_length.load(), 0);
  105. EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
  106. EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
  107. EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
  108. EXPECT_EQ(info.max_reserve.load(), 0);
  109. EXPECT_EQ(info.inline_element_size, test_element_size);
  110. EXPECT_GE(info.create_time, test_start);
  111. }
  112. TEST(HashtablezInfoTest, RecordStorageChanged) {
  113. HashtablezInfo info;
  114. absl::MutexLock l(&info.init_mu);
  115. info.PrepareForSampling();
  116. RecordStorageChangedSlow(&info, 17, 47);
  117. EXPECT_EQ(info.size.load(), 17);
  118. EXPECT_EQ(info.capacity.load(), 47);
  119. RecordStorageChangedSlow(&info, 20, 20);
  120. EXPECT_EQ(info.size.load(), 20);
  121. EXPECT_EQ(info.capacity.load(), 20);
  122. }
  123. TEST(HashtablezInfoTest, RecordInsert) {
  124. HashtablezInfo info;
  125. absl::MutexLock l(&info.init_mu);
  126. info.PrepareForSampling();
  127. EXPECT_EQ(info.max_probe_length.load(), 0);
  128. RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
  129. EXPECT_EQ(info.max_probe_length.load(), 6);
  130. EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00);
  131. EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00);
  132. EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x0000FF00);
  133. RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength);
  134. EXPECT_EQ(info.max_probe_length.load(), 6);
  135. EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000);
  136. EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00);
  137. EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x000F0F00);
  138. RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength);
  139. EXPECT_EQ(info.max_probe_length.load(), 12);
  140. EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000);
  141. EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00);
  142. EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x00F00F00);
  143. }
  144. TEST(HashtablezInfoTest, RecordErase) {
  145. const size_t test_element_size = 29;
  146. HashtablezInfo info;
  147. absl::MutexLock l(&info.init_mu);
  148. info.PrepareForSampling();
  149. info.inline_element_size = test_element_size;
  150. EXPECT_EQ(info.num_erases.load(), 0);
  151. EXPECT_EQ(info.size.load(), 0);
  152. RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
  153. EXPECT_EQ(info.size.load(), 1);
  154. RecordEraseSlow(&info);
  155. EXPECT_EQ(info.size.load(), 0);
  156. EXPECT_EQ(info.num_erases.load(), 1);
  157. EXPECT_EQ(info.inline_element_size, test_element_size);
  158. }
  159. TEST(HashtablezInfoTest, RecordRehash) {
  160. const size_t test_element_size = 31;
  161. HashtablezInfo info;
  162. absl::MutexLock l(&info.init_mu);
  163. info.PrepareForSampling();
  164. info.inline_element_size = test_element_size;
  165. RecordInsertSlow(&info, 0x1, 0);
  166. RecordInsertSlow(&info, 0x2, kProbeLength);
  167. RecordInsertSlow(&info, 0x4, kProbeLength);
  168. RecordInsertSlow(&info, 0x8, 2 * kProbeLength);
  169. EXPECT_EQ(info.size.load(), 4);
  170. EXPECT_EQ(info.total_probe_length.load(), 4);
  171. RecordEraseSlow(&info);
  172. RecordEraseSlow(&info);
  173. EXPECT_EQ(info.size.load(), 2);
  174. EXPECT_EQ(info.total_probe_length.load(), 4);
  175. EXPECT_EQ(info.num_erases.load(), 2);
  176. RecordRehashSlow(&info, 3 * kProbeLength);
  177. EXPECT_EQ(info.size.load(), 2);
  178. EXPECT_EQ(info.total_probe_length.load(), 3);
  179. EXPECT_EQ(info.num_erases.load(), 0);
  180. EXPECT_EQ(info.num_rehashes.load(), 1);
  181. EXPECT_EQ(info.inline_element_size, test_element_size);
  182. }
  183. TEST(HashtablezInfoTest, RecordReservation) {
  184. HashtablezInfo info;
  185. absl::MutexLock l(&info.init_mu);
  186. info.PrepareForSampling();
  187. RecordReservationSlow(&info, 3);
  188. EXPECT_EQ(info.max_reserve.load(), 3);
  189. RecordReservationSlow(&info, 2);
  190. // High watermark does not change
  191. EXPECT_EQ(info.max_reserve.load(), 3);
  192. RecordReservationSlow(&info, 10);
  193. // High watermark does change
  194. EXPECT_EQ(info.max_reserve.load(), 10);
  195. }
  196. #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  197. TEST(HashtablezSamplerTest, SmallSampleParameter) {
  198. const size_t test_element_size = 31;
  199. SetHashtablezEnabled(true);
  200. SetHashtablezSampleParameter(100);
  201. for (int i = 0; i < 1000; ++i) {
  202. int64_t next_sample = 0;
  203. HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
  204. EXPECT_GT(next_sample, 0);
  205. EXPECT_NE(sample, nullptr);
  206. UnsampleSlow(sample);
  207. }
  208. }
  209. TEST(HashtablezSamplerTest, LargeSampleParameter) {
  210. const size_t test_element_size = 31;
  211. SetHashtablezEnabled(true);
  212. SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
  213. for (int i = 0; i < 1000; ++i) {
  214. int64_t next_sample = 0;
  215. HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size);
  216. EXPECT_GT(next_sample, 0);
  217. EXPECT_NE(sample, nullptr);
  218. UnsampleSlow(sample);
  219. }
  220. }
  221. TEST(HashtablezSamplerTest, Sample) {
  222. const size_t test_element_size = 31;
  223. SetHashtablezEnabled(true);
  224. SetHashtablezSampleParameter(100);
  225. int64_t num_sampled = 0;
  226. int64_t total = 0;
  227. double sample_rate = 0.0;
  228. for (int i = 0; i < 1000000; ++i) {
  229. HashtablezInfoHandle h = Sample(test_element_size);
  230. ++total;
  231. if (HashtablezInfoHandlePeer::IsSampled(h)) {
  232. ++num_sampled;
  233. }
  234. sample_rate = static_cast<double>(num_sampled) / total;
  235. if (0.005 < sample_rate && sample_rate < 0.015) break;
  236. }
  237. EXPECT_NEAR(sample_rate, 0.01, 0.005);
  238. }
  239. TEST(HashtablezSamplerTest, Handle) {
  240. auto& sampler = GlobalHashtablezSampler();
  241. HashtablezInfoHandle h(sampler.Register());
  242. auto* info = HashtablezInfoHandlePeer::GetInfo(&h);
  243. info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed);
  244. bool found = false;
  245. sampler.Iterate([&](const HashtablezInfo& h) {
  246. if (&h == info) {
  247. EXPECT_EQ(h.hashes_bitwise_and.load(), 0x12345678);
  248. found = true;
  249. }
  250. });
  251. EXPECT_TRUE(found);
  252. h = HashtablezInfoHandle();
  253. found = false;
  254. sampler.Iterate([&](const HashtablezInfo& h) {
  255. if (&h == info) {
  256. // this will only happen if some other thread has resurrected the info
  257. // the old handle was using.
  258. if (h.hashes_bitwise_and.load() == 0x12345678) {
  259. found = true;
  260. }
  261. }
  262. });
  263. EXPECT_FALSE(found);
  264. }
  265. #endif
  266. TEST(HashtablezSamplerTest, Registration) {
  267. HashtablezSampler sampler;
  268. auto* info1 = Register(&sampler, 1);
  269. EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1));
  270. auto* info2 = Register(&sampler, 2);
  271. EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2));
  272. info1->size.store(3);
  273. EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2));
  274. sampler.Unregister(info1);
  275. sampler.Unregister(info2);
  276. }
  277. TEST(HashtablezSamplerTest, Unregistration) {
  278. HashtablezSampler sampler;
  279. std::vector<HashtablezInfo*> infos;
  280. for (size_t i = 0; i < 3; ++i) {
  281. infos.push_back(Register(&sampler, i));
  282. }
  283. EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2));
  284. sampler.Unregister(infos[1]);
  285. EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2));
  286. infos.push_back(Register(&sampler, 3));
  287. infos.push_back(Register(&sampler, 4));
  288. EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4));
  289. sampler.Unregister(infos[3]);
  290. EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4));
  291. sampler.Unregister(infos[0]);
  292. sampler.Unregister(infos[2]);
  293. sampler.Unregister(infos[4]);
  294. EXPECT_THAT(GetSizes(&sampler), IsEmpty());
  295. }
  296. TEST(HashtablezSamplerTest, MultiThreaded) {
  297. HashtablezSampler sampler;
  298. Notification stop;
  299. ThreadPool pool(10);
  300. for (int i = 0; i < 10; ++i) {
  301. pool.Schedule([&sampler, &stop]() {
  302. std::random_device rd;
  303. std::mt19937 gen(rd());
  304. std::vector<HashtablezInfo*> infoz;
  305. while (!stop.HasBeenNotified()) {
  306. if (infoz.empty()) {
  307. infoz.push_back(sampler.Register());
  308. }
  309. switch (std::uniform_int_distribution<>(0, 2)(gen)) {
  310. case 0: {
  311. infoz.push_back(sampler.Register());
  312. break;
  313. }
  314. case 1: {
  315. size_t p =
  316. std::uniform_int_distribution<>(0, infoz.size() - 1)(gen);
  317. HashtablezInfo* info = infoz[p];
  318. infoz[p] = infoz.back();
  319. infoz.pop_back();
  320. sampler.Unregister(info);
  321. break;
  322. }
  323. case 2: {
  324. absl::Duration oldest = absl::ZeroDuration();
  325. sampler.Iterate([&](const HashtablezInfo& info) {
  326. oldest = std::max(oldest, absl::Now() - info.create_time);
  327. });
  328. ASSERT_GE(oldest, absl::ZeroDuration());
  329. break;
  330. }
  331. }
  332. }
  333. });
  334. }
  335. // The threads will hammer away. Give it a little bit of time for tsan to
  336. // spot errors.
  337. absl::SleepFor(absl::Seconds(3));
  338. stop.Notify();
  339. }
  340. TEST(HashtablezSamplerTest, Callback) {
  341. HashtablezSampler sampler;
  342. auto* info1 = Register(&sampler, 1);
  343. auto* info2 = Register(&sampler, 2);
  344. static const HashtablezInfo* expected;
  345. auto callback = [](const HashtablezInfo& info) {
  346. // We can't use `info` outside of this callback because the object will be
  347. // disposed as soon as we return from here.
  348. EXPECT_EQ(&info, expected);
  349. };
  350. // Set the callback.
  351. EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
  352. expected = info1;
  353. sampler.Unregister(info1);
  354. // Unset the callback.
  355. EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
  356. expected = nullptr; // no more calls.
  357. sampler.Unregister(info2);
  358. }
  359. } // namespace
  360. } // namespace container_internal
  361. ABSL_NAMESPACE_END
  362. } // namespace absl