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

add visitContiguousRanges iteration API to F14

Summary:
When passing elements of a set or map through a bulk API that understands
arrays, it is a useful optimization to be able to iterator or visit
contiguous ranges rather than individual elements.  This diff adds
visitContiguousRanges to F14 maps and sets, which repeatedly passes a
contiguous range to the visitor.  The range is identified with a begin
and end value_type const*.  Each value_type of the table will be included
in exactly one of the passed ranges, and the number of ranges will be
minimized whenever possible.

Reviewed By: yfeldblum

Differential Revision: D8332897

fbshipit-source-id: a7cd5d670b14faeeef97497cccd4cb0a3b07dd50
parent dc017eb6
......@@ -54,6 +54,8 @@ class F14BasicMap : public std::unordered_map<K, M, H, E, A> {
using Super = std::unordered_map<K, M, H, E, A>;
public:
using typename Super::value_type;
using Super::Super;
F14BasicMap() : Super() {}
......@@ -74,6 +76,14 @@ class F14BasicMap : public std::unordered_map<K, M, H, E, A> {
visitor(
this->size(), sizeof(StdNodeReplica<K, typename Super::value_type, H>));
}
template <typename V>
void visitContiguousRanges(V&& visitor) const {
for (value_type const& entry : *this) {
value_type const* b = std::addressof(entry);
visitor(b, b + 1);
}
}
};
} // namespace detail
} // namespace f14
......@@ -790,6 +800,13 @@ class F14BasicMap {
return table_.visitAllocationClasses(visitor);
}
// Calls visitor with two value_type const*, b and e, such that every
// entry in the table is included in exactly one of the ranges [b,e).
// This can be used to efficiently iterate elements in bulk when crossing
// an API boundary that supports contiguous blocks of items.
template <typename V>
void visitContiguousRanges(V&& visitor) const;
F14TableStats computeStats() const noexcept {
return table_.computeStats();
}
......@@ -878,6 +895,11 @@ class F14ValueMap
void swap(F14ValueMap& rhs) noexcept(Policy::kSwapIsNoexcept) {
this->table_.swap(rhs.table_);
}
template <typename V>
void visitContiguousRanges(V&& visitor) const {
this->table_.visitContiguousItemRanges(visitor);
}
};
template <typename K, typename M, typename H, typename E, typename A>
......@@ -918,6 +940,8 @@ class F14NodeMap
using Super = f14::detail::F14BasicMap<Policy>;
public:
using typename Super::value_type;
F14NodeMap() noexcept(Policy::kDefaultConstructIsNoexcept) : Super{} {}
using Super::Super;
......@@ -926,6 +950,14 @@ class F14NodeMap
this->table_.swap(rhs.table_);
}
template <typename V>
void visitContiguousRanges(V&& visitor) const {
this->table_.visitItems([&](typename Policy::Item ptr) {
value_type const* b = std::addressof(*ptr);
visitor(b, b + 1);
});
}
// TODO extract and node_handle insert
};
......@@ -989,6 +1021,7 @@ class F14VectorMap
using typename Super::const_iterator;
using typename Super::iterator;
using typename Super::key_type;
using typename Super::value_type;
using reverse_iterator = typename Policy::ReverseIter;
using const_reverse_iterator = typename Policy::ConstReverseIter;
......@@ -1148,6 +1181,15 @@ class F14VectorMap
EnableHeterogeneousVectorErase<K, std::size_t> erase(K const& key) {
return eraseUnderlyingKey(key);
}
template <typename V>
void visitContiguousRanges(V&& visitor) const {
auto n = this->table_.size();
if (n > 0) {
value_type const* b = std::addressof(this->table_.values_[0]);
visitor(b, b + n);
}
}
};
template <typename K, typename M, typename H, typename E, typename A>
......
......@@ -70,6 +70,14 @@ class F14BasicSet : public std::unordered_set<K, H, E, A> {
visitor(
this->size(), sizeof(StdNodeReplica<K, typename Super::value_type, H>));
}
template <typename V>
void visitContiguousRanges(V&& visitor) const {
for (value_type const& entry : *this) {
value_type const* b = std::addressof(entry);
visitor(b, b + 1);
}
}
};
} // namespace detail
} // namespace f14
......@@ -619,6 +627,13 @@ class F14BasicSet {
return table_.visitAllocationClasses(visitor);
}
// Calls visitor with two value_type const*, b and e, such that every
// entry in the table is included in exactly one of the ranges [b,e).
// This can be used to efficiently iterate elements in bulk when crossing
// an API boundary that supports contiguous blocks of items.
template <typename V>
void visitContiguousRanges(V&& visitor) const;
F14TableStats computeStats() const noexcept {
return table_.computeStats();
}
......@@ -686,6 +701,11 @@ class F14ValueSet
void swap(F14ValueSet& rhs) noexcept(Policy::kSwapIsNoexcept) {
this->table_.swap(rhs.table_);
}
template <typename V>
void visitContiguousRanges(V&& visitor) const {
this->table_.visitContiguousItemRanges(std::forward<V>(visitor));
}
};
template <typename K, typename H, typename E, typename A>
......@@ -719,6 +739,8 @@ class F14NodeSet
using Super = f14::detail::F14BasicSet<Policy>;
public:
using typename Super::value_type;
F14NodeSet() noexcept(Policy::kDefaultConstructIsNoexcept) : Super{} {}
using Super::Super;
......@@ -726,6 +748,14 @@ class F14NodeSet
void swap(F14NodeSet& rhs) noexcept(Policy::kSwapIsNoexcept) {
this->table_.swap(rhs.table_);
}
template <typename V>
void visitContiguousRanges(V&& visitor) const {
this->table_.visitItems([&](typename Policy::Item ptr) {
value_type const* b = std::addressof(*ptr);
visitor(b, b + 1);
});
}
};
template <typename K, typename H, typename E, typename A>
......@@ -954,6 +984,15 @@ class F14VectorSet
BeforeDestroy&& beforeDestroy) {
return eraseUnderlyingKey(key, beforeDestroy);
}
template <typename V>
void visitContiguousRanges(V&& visitor) const {
auto n = this->table_.size();
if (n > 0) {
value_type const* b = std::addressof(this->table_.values_[0]);
visitor(b, b + n);
}
}
};
template <typename K, typename H, typename E, typename A>
......
......@@ -286,7 +286,7 @@ struct BasePolicy
void afterReset() {}
void prefetchValue(Item const&) {
void prefetchValue(Item const&) const {
// Subclass should disable with prefetchBeforeRehash(),
// prefetchBeforeCopy(), and prefetchBeforeDestroy(). if they don't
// override this method, because neither gcc nor clang can figure
......@@ -730,7 +730,7 @@ class NodeContainerPolicy
src.~Item();
}
void prefetchValue(Item const& item) {
void prefetchValue(Item const& item) const {
prefetchAddr(std::addressof(*item));
}
......@@ -1048,7 +1048,7 @@ class VectorContainerPolicy : public BasePolicy<
*itemAddr = src;
}
void prefetchValue(Item const& item) {
void prefetchValue(Item const& item) const {
prefetchAddr(std::addressof(values_[item]));
}
......
......@@ -338,6 +338,34 @@ class DenseMaskIter {
}
};
// Iterates a mask, returning pairs of [begin,end) index covering blocks
// of set bits
class MaskRangeIter {
MaskType mask_;
unsigned shift_{0};
public:
explicit MaskRangeIter(MaskType mask) {
// If kMaskSpacing is > 1 then there will be empty bits even for
// contiguous ranges. Fill them in.
mask_ = mask * ((1 << kMaskSpacing) - 1);
}
bool hasNext() {
return mask_ != 0;
}
std::pair<unsigned, unsigned> next() {
FOLLY_SAFE_DCHECK(hasNext(), "");
auto s = shift_;
unsigned b = findFirstSetNonZero(mask_);
unsigned e = findFirstSetNonZero(~(mask_ | (mask_ - 1)));
mask_ >>= e;
shift_ = s + e;
return std::make_pair((s + b) / kMaskSpacing, (s + e) / kMaskSpacing);
}
};
// Holds the result of an index query that has an optional result,
// interpreting a mask of 0 to be the empty answer and the index of the
// last set bit to be the non-empty answer
......@@ -556,6 +584,10 @@ struct alignas(max_align_t) F14Chunk {
return DenseMaskIter{occupiedMask()};
}
MaskRangeIter occupiedRangeIter() const {
return MaskRangeIter{occupiedMask()};
}
LastOccupiedInMask lastOccupied() const {
return LastOccupiedInMask{occupiedMask()};
}
......@@ -1855,6 +1887,40 @@ class F14Table : public Policy {
this->visitPolicyAllocationClasses(size(), bc, visitor);
}
// visitor should take an Item const&
template <typename V>
void visitItems(V&& visitor) const {
std::size_t maxChunkIndex = lastOccupiedChunk() - chunks_;
auto chunk = &chunks_[0];
for (std::size_t i = 0; i < maxChunkIndex; ++i, ++chunk) {
auto iter = chunk->occupiedIter();
if (Policy::prefetchBeforeCopy()) {
for (auto piter = iter; piter.hasNext();) {
this->prefetchValue(chunk->citem(piter.next()));
}
}
while (iter.hasNext()) {
visitor(chunk->citem(iter.next()));
}
}
}
// visitor should take two Item const*
template <typename V>
void visitContiguousItemRanges(V&& visitor) const {
std::size_t maxChunkIndex = lastOccupiedChunk() - chunks_;
auto chunk = &chunks_[0];
for (std::size_t i = 0; i < maxChunkIndex; ++i, ++chunk) {
for (auto iter = chunk->occupiedRangeIter(); iter.hasNext();) {
auto be = iter.next();
FOLLY_SAFE_DCHECK(
chunk->occupied(be.first) && chunk->occupied(be.second - 1), "");
Item const* b = chunk->itemAddr(be.first);
visitor(b, b + (be.second - be.first));
}
}
}
private:
static std::size_t& histoAt(
std::vector<std::size_t>& histo,
......
......@@ -16,6 +16,8 @@
#include <folly/container/F14Map.h>
#include <unordered_map>
#include <folly/Conv.h>
#include <folly/FBString.h>
#include <folly/container/test/F14TestUtil.h>
......@@ -1331,6 +1333,46 @@ TEST(F14FastMap, heterogeneousInsert) {
TransparentTrackedEqual<1>>>();
}
template <typename M>
void runVisitContiguousRangesTest() {
M map;
for (int i = 0; i < 1000; ++i) {
map[i] = i;
map.erase(i / 2);
}
std::unordered_map<uintptr_t, bool> visited;
for (auto& entry : map) {
visited[reinterpret_cast<uintptr_t>(&entry)] = false;
}
map.visitContiguousRanges([&](auto b, auto e) {
for (auto i = b; i != e; ++i) {
auto iter = visited.find(reinterpret_cast<uintptr_t>(i));
ASSERT_TRUE(iter != visited.end());
EXPECT_FALSE(iter->second);
iter->second = true;
}
});
}
TEST(F14ValueMap, visitContiguousRanges) {
runVisitContiguousRangesTest<F14ValueMap<int, int>>();
}
TEST(F14NodeMap, visitContiguousRanges) {
runVisitContiguousRangesTest<F14NodeMap<int, int>>();
}
TEST(F14VectorMap, visitContiguousRanges) {
runVisitContiguousRangesTest<F14VectorMap<int, int>>();
}
TEST(F14FastMap, visitContiguousRanges) {
runVisitContiguousRangesTest<F14FastMap<int, int>>();
}
///////////////////////////////////
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
///////////////////////////////////
......@@ -16,6 +16,8 @@
#include <folly/container/F14Set.h>
#include <unordered_map>
#include <folly/Conv.h>
#include <folly/FBString.h>
#include <folly/container/test/F14TestUtil.h>
......@@ -962,6 +964,46 @@ TEST(F14FastSet, heterogeneousInsert) {
TransparentTrackedEqual<1>>>();
}
template <typename S>
void runVisitContiguousRangesTest() {
S set;
for (int i = 0; i < 1000; ++i) {
set.insert(i);
set.erase(i / 2);
}
std::unordered_map<uintptr_t, bool> visited;
for (auto& entry : set) {
visited[reinterpret_cast<uintptr_t>(&entry)] = false;
}
set.visitContiguousRanges([&](auto b, auto e) {
for (auto i = b; i != e; ++i) {
auto iter = visited.find(reinterpret_cast<uintptr_t>(i));
ASSERT_TRUE(iter != visited.end());
EXPECT_FALSE(iter->second);
iter->second = true;
}
});
}
TEST(F14ValueSet, visitContiguousRanges) {
runVisitContiguousRangesTest<F14ValueSet<int>>();
}
TEST(F14NodeSet, visitContiguousRanges) {
runVisitContiguousRangesTest<F14NodeSet<int>>();
}
TEST(F14VectorSet, visitContiguousRanges) {
runVisitContiguousRangesTest<F14VectorSet<int>>();
}
TEST(F14FastSet, visitContiguousRanges) {
runVisitContiguousRangesTest<F14FastSet<int>>();
}
///////////////////////////////////
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
///////////////////////////////////
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