// Copyright 2018 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/container/node_hash_map.h" #include "absl/container/node_hash_set.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) // Create some tables of type `Table`, then look at all the new // `HashtablezInfo`s to make sure that the `inline_element_size == // expected_element_size`. The `inline_element_size` is the amount of memory // allocated for each slot of a hash table, that is `sizeof(slot_type)`. Add // the new `HashtablezInfo`s to `preexisting_info`. Store all the new tables // into `tables`. template void TestInlineElementSize( HashtablezSampler& sampler, // clang-tidy gives a false positive on this declaration. This unordered // set cannot be flat_hash_set, however, since that would introduce a mutex // deadlock. std::unordered_set& preexisting_info, // NOLINT std::vector& tables, const typename Table::value_type& elt, size_t expected_element_size) { for (int i = 0; i < 10; ++i) { // We create a new table and must store it somewhere so that when we store // a pointer to the resulting `HashtablezInfo` into `preexisting_info` // that we aren't storing a dangling pointer. tables.emplace_back(); // We must insert an element to get a hashtablez to instantiate. tables.back().insert(elt); } size_t new_count = 0; sampler.Iterate([&](const HashtablezInfo& info) { if (preexisting_info.insert(&info).second) { EXPECT_EQ(info.inline_element_size, expected_element_size); ++new_count; } }); // Make sure we actually did get a new hashtablez. EXPECT_GT(new_count, 0); } struct bigstruct { char a[1000]; friend bool operator==(const bigstruct& x, const bigstruct& y) { return memcmp(x.a, y.a, sizeof(x.a)) == 0; } template friend H AbslHashValue(H h, const bigstruct& c) { return H::combine_contiguous(std::move(h), c.a, sizeof(c.a)); } }; #endif TEST(FlatHashMap, SampleElementSize) { #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) // Enable sampling even if the prod default is off. SetHashtablezEnabled(true); SetHashtablezSampleParameter(1); auto& sampler = GlobalHashtablezSampler(); std::vector> flat_map_tables; std::vector> flat_set_tables; std::vector> node_map_tables; std::vector> node_set_tables; // It takes thousands of new tables after changing the sampling parameters // before you actually get some instrumentation. And if you must actually // put something into those tables. for (int i = 0; i < 10000; ++i) { flat_map_tables.emplace_back(); flat_map_tables.back()[i] = bigstruct{}; } // clang-tidy gives a false positive on this declaration. This unordered set // cannot be a flat_hash_set, however, since that would introduce a mutex // deadlock. std::unordered_set preexisting_info; // NOLINT sampler.Iterate( [&](const HashtablezInfo& info) { preexisting_info.insert(&info); }); TestInlineElementSize(sampler, preexisting_info, flat_map_tables, {0, bigstruct{}}, sizeof(int) + sizeof(bigstruct)); TestInlineElementSize(sampler, preexisting_info, node_map_tables, {0, bigstruct{}}, sizeof(void*)); TestInlineElementSize(sampler, preexisting_info, flat_set_tables, // bigstruct{}, sizeof(bigstruct)); TestInlineElementSize(sampler, preexisting_info, node_set_tables, // bigstruct{}, sizeof(void*)); #endif } } // namespace } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl