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
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 #include "absl/container/flat_hash_map.h"
18 #include "absl/container/flat_hash_set.h"
19 #include "absl/container/node_hash_map.h"
20 #include "absl/container/node_hash_set.h"
21
22 namespace absl {
23 ABSL_NAMESPACE_BEGIN
24 namespace container_internal {
25 namespace {
26
27 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
28 // Create some tables of type `Table`, then look at all the new
29 // `HashtablezInfo`s to make sure that the `inline_element_size ==
30 // expected_element_size`. The `inline_element_size` is the amount of memory
31 // allocated for each slot of a hash table, that is `sizeof(slot_type)`. Add
32 // the new `HashtablezInfo`s to `preexisting_info`. Store all the new tables
33 // into `tables`.
34 template <class Table>
TestInlineElementSize(HashtablezSampler & sampler,std::unordered_set<const HashtablezInfo * > & preexisting_info,std::vector<Table> & tables,const typename Table::value_type & elt,size_t expected_element_size)35 void TestInlineElementSize(
36 HashtablezSampler& sampler,
37 // clang-tidy gives a false positive on this declaration. This unordered
38 // set cannot be flat_hash_set, however, since that would introduce a mutex
39 // deadlock.
40 std::unordered_set<const HashtablezInfo*>& preexisting_info, // NOLINT
41 std::vector<Table>& tables, const typename Table::value_type& elt,
42 size_t expected_element_size) {
43 for (int i = 0; i < 10; ++i) {
44 // We create a new table and must store it somewhere so that when we store
45 // a pointer to the resulting `HashtablezInfo` into `preexisting_info`
46 // that we aren't storing a dangling pointer.
47 tables.emplace_back();
48 // We must insert an element to get a hashtablez to instantiate.
49 tables.back().insert(elt);
50 }
51 size_t new_count = 0;
52 sampler.Iterate([&](const HashtablezInfo& info) {
53 if (preexisting_info.insert(&info).second) {
54 EXPECT_EQ(info.inline_element_size, expected_element_size);
55 ++new_count;
56 }
57 });
58 // Make sure we actually did get a new hashtablez.
59 EXPECT_GT(new_count, 0);
60 }
61
62 struct bigstruct {
63 char a[1000];
operator ==(const bigstruct & x,const bigstruct & y)64 friend bool operator==(const bigstruct& x, const bigstruct& y) {
65 return memcmp(x.a, y.a, sizeof(x.a)) == 0;
66 }
67 template <typename H>
AbslHashValue(H h,const bigstruct & c)68 friend H AbslHashValue(H h, const bigstruct& c) {
69 return H::combine_contiguous(std::move(h), c.a, sizeof(c.a));
70 }
71 };
72 #endif
73
TEST(FlatHashMap,SampleElementSize)74 TEST(FlatHashMap, SampleElementSize) {
75 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
76 // Enable sampling even if the prod default is off.
77 SetHashtablezEnabled(true);
78 SetHashtablezSampleParameter(1);
79
80 auto& sampler = GlobalHashtablezSampler();
81 std::vector<flat_hash_map<int, bigstruct>> flat_map_tables;
82 std::vector<flat_hash_set<bigstruct>> flat_set_tables;
83 std::vector<node_hash_map<int, bigstruct>> node_map_tables;
84 std::vector<node_hash_set<bigstruct>> node_set_tables;
85
86 // It takes thousands of new tables after changing the sampling parameters
87 // before you actually get some instrumentation. And if you must actually
88 // put something into those tables.
89 for (int i = 0; i < 10000; ++i) {
90 flat_map_tables.emplace_back();
91 flat_map_tables.back()[i] = bigstruct{};
92 }
93
94 // clang-tidy gives a false positive on this declaration. This unordered set
95 // cannot be a flat_hash_set, however, since that would introduce a mutex
96 // deadlock.
97 std::unordered_set<const HashtablezInfo*> preexisting_info; // NOLINT
98 sampler.Iterate(
99 [&](const HashtablezInfo& info) { preexisting_info.insert(&info); });
100 TestInlineElementSize(sampler, preexisting_info, flat_map_tables,
101 {0, bigstruct{}}, sizeof(int) + sizeof(bigstruct));
102 TestInlineElementSize(sampler, preexisting_info, node_map_tables,
103 {0, bigstruct{}}, sizeof(void*));
104 TestInlineElementSize(sampler, preexisting_info, flat_set_tables, //
105 bigstruct{}, sizeof(bigstruct));
106 TestInlineElementSize(sampler, preexisting_info, node_set_tables, //
107 bigstruct{}, sizeof(void*));
108 #endif
109 }
110
111 } // namespace
112 } // namespace container_internal
113 ABSL_NAMESPACE_END
114 } // namespace absl
115