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,43 +240,62 @@ void runRehash() { ...@@ -240,43 +240,62 @@ 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 t0;
T t1; T t1;
R r0; R r0;
R r1; R r1;
std::size_t rollbacks = 0;
for (std::size_t reps = 0; reps < 100000; ++reps) {
if (pctDist(gen) < 20) {
// 10% chance allocator will fail after 0 to 3 more allocations
limitTestAllocations(gen() & 3);
} else {
unlimitTestAllocations();
}
bool leakCheckOnly = false;
for (std::size_t reps = 0; reps < 10000; ++reps) {
// discardBits will be from 0 to 62 // discardBits will be from 0 to 62
auto discardBits = (uint64_t{1} << bitsBitsDist(gen)) - 2; auto discardBits = (uint64_t{1} << bitsBitsDist(gen)) - 2;
auto k = gen() >> discardBits; auto k = gen() >> discardBits;
auto v = gen(); auto v = gen();
auto pct = pctDist(gen); auto pct = pctDist(gen);
try {
EXPECT_EQ(t0.empty(), r0.empty()); EXPECT_EQ(t0.empty(), r0.empty());
EXPECT_EQ(t0.size(), r0.size()); 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) { if (pct < 15) {
// insert // insert
auto t = t0.insert(std::make_pair(k, v)); auto t = t0.insert(std::make_pair(k, v));
auto r = r0.insert(std::make_pair(k, v)); auto r = r0.insert(std::make_pair(k, v));
EXPECT_EQ(*t.first, *r.first); EXPECT_EQ(t.first->first, r.first->first);
EXPECT_EQ(t.first->second.val_, r.first->second.val_);
EXPECT_EQ(t.second, r.second); EXPECT_EQ(t.second, r.second);
} else if (pct < 25) { } else if (pct < 25) {
// emplace // emplace
auto t = t0.emplace(k, v); auto t = t0.emplace(k, v);
auto r = r0.emplace(k, v); auto r = r0.emplace(k, v);
EXPECT_EQ(*t.first, *r.first); EXPECT_EQ(t.first->first, r.first->first);
EXPECT_EQ(t.first->second.val_, r.first->second.val_);
EXPECT_EQ(t.second, r.second); EXPECT_EQ(t.second, r.second);
} else if (pct < 30) { } else if (pct < 30) {
// bulk insert // bulk insert
t0.insert(r1.begin(), r1.end()); leakCheckOnly = true;
t0.insert(t1.begin(), t1.end());
r0.insert(r1.begin(), r1.end()); r0.insert(r1.begin(), r1.end());
} else if (pct < 40) { } else if (pct < 40) {
// erase by key // erase by key
...@@ -329,7 +348,8 @@ void runRandom() { ...@@ -329,7 +348,8 @@ void runRandom() {
auto r = r0.find(k); auto r = r0.find(k);
EXPECT_EQ((t == t0.end()), (r == r0.end())); EXPECT_EQ((t == t0.end()), (r == r0.end()));
if (t != t0.end() && r != r0.end()) { if (t != t0.end() && r != r0.end()) {
EXPECT_EQ(*t, *r); EXPECT_EQ(t->first, r->first);
EXPECT_EQ(t->second.val_, r->second.val_);
} }
EXPECT_EQ(t0.count(k), r0.count(k)); EXPECT_EQ(t0.count(k), r0.count(k));
} else if (pct < 60) { } else if (pct < 60) {
...@@ -338,7 +358,8 @@ void runRandom() { ...@@ -338,7 +358,8 @@ void runRandom() {
auto r = r0.equal_range(k); auto r = r0.equal_range(k);
EXPECT_EQ((t.first == t.second), (r.first == r.second)); EXPECT_EQ((t.first == t.second), (r.first == r.second));
if (t.first != t.second && r.first != r.second) { if (t.first != t.second && r.first != r.second) {
EXPECT_EQ(*t.first, *r.first); EXPECT_EQ(t.first->first, r.first->first);
EXPECT_EQ(t.first->second.val_, r.first->second.val_);
t.first++; t.first++;
r.first++; r.first++;
EXPECT_TRUE(t.first == t.second); EXPECT_TRUE(t.first == t.second);
...@@ -348,11 +369,11 @@ void runRandom() { ...@@ -348,11 +369,11 @@ void runRandom() {
// iterate // iterate
uint64_t t = 0; uint64_t t = 0;
for (auto& e : t0) { for (auto& e : t0) {
t += e.first * 37 + e.second + 1000; t += e.first * 37 + e.second.val_ + 1000;
} }
uint64_t r = 0; uint64_t r = 0;
for (auto& e : r0) { for (auto& e : r0) {
r += e.first * 37 + e.second + 1000; r += e.first * 37 + e.second.val_ + 1000;
} }
EXPECT_EQ(t, r); EXPECT_EQ(t, r);
} else if (pct < 69) { } else if (pct < 69) {
...@@ -373,38 +394,51 @@ void runRandom() { ...@@ -373,38 +394,51 @@ void runRandom() {
} else if (pct < 74) { } else if (pct < 74) {
// default construct with capacity // default construct with capacity
std::size_t capacity = k & 0xffff; std::size_t capacity = k & 0xffff;
t0.~T(); T t(capacity);
new (&t0) T(capacity); t0 = std::move(t);
r0.~R(); R r(capacity);
new (&r0) R(capacity); r0 = std::move(r);
} else if (pct < 80) { } else if (pct < 80) {
// bulk iterator construct // bulk iterator construct
t0.~T(); t0 = T{t1.begin(), t1.end()};
new (&t0) T(r1.begin(), r1.end()); r0 = R{r1.begin(), r1.end()};
r0.~R();
new (&r0) R(r1.begin(), r1.end());
} else if (pct < 82) { } else if (pct < 82) {
// initializer list construct // initializer list construct
auto k2 = gen() >> discardBits; auto k2 = gen() >> discardBits;
auto v2 = gen(); auto v2 = gen();
t0.~T(); T t({{k, v}, {k2, v}, {k2, v2}});
new (&t0) T({{k, v}, {k2, v}, {k2, v2}}); t0 = std::move(t);
r0.~R(); R r({{k, v}, {k2, v}, {k2, v2}});
new (&r0) 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) { } else if (pct < 88) {
// copy construct // 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(); t0.~T();
new (&t0) T(t1); new (&t0) T(std::move(t1));
r0.~R(); r0.~R();
new (&r0) R(r1); new (&r0) R(std::move(r1));
} else if (pct < 90) { } else if (pct < 90) {
// move construct // move construct
t0.~T(); t0.~T();
new (&t0) T(std::move(t1)); auto ta = t1.get_allocator();
new (&t0) T(std::move(t1), ta);
r0.~R(); r0.~R();
new (&r0) R(std::move(r1)); auto ra = r1.get_allocator();
new (&r0) R(std::move(r1), ra);
} else if (pct < 94) { } else if (pct < 94) {
// copy assign // copy assign
leakCheckOnly = true;
t0 = t1; t0 = t1;
r0 = r1; r0 = r1;
} else if (pct < 96) { } else if (pct < 96) {
...@@ -429,7 +463,33 @@ void runRandom() { ...@@ -429,7 +463,33 @@ void runRandom() {
r0.reserve(static_cast<std::size_t>(target)); r0.reserve(static_cast<std::size_t>(target));
} }
} }
} catch (std::bad_alloc const&) {
++rollbacks;
F14TableStats::compute(t0);
if (leakCheckOnly) {
unlimitTestAllocations();
t0.clear();
for (auto&& kv : r0) {
t0[kv.first] = kv.second.val_;
}
}
assert(t0.size() == r0.size());
for (auto&& kv : r0) {
auto t = t0.find(kv.first);
EXPECT_TRUE(
t != t0.end() && t->first == kv.first &&
t->second.val_ == kv.second.val_);
}
} }
}
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