Use `AddConstRefIfExpensiveToCopy` and `ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS` for hash/eq functor members in SwissTable helpers. HashElement, HashKey, and EqualElement previously stored `const Hash&` and `const KeyEqual&` references. Even for empty stateless functors (such as absl::Hash) or small trivially copyable stateful functors, those reference members occupied 8 bytes, forced callers like find_or_prepare_insert_small() to materialize and store `&this->hash_` on the stack inside HashKey, and placed HashKey::key at offset 8 instead of offset 0 in HashKey::GetHash(). Store hash/eq as `ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS type_traits_internal::AddConstRefIfExpensiveToCopy` so empty functors occupy no space and small trivial functors are passed and stored by value without pointer indirection. PiperOrigin-RevId: 1009828336
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 351fc22..4bb6fec 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel
@@ -440,6 +440,7 @@ ], deps = [ "//absl/base:config", + "//absl/base:core_headers", "//absl/hash", "//absl/memory", "//absl/meta:type_traits",
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 5b33ebf..903b308 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt
@@ -490,6 +490,7 @@ ${ABSL_DEFAULT_COPTS} DEPS absl::config + absl::core_headers absl::hash absl::memory absl::type_traits
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index 065d97a..3d16a68 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h
@@ -25,6 +25,7 @@ #include <type_traits> #include <utility> +#include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/hash/hash.h" #include "absl/memory/memory.h" @@ -569,7 +570,9 @@ // Useful for usage with policy traits. template <class Hash, bool kIsAbsl, size_t kSeedShift> struct HashElement { - HashElement(const Hash& h, size_t s) : hash(h), seed(s >> kSeedShift) {} + HashElement(type_traits_internal::AddConstRefIfExpensiveToCopy<Hash> h, + size_t s) + : hash(h), seed(s >> kSeedShift) {} template <class K, class... Args> size_t operator()(const K& key, Args&&...) const { @@ -582,9 +585,8 @@ // NOLINTNEXTLINE(clang-diagnostic-sign-conversion) return hash(key) ^ seed; } - // TODO(ezb): try using AddConstRefIfExpensiveToCopy and - // ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS for `hash`/`eq` functor members. - const Hash& hash; + ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS + type_traits_internal::AddConstRefIfExpensiveToCopy<Hash> hash; size_t seed; }; @@ -599,7 +601,8 @@ static size_t GetHash(void* data, size_t seed) { return (*static_cast<const HashKey*>(data))(seed); } - const Hash& hash; + ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS + type_traits_internal::AddConstRefIfExpensiveToCopy<Hash> hash; type_traits_internal::AddConstRefIfExpensiveToCopy<Key> key; }; @@ -612,7 +615,8 @@ ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(eq(lhs, rhs)); } type_traits_internal::AddConstRefIfExpensiveToCopy<K1> rhs; - const KeyEqual& eq; + ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS + type_traits_internal::AddConstRefIfExpensiveToCopy<KeyEqual> eq; }; // Type erased function for computing hash of the slot.
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index b31af8f..4c33b4f 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc
@@ -4847,18 +4847,20 @@ TEST(Table, AllocatorPropagation) { TestAllocPropagation<RawHashSetAlloc>(); } struct CountedHash { + explicit CountedHash(int* count) : count(count) {} size_t operator()(int64_t value) const { - ++count; + ++(*count); return static_cast<size_t>(value); } - mutable int count = 0; + int* count = nullptr; }; struct CountedHashIntTable : raw_hash_set<IntPolicy, CountedHash, std::equal_to<int>, std::allocator<int>> { using Base = typename CountedHashIntTable::raw_hash_set; - using Base::Base; + CountedHashIntTable() : Base(0, CountedHash(&count)) {} + int count = 0; }; TEST(Table, CountedHash) { @@ -4876,7 +4878,7 @@ DisableSampling(); using Table = CountedHashIntTable; - auto HashCount = [](const Table& t) { return t.hash_function().count; }; + auto HashCount = [](const Table& t) { return t.count; }; { Table t; t.find(0);