Commit 56a0d739 authored by Nathan Bronson's avatar Nathan Bronson Committed by Facebook Github Bot

tests for F14 exception safety plus a fix for a rare constructor

Summary:
This diff fixes the move constructor form that takes an explicit
allocator for F14VectorMap and F14VectorSet (which may also include
F14FastMap and F14FastSet depending on the value_type).  It also includes
test coverage for exception safety, which did not reveal any problems.

Reviewed By: yfeldblum

Differential Revision: D7981324

fbshipit-source-id: f4ca39aa2654d75289b554a9081621b2a3a6c136
parent d67aa207
...@@ -819,8 +819,10 @@ class VectorContainerPolicy : public BasePolicy< ...@@ -819,8 +819,10 @@ class VectorContainerPolicy : public BasePolicy<
AllocOrVoid, AllocOrVoid,
uint32_t>; uint32_t>;
using typename Super::Alloc; using typename Super::Alloc;
using typename Super::Hasher;
using typename Super::Item; using typename Super::Item;
using typename Super::ItemIter; using typename Super::ItemIter;
using typename Super::KeyEqual;
using typename Super::Value; using typename Super::Value;
private: private:
...@@ -862,17 +864,33 @@ class VectorContainerPolicy : public BasePolicy< ...@@ -862,17 +864,33 @@ class VectorContainerPolicy : public BasePolicy<
return false; return false;
} }
// inherit constructors VectorContainerPolicy(
using Super::Super; Hasher const& hasher,
KeyEqual const& keyEqual,
Alloc const& alloc)
: Super{hasher, keyEqual, alloc} {}
VectorContainerPolicy(VectorContainerPolicy const& rhs) VectorContainerPolicy(VectorContainerPolicy const& rhs) : Super{rhs} {
: Super{rhs}, values_{nullptr} {} // values_ will get allocated later to do the copy
}
VectorContainerPolicy(VectorContainerPolicy const& rhs, Alloc const& alloc)
: Super{rhs, alloc} {
// values_ will get allocated later to do the copy
}
VectorContainerPolicy(VectorContainerPolicy&& rhs) noexcept VectorContainerPolicy(VectorContainerPolicy&& rhs) noexcept
: Super{std::move(rhs)}, values_{rhs.values_} { : Super{std::move(rhs)}, values_{rhs.values_} {
rhs.values_ = nullptr; rhs.values_ = nullptr;
} }
VectorContainerPolicy(
VectorContainerPolicy&& rhs,
Alloc const& alloc) noexcept
: Super{std::move(rhs), alloc}, values_{rhs.values_} {
rhs.values_ = nullptr;
}
VectorContainerPolicy& operator=(VectorContainerPolicy const& rhs) { VectorContainerPolicy& operator=(VectorContainerPolicy const& rhs) {
if (this != &rhs) { if (this != &rhs) {
FOLLY_SAFE_DCHECK(values_ == nullptr, ""); FOLLY_SAFE_DCHECK(values_ == nullptr, "");
......
...@@ -52,18 +52,18 @@ template < ...@@ -52,18 +52,18 @@ template <
template <typename, typename, typename, typename, typename> class TMap, template <typename, typename, typename, typename, typename> class TMap,
typename K, typename K,
typename V> typename V>
void testAllocatedMemorySize() { void runAllocatedMemorySizeTest() {
using namespace folly::f14; using namespace folly::f14;
using A = SwapTrackingAlloc<std::pair<const K, V>>; using A = SwapTrackingAlloc<std::pair<const K, V>>;
A::resetTracking(); resetTracking();
TMap<K, V, DefaultHasher<K>, DefaultKeyEqual<K>, A> m; TMap<K, V, DefaultHasher<K>, DefaultKeyEqual<K>, A> m;
EXPECT_EQ(A::getAllocatedMemorySize(), m.getAllocatedMemorySize()); EXPECT_EQ(testAllocatedMemorySize, m.getAllocatedMemorySize());
for (size_t i = 0; i < 1000; ++i) { for (size_t i = 0; i < 1000; ++i) {
m.insert(std::make_pair(folly::to<K>(i), V{})); m.insert(std::make_pair(folly::to<K>(i), V{}));
m.erase(folly::to<K>(i / 10 + 2)); m.erase(folly::to<K>(i / 10 + 2));
EXPECT_EQ(A::getAllocatedMemorySize(), m.getAllocatedMemorySize()); EXPECT_EQ(testAllocatedMemorySize, m.getAllocatedMemorySize());
std::size_t size = 0; std::size_t size = 0;
std::size_t count = 0; std::size_t count = 0;
m.visitAllocationClasses([&](std::size_t, std::size_t) mutable {}); m.visitAllocationClasses([&](std::size_t, std::size_t) mutable {});
...@@ -71,33 +71,33 @@ void testAllocatedMemorySize() { ...@@ -71,33 +71,33 @@ void testAllocatedMemorySize() {
size += bytes * n; size += bytes * n;
count += n; count += n;
}); });
EXPECT_EQ(A::getAllocatedMemorySize(), size); EXPECT_EQ(testAllocatedMemorySize, size);
EXPECT_EQ(A::getAllocatedBlockCount(), count); EXPECT_EQ(testAllocatedBlockCount, count);
} }
m = decltype(m){}; m = decltype(m){};
EXPECT_EQ(A::getAllocatedMemorySize(), 0); EXPECT_EQ(testAllocatedMemorySize, 0);
EXPECT_EQ(A::getAllocatedBlockCount(), 0); EXPECT_EQ(testAllocatedBlockCount, 0);
m.visitAllocationClasses([](std::size_t, std::size_t n) { EXPECT_EQ(n, 0); }); m.visitAllocationClasses([](std::size_t, std::size_t n) { EXPECT_EQ(n, 0); });
} }
template <typename K, typename V> template <typename K, typename V>
void runAllocatedMemorySizeTest() { void runAllocatedMemorySizeTests() {
testAllocatedMemorySize<folly::F14ValueMap, K, V>(); runAllocatedMemorySizeTest<folly::F14ValueMap, K, V>();
testAllocatedMemorySize<folly::F14NodeMap, K, V>(); runAllocatedMemorySizeTest<folly::F14NodeMap, K, V>();
testAllocatedMemorySize<folly::F14VectorMap, K, V>(); runAllocatedMemorySizeTest<folly::F14VectorMap, K, V>();
testAllocatedMemorySize<folly::F14FastMap, K, V>(); runAllocatedMemorySizeTest<folly::F14FastMap, K, V>();
} }
} // namespace } // namespace
TEST(F14Map, getAllocatedMemorySize) { TEST(F14Map, getAllocatedMemorySize) {
runAllocatedMemorySizeTest<bool, bool>(); runAllocatedMemorySizeTests<bool, bool>();
runAllocatedMemorySizeTest<int, int>(); runAllocatedMemorySizeTests<int, int>();
runAllocatedMemorySizeTest<bool, std::string>(); runAllocatedMemorySizeTests<bool, std::string>();
runAllocatedMemorySizeTest<long double, std::string>(); runAllocatedMemorySizeTests<long double, std::string>();
runAllocatedMemorySizeTest<std::string, int>(); runAllocatedMemorySizeTests<std::string, int>();
runAllocatedMemorySizeTest<std::string, std::string>(); runAllocatedMemorySizeTests<std::string, std::string>();
runAllocatedMemorySizeTest<folly::fbstring, long>(); runAllocatedMemorySizeTests<folly::fbstring, long>();
} }
/////////////////////////////////// ///////////////////////////////////
...@@ -240,196 +240,256 @@ void runRehash() { ...@@ -240,196 +240,256 @@ void runRehash() {
F14TableStats::compute(h); F14TableStats::compute(h);
} }
// T should be a map from uint64_t to uint64_t // T should be a map from uint64_t to Tracked<1> that uses SwapTrackingAlloc
template <typename T> template <typename T>
void runRandom() { void runRandom() {
using R = std::unordered_map<uint64_t, uint64_t>; using R = std::unordered_map<uint64_t, Tracked<2>>;
resetTracking();
std::mt19937_64 gen(0); std::mt19937_64 gen(0);
std::uniform_int_distribution<> pctDist(0, 100); std::uniform_int_distribution<> pctDist(0, 100);
std::uniform_int_distribution<uint64_t> bitsBitsDist(1, 6); std::uniform_int_distribution<uint64_t> bitsBitsDist(1, 6);
T t0; {
T t1; T t0;
R r0; T t1;
R r1; R r0;
R r1;
for (std::size_t reps = 0; reps < 10000; ++reps) { std::size_t rollbacks = 0;
// discardBits will be from 0 to 62
auto discardBits = (uint64_t{1} << bitsBitsDist(gen)) - 2; for (std::size_t reps = 0; reps < 100000; ++reps) {
auto k = gen() >> discardBits; if (pctDist(gen) < 20) {
auto v = gen(); // 10% chance allocator will fail after 0 to 3 more allocations
auto pct = pctDist(gen); limitTestAllocations(gen() & 3);
} else {
EXPECT_EQ(t0.empty(), r0.empty()); unlimitTestAllocations();
EXPECT_EQ(t0.size(), r0.size());
if (pct < 15) {
// insert
auto t = t0.insert(std::make_pair(k, v));
auto r = r0.insert(std::make_pair(k, v));
EXPECT_EQ(*t.first, *r.first);
EXPECT_EQ(t.second, r.second);
} else if (pct < 25) {
// emplace
auto t = t0.emplace(k, v);
auto r = r0.emplace(k, v);
EXPECT_EQ(*t.first, *r.first);
EXPECT_EQ(t.second, r.second);
} else if (pct < 30) {
// bulk insert
t0.insert(r1.begin(), r1.end());
r0.insert(r1.begin(), r1.end());
} else if (pct < 40) {
// erase by key
auto t = t0.erase(k);
auto r = r0.erase(k);
EXPECT_EQ(t, r);
} else if (pct < 47) {
// erase by iterator
if (t0.size() > 0) {
auto r = r0.find(k);
if (r == r0.end()) {
r = r0.begin();
}
k = r->first;
auto t = t0.find(k);
t = t0.erase(t);
if (t != t0.end()) {
EXPECT_NE(t->first, k);
}
r = r0.erase(r);
if (r != r0.end()) {
EXPECT_NE(r->first, k);
}
} }
} else if (pct < 50) { bool leakCheckOnly = false;
// bulk erase
if (t0.size() > 0) { // discardBits will be from 0 to 62
auto r = r0.find(k); auto discardBits = (uint64_t{1} << bitsBitsDist(gen)) - 2;
if (r == r0.end()) { auto k = gen() >> discardBits;
r = r0.begin(); auto v = gen();
auto pct = pctDist(gen);
try {
EXPECT_EQ(t0.empty(), r0.empty());
EXPECT_EQ(t0.size(), r0.size());
EXPECT_EQ(2, Tracked<0>::counts.liveCount());
EXPECT_EQ(t0.size() + t1.size(), Tracked<1>::counts.liveCount());
EXPECT_EQ(r0.size() + r1.size(), Tracked<2>::counts.liveCount());
if (pct < 15) {
// insert
auto t = t0.insert(std::make_pair(k, v));
auto r = r0.insert(std::make_pair(k, v));
EXPECT_EQ(t.first->first, r.first->first);
EXPECT_EQ(t.first->second.val_, r.first->second.val_);
EXPECT_EQ(t.second, r.second);
} else if (pct < 25) {
// emplace
auto t = t0.emplace(k, v);
auto r = r0.emplace(k, v);
EXPECT_EQ(t.first->first, r.first->first);
EXPECT_EQ(t.first->second.val_, r.first->second.val_);
EXPECT_EQ(t.second, r.second);
} else if (pct < 30) {
// bulk insert
leakCheckOnly = true;
t0.insert(t1.begin(), t1.end());
r0.insert(r1.begin(), r1.end());
} else if (pct < 40) {
// erase by key
auto t = t0.erase(k);
auto r = r0.erase(k);
EXPECT_EQ(t, r);
} else if (pct < 47) {
// erase by iterator
if (t0.size() > 0) {
auto r = r0.find(k);
if (r == r0.end()) {
r = r0.begin();
}
k = r->first;
auto t = t0.find(k);
t = t0.erase(t);
if (t != t0.end()) {
EXPECT_NE(t->first, k);
}
r = r0.erase(r);
if (r != r0.end()) {
EXPECT_NE(r->first, k);
}
}
} else if (pct < 50) {
// bulk erase
if (t0.size() > 0) {
auto r = r0.find(k);
if (r == r0.end()) {
r = r0.begin();
}
k = r->first;
auto t = t0.find(k);
auto firstt = t;
auto lastt = ++t;
t = t0.erase(firstt, lastt);
if (t != t0.end()) {
EXPECT_NE(t->first, k);
}
auto firstr = r;
auto lastr = ++r;
r = r0.erase(firstr, lastr);
if (r != r0.end()) {
EXPECT_NE(r->first, k);
}
}
} else if (pct < 58) {
// find
auto t = t0.find(k);
auto r = r0.find(k);
EXPECT_EQ((t == t0.end()), (r == r0.end()));
if (t != t0.end() && r != r0.end()) {
EXPECT_EQ(t->first, r->first);
EXPECT_EQ(t->second.val_, r->second.val_);
}
EXPECT_EQ(t0.count(k), r0.count(k));
} else if (pct < 60) {
// equal_range
auto t = t0.equal_range(k);
auto r = r0.equal_range(k);
EXPECT_EQ((t.first == t.second), (r.first == r.second));
if (t.first != t.second && r.first != r.second) {
EXPECT_EQ(t.first->first, r.first->first);
EXPECT_EQ(t.first->second.val_, r.first->second.val_);
t.first++;
r.first++;
EXPECT_TRUE(t.first == t.second);
EXPECT_TRUE(r.first == r.second);
}
} else if (pct < 65) {
// iterate
uint64_t t = 0;
for (auto& e : t0) {
t += e.first * 37 + e.second.val_ + 1000;
}
uint64_t r = 0;
for (auto& e : r0) {
r += e.first * 37 + e.second.val_ + 1000;
}
EXPECT_EQ(t, r);
} else if (pct < 69) {
// swap
using std::swap;
swap(t0, t1);
swap(r0, r1);
} else if (pct < 70) {
// swap
t0.swap(t1);
r0.swap(r1);
} else if (pct < 72) {
// default construct
t0.~T();
new (&t0) T();
r0.~R();
new (&r0) R();
} else if (pct < 74) {
// default construct with capacity
std::size_t capacity = k & 0xffff;
T t(capacity);
t0 = std::move(t);
R r(capacity);
r0 = std::move(r);
} else if (pct < 80) {
// bulk iterator construct
t0 = T{t1.begin(), t1.end()};
r0 = R{r1.begin(), r1.end()};
} else if (pct < 82) {
// initializer list construct
auto k2 = gen() >> discardBits;
auto v2 = gen();
T t({{k, v}, {k2, v}, {k2, v2}});
t0 = std::move(t);
R r({{k, v}, {k2, v}, {k2, v2}});
r0 = std::move(r);
} else if (pct < 85) {
// copy construct
T t(t1);
t0 = std::move(t);
R r(r1);
r0 = std::move(r);
} else if (pct < 88) {
// copy construct
T t(t1, t1.get_allocator());
t0 = std::move(t);
R r(r1, r1.get_allocator());
r0 = std::move(r);
} else if (pct < 89) {
// move construct
t0.~T();
new (&t0) T(std::move(t1));
r0.~R();
new (&r0) R(std::move(r1));
} else if (pct < 90) {
// move construct
t0.~T();
auto ta = t1.get_allocator();
new (&t0) T(std::move(t1), ta);
r0.~R();
auto ra = r1.get_allocator();
new (&r0) R(std::move(r1), ra);
} else if (pct < 94) {
// copy assign
leakCheckOnly = true;
t0 = t1;
r0 = r1;
} else if (pct < 96) {
// move assign
t0 = std::move(t1);
r0 = std::move(r1);
} else if (pct < 98) {
// operator==
EXPECT_EQ((t0 == t1), (r0 == r1));
} else if (pct < 99) {
// clear
F14TableStats::compute(t0);
t0.clear();
r0.clear();
} else if (pct < 100) {
// reserve
auto scale = std::uniform_int_distribution<>(0, 8)(gen);
auto delta = std::uniform_int_distribution<>(-2, 2)(gen);
std::ptrdiff_t target = (t0.size() * scale) / 4 + delta;
if (target >= 0) {
t0.reserve(static_cast<std::size_t>(target));
r0.reserve(static_cast<std::size_t>(target));
}
} }
k = r->first; } catch (std::bad_alloc const&) {
auto t = t0.find(k); ++rollbacks;
auto firstt = t;
auto lastt = ++t; F14TableStats::compute(t0);
t = t0.erase(firstt, lastt);
if (t != t0.end()) { if (leakCheckOnly) {
EXPECT_NE(t->first, k); unlimitTestAllocations();
t0.clear();
for (auto&& kv : r0) {
t0[kv.first] = kv.second.val_;
}
} }
auto firstr = r;
auto lastr = ++r; assert(t0.size() == r0.size());
r = r0.erase(firstr, lastr); for (auto&& kv : r0) {
if (r != r0.end()) { auto t = t0.find(kv.first);
EXPECT_NE(r->first, k); EXPECT_TRUE(
t != t0.end() && t->first == kv.first &&
t->second.val_ == kv.second.val_);
} }
} }
} else if (pct < 58) {
// find
auto t = t0.find(k);
auto r = r0.find(k);
EXPECT_EQ((t == t0.end()), (r == r0.end()));
if (t != t0.end() && r != r0.end()) {
EXPECT_EQ(*t, *r);
}
EXPECT_EQ(t0.count(k), r0.count(k));
} else if (pct < 60) {
// equal_range
auto t = t0.equal_range(k);
auto r = r0.equal_range(k);
EXPECT_EQ((t.first == t.second), (r.first == r.second));
if (t.first != t.second && r.first != r.second) {
EXPECT_EQ(*t.first, *r.first);
t.first++;
r.first++;
EXPECT_TRUE(t.first == t.second);
EXPECT_TRUE(r.first == r.second);
}
} else if (pct < 65) {
// iterate
uint64_t t = 0;
for (auto& e : t0) {
t += e.first * 37 + e.second + 1000;
}
uint64_t r = 0;
for (auto& e : r0) {
r += e.first * 37 + e.second + 1000;
}
EXPECT_EQ(t, r);
} else if (pct < 69) {
// swap
using std::swap;
swap(t0, t1);
swap(r0, r1);
} else if (pct < 70) {
// swap
t0.swap(t1);
r0.swap(r1);
} else if (pct < 72) {
// default construct
t0.~T();
new (&t0) T();
r0.~R();
new (&r0) R();
} else if (pct < 74) {
// default construct with capacity
std::size_t capacity = k & 0xffff;
t0.~T();
new (&t0) T(capacity);
r0.~R();
new (&r0) R(capacity);
} else if (pct < 80) {
// bulk iterator construct
t0.~T();
new (&t0) T(r1.begin(), r1.end());
r0.~R();
new (&r0) R(r1.begin(), r1.end());
} else if (pct < 82) {
// initializer list construct
auto k2 = gen() >> discardBits;
auto v2 = gen();
t0.~T();
new (&t0) T({{k, v}, {k2, v}, {k2, v2}});
r0.~R();
new (&r0) R({{k, v}, {k2, v}, {k2, v2}});
} else if (pct < 88) {
// copy construct
t0.~T();
new (&t0) T(t1);
r0.~R();
new (&r0) R(r1);
} else if (pct < 90) {
// move construct
t0.~T();
new (&t0) T(std::move(t1));
r0.~R();
new (&r0) R(std::move(r1));
} else if (pct < 94) {
// copy assign
t0 = t1;
r0 = r1;
} else if (pct < 96) {
// move assign
t0 = std::move(t1);
r0 = std::move(r1);
} else if (pct < 98) {
// operator==
EXPECT_EQ((t0 == t1), (r0 == r1));
} else if (pct < 99) {
// clear
F14TableStats::compute(t0);
t0.clear();
r0.clear();
} else if (pct < 100) {
// reserve
auto scale = std::uniform_int_distribution<>(0, 8)(gen);
auto delta = std::uniform_int_distribution<>(-2, 2)(gen);
std::ptrdiff_t target = (t0.size() * scale) / 4 + delta;
if (target >= 0) {
t0.reserve(static_cast<std::size_t>(target));
r0.reserve(static_cast<std::size_t>(target));
}
} }
EXPECT_GE(rollbacks, 10);
} }
EXPECT_EQ(testAllocatedMemorySize, 0);
} }
template <typename T> template <typename T>
...@@ -462,8 +522,6 @@ TEST(F14VectorMap, simple) { ...@@ -462,8 +522,6 @@ TEST(F14VectorMap, simple) {
} }
TEST(F14FastMap, simple) { TEST(F14FastMap, simple) {
// F14FastMap inherits from a conditional typedef. Verify it compiles.
runRandom<F14FastMap<uint64_t, uint64_t>>();
runSimple<F14FastMap<std::string, std::string>>(); runSimple<F14FastMap<std::string, std::string>>();
} }
...@@ -528,15 +586,39 @@ TEST(F14NodeMap, prehash) { ...@@ -528,15 +586,39 @@ TEST(F14NodeMap, prehash) {
} }
TEST(F14ValueMap, random) { TEST(F14ValueMap, random) {
runRandom<F14ValueMap<uint64_t, uint64_t>>(); runRandom<F14ValueMap<
uint64_t,
Tracked<1>,
std::hash<uint64_t>,
std::equal_to<uint64_t>,
SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();
} }
TEST(F14NodeMap, random) { TEST(F14NodeMap, random) {
runRandom<F14NodeMap<uint64_t, uint64_t>>(); runRandom<F14NodeMap<
uint64_t,
Tracked<1>,
std::hash<uint64_t>,
std::equal_to<uint64_t>,
SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();
} }
TEST(F14VectorMap, random) { TEST(F14VectorMap, random) {
runRandom<F14VectorMap<uint64_t, uint64_t>>(); runRandom<F14VectorMap<
uint64_t,
Tracked<1>,
std::hash<uint64_t>,
std::equal_to<uint64_t>,
SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();
}
TEST(F14FastMap, random) {
runRandom<F14FastMap<
uint64_t,
Tracked<1>,
std::hash<uint64_t>,
std::equal_to<uint64_t>,
SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();
} }
TEST(F14ValueMap, grow_stats) { TEST(F14ValueMap, grow_stats) {
...@@ -655,16 +737,16 @@ TEST(Tracked, baseline) { ...@@ -655,16 +737,16 @@ TEST(Tracked, baseline) {
resetTracking(); resetTracking();
b1 = a0; b1 = a0;
EXPECT_EQ(a0.val_, b1.val_); EXPECT_EQ(a0.val_, b1.val_);
EXPECT_EQ(sumCounts, (Counts{0, 0, 1, 0, 0, 1})); EXPECT_EQ(sumCounts, (Counts{0, 0, 1, 0, 0, 1, 0, 1}));
EXPECT_EQ(Tracked<1>::counts, (Counts{0, 0, 1, 0, 0, 1})); EXPECT_EQ(Tracked<1>::counts, (Counts{0, 0, 1, 0, 0, 1, 0, 1}));
} }
{ {
Tracked<1> b1; Tracked<1> b1;
resetTracking(); resetTracking();
b1 = std::move(a0); b1 = std::move(a0);
EXPECT_EQ(a0.val_, b1.val_); EXPECT_EQ(a0.val_, b1.val_);
EXPECT_EQ(sumCounts, (Counts{0, 0, 0, 1, 0, 1})); EXPECT_EQ(sumCounts, (Counts{0, 0, 0, 1, 0, 1, 0, 1}));
EXPECT_EQ(Tracked<1>::counts, (Counts{0, 0, 0, 1, 0, 1})); EXPECT_EQ(Tracked<1>::counts, (Counts{0, 0, 0, 1, 0, 1, 0, 1}));
} }
} }
......
...@@ -49,18 +49,18 @@ namespace { ...@@ -49,18 +49,18 @@ namespace {
template < template <
template <typename, typename, typename, typename> class TSet, template <typename, typename, typename, typename> class TSet,
typename K> typename K>
void testAllocatedMemorySize() { void runAllocatedMemorySizeTest() {
using namespace folly::f14; using namespace folly::f14;
using A = SwapTrackingAlloc<K>; using A = SwapTrackingAlloc<K>;
A::resetTracking(); resetTracking();
TSet<K, DefaultHasher<K>, DefaultKeyEqual<K>, A> m; TSet<K, DefaultHasher<K>, DefaultKeyEqual<K>, A> m;
EXPECT_EQ(A::getAllocatedMemorySize(), m.getAllocatedMemorySize()); EXPECT_EQ(testAllocatedMemorySize, m.getAllocatedMemorySize());
for (size_t i = 0; i < 1000; ++i) { for (size_t i = 0; i < 1000; ++i) {
m.insert(folly::to<K>(i)); m.insert(folly::to<K>(i));
m.erase(folly::to<K>(i / 10 + 2)); m.erase(folly::to<K>(i / 10 + 2));
EXPECT_EQ(A::getAllocatedMemorySize(), m.getAllocatedMemorySize()); EXPECT_EQ(testAllocatedMemorySize, m.getAllocatedMemorySize());
std::size_t size = 0; std::size_t size = 0;
std::size_t count = 0; std::size_t count = 0;
m.visitAllocationClasses([&](std::size_t, std::size_t) mutable {}); m.visitAllocationClasses([&](std::size_t, std::size_t) mutable {});
...@@ -68,32 +68,32 @@ void testAllocatedMemorySize() { ...@@ -68,32 +68,32 @@ void testAllocatedMemorySize() {
size += bytes * n; size += bytes * n;
count += n; count += n;
}); });
EXPECT_EQ(A::getAllocatedMemorySize(), size); EXPECT_EQ(testAllocatedMemorySize, size);
EXPECT_EQ(A::getAllocatedBlockCount(), count); EXPECT_EQ(testAllocatedBlockCount, count);
} }
m = decltype(m){}; m = decltype(m){};
EXPECT_EQ(A::getAllocatedMemorySize(), 0); EXPECT_EQ(testAllocatedMemorySize, 0);
EXPECT_EQ(A::getAllocatedBlockCount(), 0); EXPECT_EQ(testAllocatedBlockCount, 0);
m.visitAllocationClasses([](std::size_t, std::size_t n) { EXPECT_EQ(n, 0); }); m.visitAllocationClasses([](std::size_t, std::size_t n) { EXPECT_EQ(n, 0); });
} }
template <typename K> template <typename K>
void runAllocatedMemorySizeTest() { void runAllocatedMemorySizeTests() {
testAllocatedMemorySize<folly::F14ValueSet, K>(); runAllocatedMemorySizeTest<folly::F14ValueSet, K>();
testAllocatedMemorySize<folly::F14NodeSet, K>(); runAllocatedMemorySizeTest<folly::F14NodeSet, K>();
testAllocatedMemorySize<folly::F14VectorSet, K>(); runAllocatedMemorySizeTest<folly::F14VectorSet, K>();
testAllocatedMemorySize<folly::F14FastSet, K>(); runAllocatedMemorySizeTest<folly::F14FastSet, K>();
} }
} // namespace } // namespace
TEST(F14Set, getAllocatedMemorySize) { TEST(F14Set, getAllocatedMemorySize) {
runAllocatedMemorySizeTest<bool>(); runAllocatedMemorySizeTests<bool>();
runAllocatedMemorySizeTest<int>(); runAllocatedMemorySizeTests<int>();
runAllocatedMemorySizeTest<long>(); runAllocatedMemorySizeTests<long>();
runAllocatedMemorySizeTest<long double>(); runAllocatedMemorySizeTests<long double>();
runAllocatedMemorySizeTest<std::string>(); runAllocatedMemorySizeTests<std::string>();
runAllocatedMemorySizeTest<folly::fbstring>(); runAllocatedMemorySizeTests<folly::fbstring>();
} }
/////////////////////////////////// ///////////////////////////////////
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <limits>
#include <ostream> #include <ostream>
#include <vector> #include <vector>
...@@ -124,6 +125,7 @@ struct Counts { ...@@ -124,6 +125,7 @@ struct Counts {
uint64_t copyAssign{0}; uint64_t copyAssign{0};
uint64_t moveAssign{0}; uint64_t moveAssign{0};
uint64_t defaultConstruct{0}; uint64_t defaultConstruct{0};
uint64_t destroyed{0};
explicit Counts( explicit Counts(
uint64_t copConstr = 0, uint64_t copConstr = 0,
...@@ -132,15 +134,23 @@ struct Counts { ...@@ -132,15 +134,23 @@ struct Counts {
uint64_t movConv = 0, uint64_t movConv = 0,
uint64_t copAssign = 0, uint64_t copAssign = 0,
uint64_t movAssign = 0, uint64_t movAssign = 0,
uint64_t def = 0) uint64_t def = 0,
uint64_t destr = 0)
: copyConstruct{copConstr}, : copyConstruct{copConstr},
moveConstruct{movConstr}, moveConstruct{movConstr},
copyConvert{copConv}, copyConvert{copConv},
moveConvert{movConv}, moveConvert{movConv},
copyAssign{copAssign}, copyAssign{copAssign},
moveAssign{movAssign}, moveAssign{movAssign},
defaultConstruct{def} {} defaultConstruct{def},
destroyed{destr} {}
int64_t liveCount() const {
return copyConstruct + moveConstruct + copyConvert + moveConvert +
defaultConstruct - destroyed;
}
// dist ignores destroyed count
uint64_t dist(Counts const& rhs) const { uint64_t dist(Counts const& rhs) const {
auto d = [](uint64_t x, uint64_t y) { return (x - y) * (x - y); }; auto d = [](uint64_t x, uint64_t y) { return (x - y) * (x - y); };
return d(copyConstruct, rhs.copyConstruct) + return d(copyConstruct, rhs.copyConstruct) +
...@@ -151,11 +161,7 @@ struct Counts { ...@@ -151,11 +161,7 @@ struct Counts {
} }
bool operator==(Counts const& rhs) const { bool operator==(Counts const& rhs) const {
return copyConstruct == rhs.copyConstruct && return dist(rhs) == 0 && destroyed == rhs.destroyed;
moveConstruct == rhs.moveConstruct && copyConvert == rhs.copyConvert &&
moveConvert == rhs.moveConvert && copyAssign == rhs.copyAssign &&
moveAssign == rhs.moveAssign &&
defaultConstruct == rhs.defaultConstruct;
} }
bool operator!=(Counts const& rhs) const { bool operator!=(Counts const& rhs) const {
return !(*this == rhs); return !(*this == rhs);
...@@ -193,6 +199,10 @@ std::ostream& operator<<(std::ostream& xo, Counts const& counts) { ...@@ -193,6 +199,10 @@ std::ostream& operator<<(std::ostream& xo, Counts const& counts) {
xo << glue << counts.defaultConstruct << " default construct"; xo << glue << counts.defaultConstruct << " default construct";
glue = ", "; glue = ", ";
} }
if (counts.destroyed > 0) {
xo << glue << counts.destroyed << " destroyed";
glue = ", ";
}
xo << "]"; xo << "]";
return xo; return xo;
} }
...@@ -252,6 +262,11 @@ struct Tracked { ...@@ -252,6 +262,11 @@ struct Tracked {
counts.moveConvert++; counts.moveConvert++;
} }
~Tracked() {
sumCounts.destroyed++;
counts.destroyed++;
}
bool operator==(Tracked const& rhs) const { bool operator==(Tracked const& rhs) const {
return val_ == rhs.val_; return val_ == rhs.val_;
} }
...@@ -273,7 +288,21 @@ thread_local Counts Tracked<4>::counts{}; ...@@ -273,7 +288,21 @@ thread_local Counts Tracked<4>::counts{};
template <> template <>
thread_local Counts Tracked<5>::counts{}; thread_local Counts Tracked<5>::counts{};
void resetTracking() { thread_local size_t testAllocatedMemorySize{0};
thread_local size_t testAllocatedBlockCount{0};
thread_local size_t testAllocationCount{0};
thread_local size_t testAllocationMaxCount{
std::numeric_limits<std::size_t>::max()};
inline void limitTestAllocations(std::size_t allocationsBeforeException = 0) {
testAllocationMaxCount = testAllocationCount + allocationsBeforeException;
}
inline void unlimitTestAllocations() {
testAllocationMaxCount = std::numeric_limits<std::size_t>::max();
}
inline void resetTracking() {
sumCounts = Counts{}; sumCounts = Counts{};
Tracked<0>::counts = Counts{}; Tracked<0>::counts = Counts{};
Tracked<1>::counts = Counts{}; Tracked<1>::counts = Counts{};
...@@ -281,41 +310,12 @@ void resetTracking() { ...@@ -281,41 +310,12 @@ void resetTracking() {
Tracked<3>::counts = Counts{}; Tracked<3>::counts = Counts{};
Tracked<4>::counts = Counts{}; Tracked<4>::counts = Counts{};
Tracked<5>::counts = Counts{}; Tracked<5>::counts = Counts{};
testAllocatedMemorySize = 0;
testAllocatedBlockCount = 0;
testAllocationCount = 0;
testAllocationMaxCount = std::numeric_limits<std::size_t>::max();
} }
std::ostream& operator<<(std::ostream& xo, F14TableStats const& stats) {
using f14::Histo;
xo << "{ " << std::endl;
xo << " policy: " << folly::demangle(stats.policy) << std::endl;
xo << " size: " << stats.size << std::endl;
xo << " valueSize: " << stats.valueSize << std::endl;
xo << " bucketCount: " << stats.bucketCount << std::endl;
xo << " chunkCount: " << stats.chunkCount << std::endl;
xo << " chunkOccupancyHisto" << Histo{stats.chunkOccupancyHisto}
<< std::endl;
xo << " chunkOutboundOverflowHisto"
<< Histo{stats.chunkOutboundOverflowHisto} << std::endl;
xo << " chunkHostedOverflowHisto" << Histo{stats.chunkHostedOverflowHisto}
<< std::endl;
xo << " keyProbeLengthHisto" << Histo{stats.keyProbeLengthHisto}
<< std::endl;
xo << " missProbeLengthHisto" << Histo{stats.missProbeLengthHisto}
<< std::endl;
xo << " totalBytes: " << stats.totalBytes << std::endl;
xo << " valueBytes: " << (stats.size * stats.valueSize) << std::endl;
xo << " overheadBytes: " << stats.overheadBytes << std::endl;
if (stats.size > 0) {
xo << " overheadBytesPerKey: " << (stats.overheadBytes * 1.0 / stats.size)
<< std::endl;
}
xo << "}";
return xo;
}
thread_local size_t allocatedMemorySize{0};
thread_local size_t allocatedBlockCount{0};
template <class T> template <class T>
class SwapTrackingAlloc { class SwapTrackingAlloc {
public: public:
...@@ -356,27 +356,18 @@ class SwapTrackingAlloc { ...@@ -356,27 +356,18 @@ class SwapTrackingAlloc {
return *this; return *this;
} }
static size_t getAllocatedMemorySize() {
return allocatedMemorySize;
}
static size_t getAllocatedBlockCount() {
return allocatedBlockCount;
}
static void resetTracking() {
allocatedMemorySize = 0;
allocatedBlockCount = 0;
}
T* allocate(size_t n) { T* allocate(size_t n) {
allocatedMemorySize += n * sizeof(T); if (testAllocationCount >= testAllocationMaxCount) {
++allocatedBlockCount; throw std::bad_alloc();
}
++testAllocationCount;
testAllocatedMemorySize += n * sizeof(T);
++testAllocatedBlockCount;
return a_.allocate(n); return a_.allocate(n);
} }
void deallocate(T* p, size_t n) { void deallocate(T* p, size_t n) {
allocatedMemorySize -= n * sizeof(T); testAllocatedMemorySize -= n * sizeof(T);
--allocatedBlockCount; --testAllocatedBlockCount;
a_.deallocate(p, n); a_.deallocate(p, n);
} }
...@@ -406,6 +397,36 @@ bool operator!=(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) { ...@@ -406,6 +397,36 @@ bool operator!=(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {
return false; return false;
} }
std::ostream& operator<<(std::ostream& xo, F14TableStats const& stats) {
using f14::Histo;
xo << "{ " << std::endl;
xo << " policy: " << folly::demangle(stats.policy) << std::endl;
xo << " size: " << stats.size << std::endl;
xo << " valueSize: " << stats.valueSize << std::endl;
xo << " bucketCount: " << stats.bucketCount << std::endl;
xo << " chunkCount: " << stats.chunkCount << std::endl;
xo << " chunkOccupancyHisto" << Histo{stats.chunkOccupancyHisto}
<< std::endl;
xo << " chunkOutboundOverflowHisto"
<< Histo{stats.chunkOutboundOverflowHisto} << std::endl;
xo << " chunkHostedOverflowHisto" << Histo{stats.chunkHostedOverflowHisto}
<< std::endl;
xo << " keyProbeLengthHisto" << Histo{stats.keyProbeLengthHisto}
<< std::endl;
xo << " missProbeLengthHisto" << Histo{stats.missProbeLengthHisto}
<< std::endl;
xo << " totalBytes: " << stats.totalBytes << std::endl;
xo << " valueBytes: " << (stats.size * stats.valueSize) << std::endl;
xo << " overheadBytes: " << stats.overheadBytes << std::endl;
if (stats.size > 0) {
xo << " overheadBytesPerKey: " << (stats.overheadBytes * 1.0 / stats.size)
<< std::endl;
}
xo << "}";
return xo;
}
} // namespace f14 } // namespace f14
} // namespace folly } // namespace folly
......
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