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.
We also provide:
F14FastMap is an alias to F14ValueMap or F14VectorMap depending 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
F14VectorMap. This strategy provides the best performance, while also
providing better memory efficiency than dense_hash_map or the other hash
tables in use at Facebook that don't individually allocate nodes.
F14FastMap inherits from either F14ValueMap or F14VectorMap depending
on entry size. When the key and mapped_type are less than 24 bytes, it
inherits from F14ValueMap. For medium and large entries, it inherits
from F14VectorMap. This strategy provides the best performance, while
also providing better memory efficiency than dense_hash_map or the other
hash tables in use at Facebook that don't individually allocate nodes.
## WHICH F14 VARIANT IS RIGHT FOR ME?
......
......@@ -19,7 +19,7 @@
/**
* F14NodeMap, F14ValueMap, and F14VectorMap
*
* F14FastMap is a conditional typedef to F14ValueMap or F14VectorMap
* F14FastMap conditionally inherits from F14ValueMap or F14VectorMap
*
* See F14.md
*
......@@ -43,14 +43,47 @@
namespace folly {
template <typename... Args>
using F14NodeMap = std::unordered_map<Args...>;
template <typename... Args>
using F14ValueMap = std::unordered_map<Args...>;
template <typename... Args>
using F14VectorMap = std::unordered_map<Args...>;
template <typename... Args>
using F14FastMap = std::unordered_map<Args...>;
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 F14ValueMap : public std::unordered_map<K, M, H, E, A> {
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
......@@ -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>
bool operator==(
F14ValueMap<K, M, H, E, A> const& lhs,
......@@ -816,13 +842,6 @@ class F14NodeMap
// 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>
bool operator==(
F14NodeMap<K, M, H, E, A> const& lhs,
......@@ -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>
bool operator==(
F14VectorMap<K, M, H, E, A> const& lhs,
......@@ -974,17 +986,58 @@ bool operator!=(
return !(lhs == rhs);
}
} // namespace folly
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
namespace folly {
template <
typename Key,
typename Mapped,
typename Hasher = f14::DefaultHasher<Key>,
typename KeyEqual = f14::DefaultKeyEqual<Key>,
typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>>
using F14FastMap = std::conditional_t<
sizeof(std::pair<Key const, Mapped>) < 24,
F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>,
F14VectorMap<Key, Mapped, Hasher, KeyEqual, Alloc>>;
class F14FastMap : public std::conditional_t<
sizeof(std::pair<Key const, Mapped>) < 24,
F14ValueMap<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 @@
/**
* F14NodeSet, F14ValueSet, and F14VectorSet
*
* F14FastSet is a conditional typedef to F14ValueSet or F14VectorSet
* F14FastSet conditionally inherits from F14ValueSet or F14VectorSet
*
* See F14.md
*
......@@ -38,14 +38,44 @@
namespace folly {
template <typename... Args>
using F14NodeSet = std::unordered_set<Args...>;
template <typename... Args>
using F14ValueSet = std::unordered_set<Args...>;
template <typename... Args>
using F14VectorSet = std::unordered_set<Args...>;
template <typename... Args>
using F14FastSet = std::unordered_set<Args...>;
template <
typename K,
typename H = std::hash<K>,
typename E = std::equal_to<K>,
typename A = std::allocator<K>>
class F14NodeSet : public std::unordered_set<K, H, E, A> {
using Super = std::unordered_set<K, H, E, A>;
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
......@@ -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>
bool operator==(
F14ValueSet<K, H, E, A> const& lhs,
......@@ -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>
bool operator==(
F14NodeSet<K, H, E, A> const& lhs,
......@@ -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>
bool operator==(
F14VectorSet<K, H, E, A> const& lhs,
......@@ -740,17 +751,53 @@ bool operator!=(
F14VectorSet<K, H, E, A> const& rhs) {
return !(lhs == rhs);
}
} // namespace folly
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
namespace folly {
template <
typename Key,
typename Hasher = f14::DefaultHasher<Key>,
typename KeyEqual = f14::DefaultKeyEqual<Key>,
typename Alloc = f14::DefaultAlloc<Key>>
using F14FastSet = std::conditional_t<
sizeof(Key) < 24,
F14ValueSet<Key, Hasher, KeyEqual, Alloc>,
F14VectorSet<Key, Hasher, KeyEqual, Alloc>>;
class F14FastSet : public std::conditional_t<
sizeof(Key) < 24,
F14ValueSet<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 {
}
std::size_t max_size() const noexcept {
allocator_type a = this->alloc();
auto& a = this->alloc();
return std::min<std::size_t>(
(std::numeric_limits<typename Policy::InternalSizeType>::max)(),
std::allocator_traits<allocator_type>::max_size(a));
......
......@@ -15,6 +15,34 @@
*/
#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
......@@ -28,9 +56,6 @@
#include <folly/Range.h>
#include <folly/hash/Hash.h>
#include <folly/portability/GTest.h>
#include <folly/container/test/F14TestUtil.h>
using namespace folly;
using namespace folly::f14;
......@@ -141,8 +166,6 @@ void runSimple() {
F14TableStats::compute(h7);
F14TableStats::compute(h8);
F14TableStats::compute(h9);
LOG(INFO) << "sizeof(" << typeid(T).name() << ") = " << sizeof(T);
}
template <typename T>
......@@ -360,7 +383,7 @@ TEST(F14VectorMap, 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>>();
runSimple<F14FastMap<std::string, std::string>>();
}
......@@ -402,10 +425,11 @@ TEST(F14ValueMap, grow_stats) {
for (unsigned i = 1; i <= 3072; ++i) {
h[i]++;
}
LOG(INFO) << "F14ValueMap just before rehash -> "
<< F14TableStats::compute(h);
// F14ValueMap just before rehash
F14TableStats::compute(h);
h[0]++;
LOG(INFO) << "F14ValueMap just after rehash -> " << F14TableStats::compute(h);
// F14ValueMap just after rehash
F14TableStats::compute(h);
}
TEST(F14ValueMap, steady_state_stats) {
......@@ -430,7 +454,8 @@ TEST(F14ValueMap, steady_state_stats) {
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) {
......@@ -502,7 +527,7 @@ TEST(Tracked, baseline) {
// and a pair const& or pair&& and cause it to be inserted
template <typename M, typename F>
void runInsertCases(
std::string const& name,
std::string const& /* name */,
F const& insertFunc,
uint64_t expectedDist = 0) {
static_assert(std::is_same<typename M::key_type, Tracked<0>>::value, "");
......@@ -512,9 +537,7 @@ void runInsertCases(
M m;
resetTracking();
insertFunc(m, p);
LOG(INFO) << name << ", fresh key, value_type const& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// fresh key, value_type const& ->
// copy is expected
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
......@@ -526,9 +549,7 @@ void runInsertCases(
M m;
resetTracking();
insertFunc(m, std::move(p));
LOG(INFO) << name << ", fresh key, value_type&& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// fresh key, value_type&& ->
// key copy is unfortunate but required
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
......@@ -540,9 +561,7 @@ void runInsertCases(
M m;
resetTracking();
insertFunc(m, p);
LOG(INFO) << name << ", fresh key, pair<key_type,mapped_type> const& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// fresh key, pair<key_type,mapped_type> const& ->
// 1 copy is required
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
......@@ -554,9 +573,7 @@ void runInsertCases(
M m;
resetTracking();
insertFunc(m, std::move(p));
LOG(INFO) << name << ", fresh key, pair<key_type,mapped_type>&& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// fresh key, pair<key_type,mapped_type>&& ->
// this is the happy path for insert(make_pair(.., ..))
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 1, 0, 0}) +
......@@ -568,11 +585,11 @@ void runInsertCases(
M m;
resetTracking();
insertFunc(m, p);
LOG(INFO) << name << ", fresh key, convertible const& -> "
<< "key_type ops " << Tracked<0>::counts << ", key_src ops "
<< Tracked<2>::counts << ", mapped_type ops "
<< Tracked<1>::counts << ", mapped_src ops "
<< Tracked<3>::counts;
// fresh key, convertible const& ->
// key_type ops: Tracked<0>::counts
// mapped_type ops: Tracked<1>::counts
// key_src ops: Tracked<2>::counts
// mapped_src ops: Tracked<3>::counts;
// There are three strategies that could be optimal for particular
// ratios of cost:
......@@ -603,12 +620,11 @@ void runInsertCases(
M m;
resetTracking();
insertFunc(m, std::move(p));
LOG(INFO) << name << ", fresh key, convertible&& -> "
<< "key_type ops " << Tracked<0>::counts << ", key_src ops "
<< Tracked<2>::counts << ", mapped_type ops "
<< Tracked<1>::counts << ", mapped_src ops "
<< Tracked<3>::counts;
// fresh key, convertible&& ->
// key_type ops: Tracked<0>::counts
// mapped_type ops: Tracked<1>::counts
// key_src ops: Tracked<2>::counts
// mapped_src ops: Tracked<3>::counts;
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 1, 0, 1}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 1}) +
......@@ -622,10 +638,7 @@ void runInsertCases(
m[0] = 0;
resetTracking();
insertFunc(m, p);
LOG(INFO) << name << ", duplicate key, value_type const& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// duplicate key, value_type const&
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
......@@ -637,10 +650,7 @@ void runInsertCases(
m[0] = 0;
resetTracking();
insertFunc(m, std::move(p));
LOG(INFO) << name << ", duplicate key, value_type&& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// duplicate key, value_type&&
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
......@@ -652,11 +662,7 @@ void runInsertCases(
m[0] = 0;
resetTracking();
insertFunc(m, p);
LOG(INFO) << name
<< ", duplicate key, pair<key_type,mapped_type> const& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// duplicate key, pair<key_type,mapped_type> const&
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
......@@ -668,10 +674,7 @@ void runInsertCases(
m[0] = 0;
resetTracking();
insertFunc(m, std::move(p));
LOG(INFO) << name << ", duplicate key, pair<key_type,mapped_type>&& -> "
<< "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
<< Tracked<1>::counts;
// duplicate key, pair<key_type,mapped_type>&&
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
......@@ -683,12 +686,11 @@ void runInsertCases(
m[0] = 0;
resetTracking();
insertFunc(m, p);
LOG(INFO) << name << ", duplicate key, convertible const& -> "
<< "key_type ops " << Tracked<0>::counts << ", key_src ops "
<< Tracked<2>::counts << ", mapped_type ops "
<< Tracked<1>::counts << ", mapped_src ops "
<< Tracked<3>::counts;
// duplicate key, convertible const& ->
// key_type ops: Tracked<0>::counts
// mapped_type ops: Tracked<1>::counts
// key_src ops: Tracked<2>::counts
// mapped_src ops: Tracked<3>::counts;
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 1, 0}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}) +
......@@ -702,12 +704,11 @@ void runInsertCases(
m[0] = 0;
resetTracking();
insertFunc(m, std::move(p));
LOG(INFO) << name << ", duplicate key, convertible&& -> "
<< "key_type ops " << Tracked<0>::counts << ", key_src ops "
<< Tracked<2>::counts << ", mapped_type ops "
<< Tracked<1>::counts << ", mapped_src ops "
<< Tracked<3>::counts;
// duplicate key, convertible&& ->
// key_type ops: Tracked<0>::counts
// mapped_type ops: Tracked<1>::counts
// key_src ops: Tracked<2>::counts
// mapped_src ops: Tracked<3>::counts;
EXPECT_EQ(
Tracked<0>::counts.dist(Counts{0, 0, 0, 1}) +
Tracked<1>::counts.dist(Counts{0, 0, 0, 0}) +
......
......@@ -15,6 +15,32 @@
*/
#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
......@@ -26,9 +52,6 @@
#include <unordered_set>
#include <folly/Range.h>
#include <folly/portability/GTest.h>
#include <folly/container/test/F14TestUtil.h>
using namespace folly;
using namespace folly::f14;
......@@ -324,7 +347,7 @@ TEST(F14VectorSet, 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>>();
runSimple<F14FastSet<std::string>>();
}
......@@ -358,10 +381,11 @@ TEST(F14ValueSet, grow_stats) {
for (unsigned i = 1; i <= 3072; ++i) {
h.insert(i);
}
LOG(INFO) << "F14ValueSet just before rehash -> "
<< F14TableStats::compute(h);
// F14ValueSet just before rehash
F14TableStats::compute(h);
h.insert(0);
LOG(INFO) << "F14ValueSet just after rehash -> " << F14TableStats::compute(h);
// F14ValueSet just after rehash
F14TableStats::compute(h);
}
TEST(F14ValueSet, steady_state_stats) {
......@@ -386,7 +410,8 @@ TEST(F14ValueSet, steady_state_stats) {
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) {
......
......@@ -194,6 +194,8 @@ thread_local Counts sumCounts{};
template <int Tag>
struct Tracked {
static_assert(Tag <= 5, "Need to extend Tracked<Tag> in F14TestUtil.h");
static thread_local Counts counts;
uint64_t val_;
......@@ -300,6 +302,79 @@ std::ostream& operator<<(std::ostream& xo, F14TableStats const& stats) {
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 folly
......@@ -317,4 +392,5 @@ struct hash<folly::f14::Tracked<Tag>> {
return tracked.val_ ^ Tag;
}
};
} // 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