Commit 77e4a6f1 authored by Xiao Shi's avatar Xiao Shi Committed by Facebook Github Bot

inherit from instead of type aliasing in F14 fallback

Summary:
* make F14 containers their own types even on platforms where we fall back to
stl containers.
* extract F14FastMap to be the same conditional typedef.
* define custom swap operators to avoid unnecessary move construct and move
  assignment.

Reviewed By: nbronson

Differential Revision: D7295846

fbshipit-source-id: 92faa2feddc5c85dd17a5dd937004ca1839a09f6
parent 8ee1ac19
...@@ -45,12 +45,12 @@ about 16 bytes per entry on average. ...@@ -45,12 +45,12 @@ about 16 bytes per entry on average.
We also provide: We also provide:
F14FastMap is an alias to F14ValueMap or F14VectorMap depending on F14FastMap inherits from either F14ValueMap or F14VectorMap depending
entry size. When the key and mapped_type are less than 24 bytes it on entry size. When the key and mapped_type are less than 24 bytes, it
typedefs to F14ValueMap. For medium and large entries it typedefs to inherits from F14ValueMap. For medium and large entries, it inherits
F14VectorMap. This strategy provides the best performance, while also from F14VectorMap. This strategy provides the best performance, while
providing better memory efficiency than dense_hash_map or the other hash also providing better memory efficiency than dense_hash_map or the other
tables in use at Facebook that don't individually allocate nodes. hash tables in use at Facebook that don't individually allocate nodes.
## WHICH F14 VARIANT IS RIGHT FOR ME? ## WHICH F14 VARIANT IS RIGHT FOR ME?
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
/** /**
* F14NodeMap, F14ValueMap, and F14VectorMap * F14NodeMap, F14ValueMap, and F14VectorMap
* *
* F14FastMap is a conditional typedef to F14ValueMap or F14VectorMap * F14FastMap conditionally inherits from F14ValueMap or F14VectorMap
* *
* See F14.md * See F14.md
* *
...@@ -43,14 +43,47 @@ ...@@ -43,14 +43,47 @@
namespace folly { namespace folly {
template <typename... Args> template <
using F14NodeMap = std::unordered_map<Args...>; typename K,
template <typename... Args> typename M,
using F14ValueMap = std::unordered_map<Args...>; typename H = std::hash<K>,
template <typename... Args> typename E = std::equal_to<K>,
using F14VectorMap = std::unordered_map<Args...>; typename A = std::allocator<std::pair<K const, M>>>
template <typename... Args> class F14ValueMap : public std::unordered_map<K, M, H, E, A> {
using F14FastMap = std::unordered_map<Args...>; using Super = std::unordered_map<K, M, H, E, A>;
public:
using Super::Super;
F14ValueMap() : Super() {}
};
template <
typename K,
typename M,
typename H = std::hash<K>,
typename E = std::equal_to<K>,
typename A = std::allocator<std::pair<K const, M>>>
class F14NodeMap : public std::unordered_map<K, M, H, E, A> {
using Super = std::unordered_map<K, M, H, E, A>;
public:
using Super::Super;
F14NodeMap() : Super() {}
};
template <
typename K,
typename M,
typename H = std::hash<K>,
typename E = std::equal_to<K>,
typename A = std::allocator<std::pair<K const, M>>>
class F14VectorMap : public std::unordered_map<K, M, H, E, A> {
using Super = std::unordered_map<K, M, H, E, A>;
public:
using Super::Super;
F14VectorMap() : Super() {}
};
} // namespace folly } // namespace folly
...@@ -757,13 +790,6 @@ class F14ValueMap ...@@ -757,13 +790,6 @@ class F14ValueMap
} }
}; };
template <typename K, typename M, typename H, typename E, typename A>
void swap(
F14ValueMap<K, M, H, E, A>& lhs,
F14ValueMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename M, typename H, typename E, typename A> template <typename K, typename M, typename H, typename E, typename A>
bool operator==( bool operator==(
F14ValueMap<K, M, H, E, A> const& lhs, F14ValueMap<K, M, H, E, A> const& lhs,
...@@ -816,13 +842,6 @@ class F14NodeMap ...@@ -816,13 +842,6 @@ class F14NodeMap
// TODO extract and node_handle insert // TODO extract and node_handle insert
}; };
template <typename K, typename M, typename H, typename E, typename A>
void swap(
F14NodeMap<K, M, H, E, A>& lhs,
F14NodeMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename M, typename H, typename E, typename A> template <typename K, typename M, typename H, typename E, typename A>
bool operator==( bool operator==(
F14NodeMap<K, M, H, E, A> const& lhs, F14NodeMap<K, M, H, E, A> const& lhs,
...@@ -953,13 +972,6 @@ class F14VectorMap ...@@ -953,13 +972,6 @@ class F14VectorMap
} }
}; };
template <typename K, typename M, typename H, typename E, typename A>
void swap(
F14VectorMap<K, M, H, E, A>& lhs,
F14VectorMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename M, typename H, typename E, typename A> template <typename K, typename M, typename H, typename E, typename A>
bool operator==( bool operator==(
F14VectorMap<K, M, H, E, A> const& lhs, F14VectorMap<K, M, H, E, A> const& lhs,
...@@ -974,17 +986,58 @@ bool operator!=( ...@@ -974,17 +986,58 @@ bool operator!=(
return !(lhs == rhs); return !(lhs == rhs);
} }
} // namespace folly
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
namespace folly {
template < template <
typename Key, typename Key,
typename Mapped, typename Mapped,
typename Hasher = f14::DefaultHasher<Key>, typename Hasher = f14::DefaultHasher<Key>,
typename KeyEqual = f14::DefaultKeyEqual<Key>, typename KeyEqual = f14::DefaultKeyEqual<Key>,
typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>> typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>>
using F14FastMap = std::conditional_t< class F14FastMap : public std::conditional_t<
sizeof(std::pair<Key const, Mapped>) < 24, sizeof(std::pair<Key const, Mapped>) < 24,
F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>, F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>,
F14VectorMap<Key, Mapped, Hasher, KeyEqual, Alloc>>; F14VectorMap<Key, Mapped, Hasher, KeyEqual, Alloc>> {
using Super = std::conditional_t<
sizeof(std::pair<Key const, Mapped>) < 24,
F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>,
F14VectorMap<Key, Mapped, Hasher, KeyEqual, Alloc>>;
} // namespace folly public:
using Super::Super;
F14FastMap() : Super() {}
};
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE template <typename K, typename M, typename H, typename E, typename A>
void swap(
F14ValueMap<K, M, H, E, A>& lhs,
F14ValueMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename M, typename H, typename E, typename A>
void swap(
F14NodeMap<K, M, H, E, A>& lhs,
F14NodeMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename M, typename H, typename E, typename A>
void swap(
F14VectorMap<K, M, H, E, A>& lhs,
F14VectorMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename M, typename H, typename E, typename A>
void swap(
F14FastMap<K, M, H, E, A>& lhs,
F14FastMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
} // namespace folly
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
/** /**
* F14NodeSet, F14ValueSet, and F14VectorSet * F14NodeSet, F14ValueSet, and F14VectorSet
* *
* F14FastSet is a conditional typedef to F14ValueSet or F14VectorSet * F14FastSet conditionally inherits from F14ValueSet or F14VectorSet
* *
* See F14.md * See F14.md
* *
...@@ -38,14 +38,44 @@ ...@@ -38,14 +38,44 @@
namespace folly { namespace folly {
template <typename... Args> template <
using F14NodeSet = std::unordered_set<Args...>; typename K,
template <typename... Args> typename H = std::hash<K>,
using F14ValueSet = std::unordered_set<Args...>; typename E = std::equal_to<K>,
template <typename... Args> typename A = std::allocator<K>>
using F14VectorSet = std::unordered_set<Args...>; class F14NodeSet : public std::unordered_set<K, H, E, A> {
template <typename... Args> using Super = std::unordered_set<K, H, E, A>;
using F14FastSet = std::unordered_set<Args...>;
public:
using Super::Super;
F14NodeSet() : Super() {}
};
template <
typename K,
typename H = std::hash<K>,
typename E = std::equal_to<K>,
typename A = std::allocator<K>>
class F14ValueSet : public std::unordered_set<K, H, E, A> {
using Super = std::unordered_set<K, H, E, A>;
public:
using Super::Super;
F14ValueSet() : Super() {}
};
template <
typename K,
typename H = std::hash<K>,
typename E = std::equal_to<K>,
typename A = std::allocator<K>>
class F14VectorSet : public std::unordered_set<K, H, E, A> {
using Super = std::unordered_set<K, H, E, A>;
public:
using Super::Super;
F14VectorSet() : Super() {}
};
} // namespace folly } // namespace folly
...@@ -539,12 +569,6 @@ class F14ValueSet ...@@ -539,12 +569,6 @@ class F14ValueSet
} }
}; };
template <typename K, typename H, typename E, typename A>
void swap(F14ValueSet<K, H, E, A>& lhs, F14ValueSet<K, H, E, A>& rhs) noexcept(
noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename H, typename E, typename A> template <typename K, typename H, typename E, typename A>
bool operator==( bool operator==(
F14ValueSet<K, H, E, A> const& lhs, F14ValueSet<K, H, E, A> const& lhs,
...@@ -592,12 +616,6 @@ class F14NodeSet ...@@ -592,12 +616,6 @@ class F14NodeSet
} }
}; };
template <typename K, typename H, typename E, typename A>
void swap(F14NodeSet<K, H, E, A>& lhs, F14NodeSet<K, H, E, A>& rhs) noexcept(
noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename H, typename E, typename A> template <typename K, typename H, typename E, typename A>
bool operator==( bool operator==(
F14NodeSet<K, H, E, A> const& lhs, F14NodeSet<K, H, E, A> const& lhs,
...@@ -720,13 +738,6 @@ class F14VectorSet ...@@ -720,13 +738,6 @@ class F14VectorSet
} }
}; };
template <typename K, typename H, typename E, typename A>
void swap(
F14VectorSet<K, H, E, A>& lhs,
F14VectorSet<K, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename H, typename E, typename A> template <typename K, typename H, typename E, typename A>
bool operator==( bool operator==(
F14VectorSet<K, H, E, A> const& lhs, F14VectorSet<K, H, E, A> const& lhs,
...@@ -740,17 +751,53 @@ bool operator!=( ...@@ -740,17 +751,53 @@ bool operator!=(
F14VectorSet<K, H, E, A> const& rhs) { F14VectorSet<K, H, E, A> const& rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
} // namespace folly
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
namespace folly {
template < template <
typename Key, typename Key,
typename Hasher = f14::DefaultHasher<Key>, typename Hasher = f14::DefaultHasher<Key>,
typename KeyEqual = f14::DefaultKeyEqual<Key>, typename KeyEqual = f14::DefaultKeyEqual<Key>,
typename Alloc = f14::DefaultAlloc<Key>> typename Alloc = f14::DefaultAlloc<Key>>
using F14FastSet = std::conditional_t< class F14FastSet : public std::conditional_t<
sizeof(Key) < 24, sizeof(Key) < 24,
F14ValueSet<Key, Hasher, KeyEqual, Alloc>, F14ValueSet<Key, Hasher, KeyEqual, Alloc>,
F14VectorSet<Key, Hasher, KeyEqual, Alloc>>; F14VectorSet<Key, Hasher, KeyEqual, Alloc>> {
using Super = std::conditional_t<
sizeof(Key) < 24,
F14ValueSet<Key, Hasher, KeyEqual, Alloc>,
F14VectorSet<Key, Hasher, KeyEqual, Alloc>>;
} // namespace folly public:
using Super::Super;
F14FastSet() : Super() {}
};
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE template <typename K, typename H, typename E, typename A>
void swap(F14ValueSet<K, H, E, A>& lhs, F14ValueSet<K, H, E, A>& rhs) noexcept(
noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename H, typename E, typename A>
void swap(F14NodeSet<K, H, E, A>& lhs, F14NodeSet<K, H, E, A>& rhs) noexcept(
noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename H, typename E, typename A>
void swap(
F14VectorSet<K, H, E, A>& lhs,
F14VectorSet<K, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
template <typename K, typename H, typename E, typename A>
void swap(F14FastSet<K, H, E, A>& lhs, F14FastSet<K, H, E, A>& rhs) noexcept(
noexcept(lhs.swap(rhs))) {
lhs.swap(rhs);
}
} // namespace folly
...@@ -862,7 +862,7 @@ class F14Table : public Policy { ...@@ -862,7 +862,7 @@ class F14Table : public Policy {
} }
std::size_t max_size() const noexcept { std::size_t max_size() const noexcept {
allocator_type a = this->alloc(); auto& a = this->alloc();
return std::min<std::size_t>( return std::min<std::size_t>(
(std::numeric_limits<typename Policy::InternalSizeType>::max)(), (std::numeric_limits<typename Policy::InternalSizeType>::max)(),
std::allocator_traits<allocator_type>::max_size(a)); std::allocator_traits<allocator_type>::max_size(a));
......
...@@ -15,6 +15,34 @@ ...@@ -15,6 +15,34 @@
*/ */
#include <folly/container/F14Map.h> #include <folly/container/F14Map.h>
#include <folly/container/test/F14TestUtil.h>
#include <folly/portability/GTest.h>
template <template <typename, typename, typename, typename, typename>
class TMap>
void testCustomSwap() {
using std::swap;
TMap<
int,
int,
folly::f14::DefaultHasher<int>,
folly::f14::DefaultKeyEqual<int>,
folly::f14::SwapTrackingAlloc<std::pair<int const, int>>>
m0, m1;
folly::f14::resetTracking();
swap(m0, m1);
EXPECT_EQ(
0, folly::f14::Tracked<0>::counts.dist(folly::f14::Counts{0, 0, 0, 0}));
}
TEST(F14Map, customSwap) {
testCustomSwap<folly::F14ValueMap>();
testCustomSwap<folly::F14NodeMap>();
testCustomSwap<folly::F14VectorMap>();
testCustomSwap<folly::F14FastMap>();
}
/////////////////////////////////// ///////////////////////////////////
#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE #if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
...@@ -28,9 +56,6 @@ ...@@ -28,9 +56,6 @@
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/hash/Hash.h> #include <folly/hash/Hash.h>
#include <folly/portability/GTest.h>
#include <folly/container/test/F14TestUtil.h>
using namespace folly; using namespace folly;
using namespace folly::f14; using namespace folly::f14;
...@@ -141,8 +166,6 @@ void runSimple() { ...@@ -141,8 +166,6 @@ void runSimple() {
F14TableStats::compute(h7); F14TableStats::compute(h7);
F14TableStats::compute(h8); F14TableStats::compute(h8);
F14TableStats::compute(h9); F14TableStats::compute(h9);
LOG(INFO) << "sizeof(" << typeid(T).name() << ") = " << sizeof(T);
} }
template <typename T> template <typename T>
...@@ -360,7 +383,7 @@ TEST(F14VectorMap, simple) { ...@@ -360,7 +383,7 @@ TEST(F14VectorMap, simple) {
} }
TEST(F14FastMap, simple) { TEST(F14FastMap, simple) {
// F14FastMap is just a conditional typedef. Verify it compiles. // F14FastMap inherits from a conditional typedef. Verify it compiles.
runRandom<F14FastMap<uint64_t, uint64_t>>(); runRandom<F14FastMap<uint64_t, uint64_t>>();
runSimple<F14FastMap<std::string, std::string>>(); runSimple<F14FastMap<std::string, std::string>>();
} }
...@@ -402,10 +425,11 @@ TEST(F14ValueMap, grow_stats) { ...@@ -402,10 +425,11 @@ TEST(F14ValueMap, grow_stats) {
for (unsigned i = 1; i <= 3072; ++i) { for (unsigned i = 1; i <= 3072; ++i) {
h[i]++; h[i]++;
} }
LOG(INFO) << "F14ValueMap just before rehash -> " // F14ValueMap just before rehash
<< F14TableStats::compute(h); F14TableStats::compute(h);
h[0]++; h[0]++;
LOG(INFO) << "F14ValueMap just after rehash -> " << F14TableStats::compute(h); // F14ValueMap just after rehash
F14TableStats::compute(h);
} }
TEST(F14ValueMap, steady_state_stats) { TEST(F14ValueMap, steady_state_stats) {
...@@ -430,7 +454,8 @@ TEST(F14ValueMap, steady_state_stats) { ...@@ -430,7 +454,8 @@ TEST(F14ValueMap, steady_state_stats) {
EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0); EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0);
} }
} }
LOG(INFO) << "F14ValueMap at steady state -> " << F14TableStats::compute(h); // F14ValueMap at steady state
F14TableStats::compute(h);
} }
TEST(Tracked, baseline) { TEST(Tracked, baseline) {
...@@ -502,7 +527,7 @@ TEST(Tracked, baseline) { ...@@ -502,7 +527,7 @@ TEST(Tracked, baseline) {
// and a pair const& or pair&& and cause it to be inserted // and a pair const& or pair&& and cause it to be inserted
template <typename M, typename F> template <typename M, typename F>
void runInsertCases( void runInsertCases(
std::string const& name, std::string const& /* name */,
F const& insertFunc, F const& insertFunc,
uint64_t expectedDist = 0) { uint64_t expectedDist = 0) {
static_assert(std::is_same<typename M::key_type, Tracked<0>>::value, ""); static_assert(std::is_same<typename M::key_type, Tracked<0>>::value, "");
...@@ -512,9 +537,7 @@ void runInsertCases( ...@@ -512,9 +537,7 @@ void runInsertCases(
M m; M m;
resetTracking(); resetTracking();
insertFunc(m, p); insertFunc(m, p);
LOG(INFO) << name << ", fresh key, value_type const& -> " // fresh key, value_type const& ->
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// copy is expected // copy is expected
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) + Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
...@@ -526,9 +549,7 @@ void runInsertCases( ...@@ -526,9 +549,7 @@ void runInsertCases(
M m; M m;
resetTracking(); resetTracking();
insertFunc(m, std::move(p)); insertFunc(m, std::move(p));
LOG(INFO) << name << ", fresh key, value_type&& -> " // fresh key, value_type&& ->
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// key copy is unfortunate but required // key copy is unfortunate but required
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) + Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
...@@ -540,9 +561,7 @@ void runInsertCases( ...@@ -540,9 +561,7 @@ void runInsertCases(
M m; M m;
resetTracking(); resetTracking();
insertFunc(m, p); insertFunc(m, p);
LOG(INFO) << name << ", fresh key, pair<key_type,mapped_type> const& -> " // fresh key, pair<key_type,mapped_type> const& ->
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// 1 copy is required // 1 copy is required
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) + Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
...@@ -554,9 +573,7 @@ void runInsertCases( ...@@ -554,9 +573,7 @@ void runInsertCases(
M m; M m;
resetTracking(); resetTracking();
insertFunc(m, std::move(p)); insertFunc(m, std::move(p));
LOG(INFO) << name << ", fresh key, pair<key_type,mapped_type>&& -> " // fresh key, pair<key_type,mapped_type>&& ->
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// this is the happy path for insert(make_pair(.., ..)) // this is the happy path for insert(make_pair(.., ..))
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 1, 0, 0}) + Tracked<0>::counts.dist(Counts{0, 1, 0, 0}) +
...@@ -568,11 +585,11 @@ void runInsertCases( ...@@ -568,11 +585,11 @@ void runInsertCases(
M m; M m;
resetTracking(); resetTracking();
insertFunc(m, p); insertFunc(m, p);
LOG(INFO) << name << ", fresh key, convertible const& -> " // fresh key, convertible const& ->
<< "key_type ops " << Tracked<0>::counts << ", key_src ops " // key_type ops: Tracked<0>::counts
<< Tracked<2>::counts << ", mapped_type ops " // mapped_type ops: Tracked<1>::counts
<< Tracked<1>::counts << ", mapped_src ops " // key_src ops: Tracked<2>::counts
<< Tracked<3>::counts; // mapped_src ops: Tracked<3>::counts;
// There are three strategies that could be optimal for particular // There are three strategies that could be optimal for particular
// ratios of cost: // ratios of cost:
...@@ -603,12 +620,11 @@ void runInsertCases( ...@@ -603,12 +620,11 @@ void runInsertCases(
M m; M m;
resetTracking(); resetTracking();
insertFunc(m, std::move(p)); insertFunc(m, std::move(p));
LOG(INFO) << name << ", fresh key, convertible&& -> " // fresh key, convertible&& ->
<< "key_type ops " << Tracked<0>::counts << ", key_src ops " // key_type ops: Tracked<0>::counts
<< Tracked<2>::counts << ", mapped_type ops " // mapped_type ops: Tracked<1>::counts
<< Tracked<1>::counts << ", mapped_src ops " // key_src ops: Tracked<2>::counts
<< Tracked<3>::counts; // mapped_src ops: Tracked<3>::counts;
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 1, 0, 1}) + Tracked<0>::counts.dist(Counts{0, 1, 0, 1}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 1}) + Tracked<1>::counts.dist(Counts{0, 0, 0, 1}) +
...@@ -622,10 +638,7 @@ void runInsertCases( ...@@ -622,10 +638,7 @@ void runInsertCases(
m[0] = 0; m[0] = 0;
resetTracking(); resetTracking();
insertFunc(m, p); insertFunc(m, p);
LOG(INFO) << name << ", duplicate key, value_type const& -> " // duplicate key, value_type const&
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) + Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
...@@ -637,10 +650,7 @@ void runInsertCases( ...@@ -637,10 +650,7 @@ void runInsertCases(
m[0] = 0; m[0] = 0;
resetTracking(); resetTracking();
insertFunc(m, std::move(p)); insertFunc(m, std::move(p));
LOG(INFO) << name << ", duplicate key, value_type&& -> " // duplicate key, value_type&&
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) + Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
...@@ -652,11 +662,7 @@ void runInsertCases( ...@@ -652,11 +662,7 @@ void runInsertCases(
m[0] = 0; m[0] = 0;
resetTracking(); resetTracking();
insertFunc(m, p); insertFunc(m, p);
LOG(INFO) << name // duplicate key, pair<key_type,mapped_type> const&
<< ", duplicate key, pair<key_type,mapped_type> const& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) + Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
...@@ -668,10 +674,7 @@ void runInsertCases( ...@@ -668,10 +674,7 @@ void runInsertCases(
m[0] = 0; m[0] = 0;
resetTracking(); resetTracking();
insertFunc(m, std::move(p)); insertFunc(m, std::move(p));
LOG(INFO) << name << ", duplicate key, pair<key_type,mapped_type>&& -> " // duplicate key, pair<key_type,mapped_type>&&
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) + Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
...@@ -683,12 +686,11 @@ void runInsertCases( ...@@ -683,12 +686,11 @@ void runInsertCases(
m[0] = 0; m[0] = 0;
resetTracking(); resetTracking();
insertFunc(m, p); insertFunc(m, p);
LOG(INFO) << name << ", duplicate key, convertible const& -> " // duplicate key, convertible const& ->
<< "key_type ops " << Tracked<0>::counts << ", key_src ops " // key_type ops: Tracked<0>::counts
<< Tracked<2>::counts << ", mapped_type ops " // mapped_type ops: Tracked<1>::counts
<< Tracked<1>::counts << ", mapped_src ops " // key_src ops: Tracked<2>::counts
<< Tracked<3>::counts; // mapped_src ops: Tracked<3>::counts;
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 1, 0}) + Tracked<0>::counts.dist(Counts{0, 0, 1, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}) + Tracked<1>::counts.dist(Counts{0, 0, 0, 0}) +
...@@ -702,12 +704,11 @@ void runInsertCases( ...@@ -702,12 +704,11 @@ void runInsertCases(
m[0] = 0; m[0] = 0;
resetTracking(); resetTracking();
insertFunc(m, std::move(p)); insertFunc(m, std::move(p));
LOG(INFO) << name << ", duplicate key, convertible&& -> " // duplicate key, convertible&& ->
<< "key_type ops " << Tracked<0>::counts << ", key_src ops " // key_type ops: Tracked<0>::counts
<< Tracked<2>::counts << ", mapped_type ops " // mapped_type ops: Tracked<1>::counts
<< Tracked<1>::counts << ", mapped_src ops " // key_src ops: Tracked<2>::counts
<< Tracked<3>::counts; // mapped_src ops: Tracked<3>::counts;
EXPECT_EQ( EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 1}) + Tracked<0>::counts.dist(Counts{0, 0, 0, 1}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}) + Tracked<1>::counts.dist(Counts{0, 0, 0, 0}) +
......
...@@ -15,6 +15,32 @@ ...@@ -15,6 +15,32 @@
*/ */
#include <folly/container/F14Set.h> #include <folly/container/F14Set.h>
#include <folly/container/test/F14TestUtil.h>
#include <folly/portability/GTest.h>
template <template <typename, typename, typename, typename> class TSet>
void testCustomSwap() {
using std::swap;
TSet<
int,
folly::f14::DefaultHasher<int>,
folly::f14::DefaultKeyEqual<int>,
folly::f14::SwapTrackingAlloc<int>>
m0, m1;
folly::f14::resetTracking();
swap(m0, m1);
EXPECT_EQ(
0, folly::f14::Tracked<0>::counts.dist(folly::f14::Counts{0, 0, 0, 0}));
}
TEST(F14Set, customSwap) {
testCustomSwap<folly::F14ValueSet>();
testCustomSwap<folly::F14NodeSet>();
testCustomSwap<folly::F14VectorSet>();
testCustomSwap<folly::F14FastSet>();
}
/////////////////////////////////// ///////////////////////////////////
#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE #if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
...@@ -26,9 +52,6 @@ ...@@ -26,9 +52,6 @@
#include <unordered_set> #include <unordered_set>
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/portability/GTest.h>
#include <folly/container/test/F14TestUtil.h>
using namespace folly; using namespace folly;
using namespace folly::f14; using namespace folly::f14;
...@@ -324,7 +347,7 @@ TEST(F14VectorSet, simple) { ...@@ -324,7 +347,7 @@ TEST(F14VectorSet, simple) {
} }
TEST(F14FastSet, simple) { TEST(F14FastSet, simple) {
// F14FastSet is just a conditional typedef. Verify it compiles. // F14FastSet inherits from a conditional typedef. Verify it compiles.
runRandom<F14FastSet<uint64_t>>(); runRandom<F14FastSet<uint64_t>>();
runSimple<F14FastSet<std::string>>(); runSimple<F14FastSet<std::string>>();
} }
...@@ -358,10 +381,11 @@ TEST(F14ValueSet, grow_stats) { ...@@ -358,10 +381,11 @@ TEST(F14ValueSet, grow_stats) {
for (unsigned i = 1; i <= 3072; ++i) { for (unsigned i = 1; i <= 3072; ++i) {
h.insert(i); h.insert(i);
} }
LOG(INFO) << "F14ValueSet just before rehash -> " // F14ValueSet just before rehash
<< F14TableStats::compute(h); F14TableStats::compute(h);
h.insert(0); h.insert(0);
LOG(INFO) << "F14ValueSet just after rehash -> " << F14TableStats::compute(h); // F14ValueSet just after rehash
F14TableStats::compute(h);
} }
TEST(F14ValueSet, steady_state_stats) { TEST(F14ValueSet, steady_state_stats) {
...@@ -386,7 +410,8 @@ TEST(F14ValueSet, steady_state_stats) { ...@@ -386,7 +410,8 @@ TEST(F14ValueSet, steady_state_stats) {
EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0); EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0);
} }
} }
LOG(INFO) << "F14ValueSet at steady state -> " << F14TableStats::compute(h); // F14ValueSet at steady state
F14TableStats::compute(h);
} }
TEST(F14ValueSet, vectorMaxSize) { TEST(F14ValueSet, vectorMaxSize) {
......
...@@ -194,6 +194,8 @@ thread_local Counts sumCounts{}; ...@@ -194,6 +194,8 @@ thread_local Counts sumCounts{};
template <int Tag> template <int Tag>
struct Tracked { struct Tracked {
static_assert(Tag <= 5, "Need to extend Tracked<Tag> in F14TestUtil.h");
static thread_local Counts counts; static thread_local Counts counts;
uint64_t val_; uint64_t val_;
...@@ -300,6 +302,79 @@ std::ostream& operator<<(std::ostream& xo, F14TableStats const& stats) { ...@@ -300,6 +302,79 @@ std::ostream& operator<<(std::ostream& xo, F14TableStats const& stats) {
return xo; return xo;
} }
template <class T>
class SwapTrackingAlloc {
public:
using Alloc = std::allocator<T>;
using value_type = typename Alloc::value_type;
using pointer = typename Alloc::pointer;
using const_pointer = typename Alloc::const_pointer;
using reference = typename Alloc::reference;
using const_reference = typename Alloc::const_reference;
using size_type = typename Alloc::size_type;
using propagate_on_container_swap = std::true_type;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
SwapTrackingAlloc() {}
template <class U>
SwapTrackingAlloc(SwapTrackingAlloc<U> const& other) noexcept
: a_(other.a_), t_(other.t_) {}
template <class U>
SwapTrackingAlloc& operator=(SwapTrackingAlloc<U> const& other) noexcept {
a_ = other.a_;
t_ = other.t_;
return *this;
}
template <class U>
SwapTrackingAlloc(SwapTrackingAlloc<U>&& other) noexcept
: a_(std::move(other.a_)), t_(std::move(other.t_)) {}
template <class U>
SwapTrackingAlloc& operator=(SwapTrackingAlloc<U>&& other) noexcept {
a_ = std::move(other.a_);
t_ = std::move(other.t_);
return *this;
}
T* allocate(size_t n) {
return a_.allocate(n);
}
void deallocate(T* p, size_t n) {
a_.deallocate(p, n);
}
private:
std::allocator<T> a_;
folly::f14::Tracked<0> t_;
template <class U>
friend class SwapTrackingAlloc;
};
template <class T>
void swap(SwapTrackingAlloc<T>&, SwapTrackingAlloc<T>&) {
// For argument dependent lookup:
// This function will be called if the custom swap functions of F14 containers
// are used. Otherwise, std::swap() will do 1 move construct and 2 move
// assigns which will get tracked by t_.
}
template <class T1, class T2>
bool operator==(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {
return true;
}
template <class T1, class T2>
bool operator!=(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {
return false;
}
} // namespace f14 } // namespace f14
} // namespace folly } // namespace folly
...@@ -317,4 +392,5 @@ struct hash<folly::f14::Tracked<Tag>> { ...@@ -317,4 +392,5 @@ struct hash<folly::f14::Tracked<Tag>> {
return tracked.val_ ^ Tag; return tracked.val_ ^ Tag;
} }
}; };
} // namespace std } // namespace std
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