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