Commit 74e8af86 authored by Nathan Bronson's avatar Nathan Bronson Committed by Facebook GitHub Bot

add heterogeneous lookup and eraseInto to fallback F14 sets

Summary:
This diff adds heterogeneous lookup support and eraseInto to the
fallback (non-SIMD) version of F14 sets, which is used on mobile.
std::is_invocable<hasher, const_iterator const&> is no longer evaluated
during erase.  It also restructures F14MapTest and F14SetTest so that
as many of the tests as possible run in the fallback mode.

(Note: this ignores all push blocking failures!)

Reviewed By: yfeldblum, ot

Differential Revision: D21489440

fbshipit-source-id: 872a250f8a6ec3c9efcd19edb7d9beca196fa865
parent ce0007aa
......@@ -70,15 +70,20 @@ class F14BasicMap {
K>::value,
T>;
template <typename K>
using IsIter = Disjunction<
std::is_same<typename Policy::Iter, remove_cvref_t<K>>,
std::is_same<typename Policy::ConstIter, remove_cvref_t<K>>>;
template <typename K, typename T>
using EnableHeterogeneousErase = std::enable_if_t<
EligibleForHeterogeneousFind<
typename Policy::Value,
typename Policy::Key,
typename Policy::Hasher,
typename Policy::KeyEqual,
K>::value &&
!std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value &&
!std::is_same<typename Policy::ConstIter, remove_cvref_t<K>>::value,
std::conditional_t<IsIter<K>::value, typename Policy::Key, K>>::
value &&
!IsIter<K>::value,
T>;
public:
......@@ -973,19 +978,21 @@ class F14VectorMapImpl : public F14BasicMap<MapPolicyWithDefaults<
private:
using Super = F14BasicMap<Policy>;
template <typename K>
using IsIter = Disjunction<
std::is_same<typename Policy::Iter, remove_cvref_t<K>>,
std::is_same<typename Policy::ConstIter, remove_cvref_t<K>>,
std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>,
std::is_same<typename Policy::ConstReverseIter, remove_cvref_t<K>>>;
template <typename K, typename T>
using EnableHeterogeneousVectorErase = std::enable_if_t<
EligibleForHeterogeneousFind<
typename Policy::Value,
typename Policy::Hasher,
typename Policy::KeyEqual,
K>::value &&
!std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value &&
!std::is_same<typename Policy::ConstIter, remove_cvref_t<K>>::value &&
!std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>::
value &&
!std::is_same<typename Policy::ConstReverseIter, remove_cvref_t<K>>::
value,
Key,
Hasher,
KeyEqual,
std::conditional_t<IsIter<K>::value, Key, K>>::value &&
!IsIter<K>::value,
T>;
public:
......
......@@ -66,14 +66,18 @@ class F14BasicSet {
K>::value,
T>;
template <typename K>
using IsIter = std::is_same<typename Policy::Iter, remove_cvref_t<K>>;
template <typename K, typename T>
using EnableHeterogeneousErase = std::enable_if_t<
EligibleForHeterogeneousFind<
typename Policy::Value,
typename Policy::Hasher,
typename Policy::KeyEqual,
K>::value &&
!std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value,
std::conditional_t<IsIter<K>::value, typename Policy::Value, K>>::
value &&
!IsIter<K>::value,
T>;
public:
......@@ -743,15 +747,20 @@ class F14VectorSetImpl : public F14BasicSet<SetPolicyWithDefaults<
private:
using Super = F14BasicSet<Policy>;
template <typename K>
using IsIter = Disjunction<
std::is_same<typename Policy::Iter, remove_cvref_t<K>>,
std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>>;
template <typename K, typename T>
using EnableHeterogeneousVectorErase = std::enable_if_t<
EligibleForHeterogeneousFind<
typename Policy::Value,
typename Policy::Hasher,
typename Policy::KeyEqual,
K>::value &&
!std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value &&
!std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>::value,
std::conditional_t<IsIter<K>::value, typename Policy::Value, K>>::
value &&
!IsIter<K>::value,
T>;
public:
......
......@@ -22,26 +22,295 @@
* required SIMD instructions, based on std::unordered_set.
*/
#include <algorithm>
#include <unordered_set>
namespace folly {
namespace f14 {
namespace detail {
template <typename K, typename H, typename E, typename A>
class F14BasicSet : public std::unordered_set<K, H, E, A> {
using Super = std::unordered_set<K, H, E, A>;
template <typename KeyType, typename Hasher, typename KeyEqual, typename Alloc>
class F14BasicSet
: public std::unordered_set<KeyType, Hasher, KeyEqual, Alloc> {
using Super = std::unordered_set<KeyType, Hasher, KeyEqual, Alloc>;
public:
using typename Super::allocator_type;
using typename Super::const_iterator;
using typename Super::hasher;
using typename Super::iterator;
using typename Super::key_equal;
using typename Super::key_type;
using typename Super::pointer;
using typename Super::size_type;
using typename Super::value_type;
private:
template <typename K, typename T>
using EnableHeterogeneousFind = std::enable_if_t<
EligibleForHeterogeneousFind<key_type, hasher, key_equal, K>::value,
T>;
template <typename K, typename T>
using EnableHeterogeneousInsert = std::enable_if_t<
EligibleForHeterogeneousInsert<key_type, hasher, key_equal, K>::value,
T>;
template <typename K>
using IsIter = Disjunction<
std::is_same<iterator, remove_cvref_t<K>>,
std::is_same<const_iterator, remove_cvref_t<K>>>;
template <typename K, typename T>
using EnableHeterogeneousErase = std::enable_if_t<
EligibleForHeterogeneousFind<
key_type,
hasher,
key_equal,
std::conditional_t<IsIter<K>::value, key_type, K>>::value &&
!IsIter<K>::value,
T>;
public:
F14BasicSet() = default;
using Super::Super;
//// PUBLIC - Modifiers
using Super::insert;
template <typename K>
EnableHeterogeneousInsert<K, std::pair<iterator, bool>> insert(K&& value) {
return emplace(std::forward<K>(value));
}
template <class InputIt>
void insert(InputIt first, InputIt last) {
while (first != last) {
insert(*first);
++first;
}
}
private:
template <typename Arg>
using UsableAsKey =
EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;
public:
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
auto a = this->get_allocator();
return folly::detail::callWithConstructedKey<key_type, UsableAsKey>(
a,
[&](auto const&, auto&& key) {
if (!std::is_same<key_type, remove_cvref_t<decltype(key)>>::value) {
// this is a heterogeneous emplace
auto it = find(key);
if (it != this->end()) {
return std::make_pair(it, false);
}
auto rv = Super::emplace(std::forward<decltype(key)>(key));
FOLLY_SAFE_CHECK(
rv.second, "post-find emplace should always insert");
return rv;
} else {
return Super::emplace(std::forward<decltype(key)>(key));
}
},
std::forward<Args>(args)...);
}
template <class... Args>
iterator emplace_hint(const_iterator /*hint*/, Args&&... args) {
return emplace(std::forward<Args>(args)...).first;
}
using Super::erase;
template <typename K>
EnableHeterogeneousErase<K, size_type> erase(K const& key) {
auto it = find(key);
if (it != this->end()) {
erase(it);
return 1;
} else {
return 0;
}
}
//// PUBLIC - Lookup
private:
template <typename K>
struct BottomKeyEqual {
[[noreturn]] bool operator()(K const&, K const&) const {
FOLLY_SAFE_CHECK(false, "bucket should not invoke key equality");
assume_unreachable();
}
};
template <typename Iter, typename Self, typename K>
static Iter findImpl(Self& self, K const& key) {
if (self.empty()) {
return self.end();
}
using A = typename std::allocator_traits<
allocator_type>::template rebind_alloc<K>;
using E = BottomKeyEqual<K>;
// this is exceedingly wicked!
auto slot =
reinterpret_cast<std::unordered_set<K, hasher, E, A> const&>(self)
.bucket(key);
auto b = self.begin(slot);
auto e = self.end(slot);
while (b != e) {
if (self.key_eq()(key, *b)) {
Iter it;
static_assert(sizeof(it) <= sizeof(b), "");
std::memcpy(&it, &b, sizeof(it));
FOLLY_SAFE_CHECK(
std::addressof(*b) == std::addressof(*it),
"ABI-assuming local_iterator to iterator conversion failed");
return it;
}
++b;
}
FOLLY_SAFE_DCHECK(
self.size() > 3 ||
std::none_of(
self.begin(),
self.end(),
[&](auto const& k) { return self.key_eq()(key, k); }),
"");
return self.end();
}
public:
using Super::count;
template <typename K>
EnableHeterogeneousFind<K, size_type> count(K const& key) const {
return contains(key) ? 1 : 0;
}
using Super::find;
template <typename K>
EnableHeterogeneousFind<K, iterator> find(K const& key) {
return findImpl<iterator>(*this, key);
}
template <typename K>
EnableHeterogeneousFind<K, const_iterator> find(K const& key) const {
return findImpl<const_iterator>(*this, key);
}
bool contains(key_type const& key) const {
return find(key) != this->end();
}
template <typename K>
EnableHeterogeneousFind<K, bool> contains(K const& key) const {
return find(key) != this->end();
}
private:
template <typename Self, typename K>
static auto equalRangeImpl(Self& self, K const& key) {
auto first = self.find(key);
auto last = first;
if (last != self.end()) {
++last;
}
return std::make_pair(first, last);
}
public:
using Super::equal_range;
template <typename K>
EnableHeterogeneousFind<K, std::pair<iterator, iterator>> equal_range(
K const& key) {
return equalRangeImpl(*this, key);
}
template <typename K>
EnableHeterogeneousFind<K, std::pair<const_iterator, const_iterator>>
equal_range(K const& key) const {
return equalRangeImpl(*this, key);
}
//// PUBLIC - F14 Extensions
#if FOLLY_F14_ERASE_INTO_AVAILABLE
private:
// converts const_iterator to iterator when they are different types
// such as in libstdc++
template <typename... Args>
iterator citerToIter(const_iterator cit, Args&&...) {
iterator it = erase(cit, cit);
FOLLY_SAFE_CHECK(std::addressof(*it) == std::addressof(*cit), "");
return it;
}
// converts const_iterator to iterator when they are the same type
// such as in libc++
iterator citerToIter(iterator it) {
return it;
}
public:
template <typename BeforeDestroy>
iterator eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {
iterator next = citerToIter(pos);
++next;
auto nh = this->extract(pos);
if (!nh.empty()) {
beforeDestroy(std::move(nh.value()));
}
return next;
}
template <typename BeforeDestroy>
iterator eraseInto(
const_iterator first,
const_iterator last,
BeforeDestroy&& beforeDestroy) {
iterator pos = citerToIter(first);
while (pos != last) {
pos = eraseInto(pos, beforeDestroy);
}
return pos;
}
private:
template <typename K, typename BeforeDestroy>
size_type eraseIntoImpl(K const& key, BeforeDestroy& beforeDestroy) {
auto it = find(key);
if (it != this->end()) {
eraseInto(it, beforeDestroy);
return 1;
} else {
return 0;
}
}
public:
template <typename BeforeDestroy>
size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {
return eraseIntoImpl(key, beforeDestroy);
}
template <typename K, typename BeforeDestroy>
EnableHeterogeneousErase<K, size_type> eraseInto(
K const& key,
BeforeDestroy&& beforeDestroy) {
return eraseIntoImpl(key, beforeDestroy);
}
#endif
bool containsEqualValue(value_type const& value) const {
// bucket is only valid if bucket_count is non-zero
if (this->empty()) {
......@@ -73,7 +342,8 @@ class F14BasicSet : public std::unordered_set<K, H, E, A> {
visitor(bc * sizeof(pointer), 1);
}
if (this->size() > 0) {
visitor(sizeof(StdNodeReplica<K, value_type, H>), this->size());
visitor(
sizeof(StdNodeReplica<key_type, value_type, hasher>), this->size());
}
}
......
......@@ -81,12 +81,18 @@
#include <xmmintrin.h> // _mm_prefetch
#endif
#ifndef FOLLY_F14_PERTURB_INSERTION_ORDER
#define FOLLY_F14_PERTURB_INSERTION_ORDER folly::kIsDebug
#endif
#else // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
#ifndef FOLLY_F14_PERTURB_INSERTION_ORDER
#define FOLLY_F14_PERTURB_INSERTION_ORDER folly::kIsDebug
#define FOLLY_F14_PERTURB_INSERTION_ORDER false
#endif
#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
namespace folly {
struct F14TableStats {
......
This diff is collapsed.
This diff is collapsed.
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