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, "");
......
This diff is collapsed.
...@@ -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