Commit db5c7993 by Abseil Team Committed by Copybara-Service

Make `FlatHashMapPolicy` return `std::true_type` for relocatable objects.

This reduces produced binary size and can trigger even more optimizations in the future.

PiperOrigin-RevId: 584136517
Change-Id: I3854833799f88f28b755ec53132925f0c3d468ab
parent f393335c
...@@ -268,6 +268,7 @@ cc_test( ...@@ -268,6 +268,7 @@ cc_test(
":unordered_map_members_test", ":unordered_map_members_test",
":unordered_map_modifiers_test", ":unordered_map_modifiers_test",
"//absl/log:check", "//absl/log:check",
"//absl/meta:type_traits",
"//absl/types:any", "//absl/types:any",
"@com_google_googletest//:gtest", "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
...@@ -401,6 +402,7 @@ cc_test( ...@@ -401,6 +402,7 @@ cc_test(
deps = [ deps = [
":container_memory", ":container_memory",
":test_instance_tracker", ":test_instance_tracker",
"//absl/meta:type_traits",
"//absl/strings", "//absl/strings",
"@com_google_googletest//:gtest", "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",
......
...@@ -305,6 +305,7 @@ absl_cc_test( ...@@ -305,6 +305,7 @@ absl_cc_test(
absl::check absl::check
absl::flat_hash_map absl::flat_hash_map
absl::hash_generator_testing absl::hash_generator_testing
absl::type_traits
absl::unordered_map_constructor_test absl::unordered_map_constructor_test
absl::unordered_map_lookup_test absl::unordered_map_lookup_test
absl::unordered_map_members_test absl::unordered_map_members_test
...@@ -450,6 +451,7 @@ absl_cc_test( ...@@ -450,6 +451,7 @@ absl_cc_test(
absl::container_memory absl::container_memory
absl::strings absl::strings
absl::test_instance_tracker absl::test_instance_tracker
absl::type_traits
GTest::gmock_main GTest::gmock_main
) )
......
...@@ -579,9 +579,9 @@ struct FlatHashMapPolicy { ...@@ -579,9 +579,9 @@ struct FlatHashMapPolicy {
} }
template <class Allocator> template <class Allocator>
static void transfer(Allocator* alloc, slot_type* new_slot, static auto transfer(Allocator* alloc, slot_type* new_slot,
slot_type* old_slot) { slot_type* old_slot) {
slot_policy::transfer(alloc, new_slot, old_slot); return slot_policy::transfer(alloc, new_slot, old_slot);
} }
template <class F, class... Args> template <class F, class... Args>
......
...@@ -14,14 +14,20 @@ ...@@ -14,14 +14,20 @@
#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_map.h"
#include <cstddef>
#include <memory> #include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/hash_generator_testing.h"
#include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_constructor_test.h"
#include "absl/container/internal/unordered_map_lookup_test.h" #include "absl/container/internal/unordered_map_lookup_test.h"
#include "absl/container/internal/unordered_map_members_test.h" #include "absl/container/internal/unordered_map_members_test.h"
#include "absl/container/internal/unordered_map_modifiers_test.h" #include "absl/container/internal/unordered_map_modifiers_test.h"
#include "absl/log/check.h" #include "absl/log/check.h"
#include "absl/meta/type_traits.h"
#include "absl/types/any.h" #include "absl/types/any.h"
namespace absl { namespace absl {
...@@ -102,6 +108,34 @@ TEST(FlatHashMap, StandardLayout) { ...@@ -102,6 +108,34 @@ TEST(FlatHashMap, StandardLayout) {
} }
} }
TEST(FlatHashMap, Relocatability) {
static_assert(absl::is_trivially_relocatable<int>::value, "");
static_assert(
absl::is_trivially_relocatable<std::pair<const int, int>>::value, "");
static_assert(
std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
int, int>::transfer<std::allocator<char>>(nullptr,
nullptr,
nullptr)),
std::true_type>::value,
"");
struct NonRelocatable {
NonRelocatable() = default;
NonRelocatable(NonRelocatable&&) {}
NonRelocatable& operator=(NonRelocatable&&) { return *this; }
void* self = nullptr;
};
EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
EXPECT_TRUE(
(std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
int, NonRelocatable>::
transfer<std::allocator<char>>(nullptr, nullptr,
nullptr)),
std::false_type>::value));
}
// gcc becomes unhappy if this is inside the method, so pull it out here. // gcc becomes unhappy if this is inside the method, so pull it out here.
struct balast {}; struct balast {};
...@@ -150,9 +184,7 @@ struct Hash { ...@@ -150,9 +184,7 @@ struct Hash {
struct Eq { struct Eq {
using is_transparent = void; using is_transparent = void;
bool operator()(size_t lhs, size_t rhs) const { bool operator()(size_t lhs, size_t rhs) const { return lhs == rhs; }
return lhs == rhs;
}
bool operator()(size_t lhs, const LazyInt& rhs) const { bool operator()(size_t lhs, const LazyInt& rhs) const {
return lhs == rhs.value; return lhs == rhs.value;
} }
......
...@@ -122,10 +122,10 @@ auto TupleRefImpl(T&& t, absl::index_sequence<Is...>) ...@@ -122,10 +122,10 @@ auto TupleRefImpl(T&& t, absl::index_sequence<Is...>)
// Returns a tuple of references to the elements of the input tuple. T must be a // Returns a tuple of references to the elements of the input tuple. T must be a
// tuple. // tuple.
template <class T> template <class T>
auto TupleRef(T&& t) -> decltype( auto TupleRef(T&& t) -> decltype(TupleRefImpl(
TupleRefImpl(std::forward<T>(t), std::forward<T>(t),
absl::make_index_sequence< absl::make_index_sequence<
std::tuple_size<typename std::decay<T>::type>::value>())) { std::tuple_size<typename std::decay<T>::type>::value>())) {
return TupleRefImpl( return TupleRefImpl(
std::forward<T>(t), std::forward<T>(t),
absl::make_index_sequence< absl::make_index_sequence<
...@@ -156,8 +156,8 @@ void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { ...@@ -156,8 +156,8 @@ void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) {
// Constructs T using the args specified in the tuple and calls F with the // Constructs T using the args specified in the tuple and calls F with the
// constructed value. // constructed value.
template <class T, class Tuple, class F> template <class T, class Tuple, class F>
decltype(std::declval<F>()(std::declval<T>())) WithConstructed( decltype(std::declval<F>()(std::declval<T>())) WithConstructed(Tuple&& t,
Tuple&& t, F&& f) { F&& f) {
return memory_internal::WithConstructedImpl<T>( return memory_internal::WithConstructedImpl<T>(
std::forward<Tuple>(t), std::forward<Tuple>(t),
absl::make_index_sequence< absl::make_index_sequence<
...@@ -423,16 +423,19 @@ struct map_slot_policy { ...@@ -423,16 +423,19 @@ struct map_slot_policy {
} }
template <class Allocator> template <class Allocator>
static void transfer(Allocator* alloc, slot_type* new_slot, static auto transfer(Allocator* alloc, slot_type* new_slot,
slot_type* old_slot) { slot_type* old_slot) {
auto is_relocatable =
typename absl::is_trivially_relocatable<value_type>::type();
emplace(new_slot); emplace(new_slot);
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
if (absl::is_trivially_relocatable<value_type>()) { if (is_relocatable) {
// TODO(b/247130232,b/251814870): remove casts after fixing warnings. // TODO(b/247130232,b/251814870): remove casts after fixing warnings.
std::memcpy(static_cast<void*>(std::launder(&new_slot->value)), std::memcpy(static_cast<void*>(std::launder(&new_slot->value)),
static_cast<const void*>(&old_slot->value), static_cast<const void*>(&old_slot->value),
sizeof(value_type)); sizeof(value_type));
return; return is_relocatable;
} }
#endif #endif
...@@ -444,6 +447,7 @@ struct map_slot_policy { ...@@ -444,6 +447,7 @@ struct map_slot_policy {
std::move(old_slot->value)); std::move(old_slot->value));
} }
destroy(alloc, old_slot); destroy(alloc, old_slot);
return is_relocatable;
} }
}; };
......
...@@ -14,8 +14,11 @@ ...@@ -14,8 +14,11 @@
#include "absl/container/internal/container_memory.h" #include "absl/container/internal/container_memory.h"
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <memory>
#include <tuple> #include <tuple>
#include <type_traits>
#include <typeindex> #include <typeindex>
#include <typeinfo> #include <typeinfo>
#include <utility> #include <utility>
...@@ -23,6 +26,7 @@ ...@@ -23,6 +26,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/container/internal/test_instance_tracker.h" #include "absl/container/internal/test_instance_tracker.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
namespace absl { namespace absl {
...@@ -219,8 +223,7 @@ TEST(DecomposePair, NotDecomposable) { ...@@ -219,8 +223,7 @@ TEST(DecomposePair, NotDecomposable) {
ADD_FAILURE() << "Must not be called"; ADD_FAILURE() << "Must not be called";
return 'A'; return 'A';
}; };
EXPECT_STREQ("not decomposable", EXPECT_STREQ("not decomposable", TryDecomposePair(f));
TryDecomposePair(f));
EXPECT_STREQ("not decomposable", EXPECT_STREQ("not decomposable",
TryDecomposePair(f, std::piecewise_construct, std::make_tuple(), TryDecomposePair(f, std::piecewise_construct, std::make_tuple(),
std::make_tuple(0.5))); std::make_tuple(0.5)));
...@@ -251,6 +254,31 @@ TEST(MapSlotPolicy, ConstKeyAndValue) { ...@@ -251,6 +254,31 @@ TEST(MapSlotPolicy, ConstKeyAndValue) {
EXPECT_EQ(tracker.copies(), 0); EXPECT_EQ(tracker.copies(), 0);
} }
TEST(MapSlotPolicy, TransferReturnsTrue) {
{
using slot_policy = map_slot_policy<int, float>;
EXPECT_TRUE(
(std::is_same<decltype(slot_policy::transfer<std::allocator<char>>(
nullptr, nullptr, nullptr)),
std::true_type>::value));
}
{
struct NonRelocatable {
NonRelocatable() = default;
NonRelocatable(NonRelocatable&&) {}
NonRelocatable& operator=(NonRelocatable&&) { return *this; }
void* self = nullptr;
};
EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
using slot_policy = map_slot_policy<int, NonRelocatable>;
EXPECT_TRUE(
(std::is_same<decltype(slot_policy::transfer<std::allocator<char>>(
nullptr, nullptr, nullptr)),
std::false_type>::value));
}
}
} // namespace } // namespace
} // namespace container_internal } // namespace container_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment