Teach map/unordered_map how to optimize 'emplace(Key, T)'.

In cases where emplace is called with two arguments and the first one
matches the key_type we can Key to check for duplicates before allocating.

This patch expands on work done by dexonsmith@apple.com.

llvm-svn: 266498
This commit is contained in:
Eric Fiselier 2016-04-16 00:23:12 +00:00
parent 50154d4ec4
commit 500886841d
7 changed files with 184 additions and 4 deletions

View File

@ -1056,6 +1056,17 @@ public:
return __emplace_unique_extract_key(_VSTD::forward<_Pp>(__x),
__can_extract_key<_Pp, key_type>());
}
template <class _First, class _Second>
_LIBCPP_INLINE_VISIBILITY
typename enable_if<
__can_extract_map_key<_First, key_type, __container_value_type>::value,
pair<iterator, bool>
>::type __emplace_unique(_First&& __f, _Second&& __s) {
return __emplace_unique_key_args(__f, _VSTD::forward<_First>(__f),
_VSTD::forward<_Second>(__s));
}
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
pair<iterator, bool> __emplace_unique(_Args&&... __args) {

View File

@ -1082,6 +1082,16 @@ public:
__can_extract_key<_Pp, key_type>());
}
template <class _First, class _Second>
_LIBCPP_INLINE_VISIBILITY
typename enable_if<
__can_extract_map_key<_First, key_type, __container_value_type>::value,
pair<iterator, bool>
>::type __emplace_unique(_First&& __f, _Second&& __s) {
return __emplace_unique_key_args(__f, _VSTD::forward<_First>(__f),
_VSTD::forward<_Second>(__s));
}
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
pair<iterator, bool> __emplace_unique(_Args&&... __args) {
@ -1116,6 +1126,17 @@ public:
__can_extract_key<_Pp, key_type>());
}
template <class _First, class _Second>
_LIBCPP_INLINE_VISIBILITY
typename enable_if<
__can_extract_map_key<_First, key_type, __container_value_type>::value,
iterator
>::type __emplace_hint_unique(const_iterator __p, _First&& __f, _Second&& __s) {
return __emplace_hint_unique_key_args(__p, __f,
_VSTD::forward<_First>(__f),
_VSTD::forward<_Second>(__s));
}
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
iterator __emplace_hint_unique(const_iterator __p, _Args&&... __args) {

View File

@ -4452,6 +4452,21 @@ template <class _Pair, class _Key, class _First, class _Second>
struct __can_extract_key<_Pair, _Key, pair<_First, _Second>>
: conditional<is_same<typename remove_const<_First>::type, _Key>::value,
__extract_key_first_tag, __extract_key_fail_tag>::type {};
// __can_extract_map_key uses true_type/false_type instead of the tags.
// It returns true if _Key != _ContainerValueTy (the container is a map not a set)
// and _ValTy == _Key.
template <class _ValTy, class _Key, class _ContainerValueTy,
class _RawValTy = typename __unconstref<_ValTy>::type>
struct __can_extract_map_key
: integral_constant<bool, is_same<_RawValTy, _Key>::value> {};
// This specialization returns __extract_key_fail_tag for non-map containers
// because _Key == _ContainerValueTy
template <class _ValTy, class _Key, class _RawValTy>
struct __can_extract_map_key<_ValTy, _Key, _Key, _RawValTy>
: false_type {};
#endif
_LIBCPP_END_NAMESPACE_STD

View File

@ -231,6 +231,62 @@ void testMapEmplace()
assert(c.emplace(std::move(v2)).second == false);
}
}
{
CHECKPOINT("Testing C::emplace(const Key&, ConvertibleToMapped&&)");
Container c;
const Key k(42);
cc->expect<Key const&, int&&>();
assert(c.emplace(k, 1).second);
assert(!cc->unchecked());
{
DisableAllocationGuard g;
const Key k2(42);
assert(c.emplace(k2, 2).second == false);
}
}
{
CHECKPOINT("Testing C::emplace(Key&, Mapped&)");
Container c;
Key k(42);
Mapped m(1);
cc->expect<Key&, Mapped&>();
assert(c.emplace(k, m).second);
assert(!cc->unchecked());
{
DisableAllocationGuard g;
Key k2(42);
assert(c.emplace(k2, m).second == false);
}
}
{
CHECKPOINT("Testing C::emplace(Key&&, Mapped&&)");
Container c;
Key k(42);
Mapped m(1);
cc->expect<Key&&, Mapped&&>();
assert(c.emplace(std::move(k), std::move(m)).second);
assert(!cc->unchecked());
{
DisableAllocationGuard g;
Key k2(42);
Mapped m2(2);
assert(c.emplace(std::move(k2), std::move(m2)).second == false);
}
}
{
CHECKPOINT("Testing C::emplace(ConvertibleToKey&&, ConvertibleToMapped&&)");
Container c;
cc->expect<int&&, int&&>();
assert(c.emplace(42, 1).second);
assert(!cc->unchecked());
{
// test that emplacing a duplicate item allocates. We cannot optimize
// this case because int&& does not match the type of key exactly.
cc->expect<int&&, int&&>();
assert(c.emplace(42, 1).second == false);
assert(!cc->unchecked());
}
}
}
@ -347,6 +403,78 @@ void testMapEmplaceHint()
assert(c.size() == 1);
}
}
{
CHECKPOINT("Testing C::emplace_hint(p, const Key&, ConvertibleToMapped&&)");
Container c;
const Key k(42);
cc->expect<Key const&, int&&>();
It ret = c.emplace_hint(c.end(), k, 42);
assert(ret != c.end());
assert(c.size() == 1);
assert(!cc->unchecked());
{
DisableAllocationGuard g;
const Key k2(42);
It ret2 = c.emplace_hint(c.begin(), k2, 1);
assert(&(*ret2) == &(*ret));
assert(c.size() == 1);
}
}
{
CHECKPOINT("Testing C::emplace_hint(p, Key&, Mapped&)");
Container c;
Key k(42);
Mapped m(1);
cc->expect<Key&, Mapped&>();
It ret = c.emplace_hint(c.end(), k, m);
assert(ret != c.end());
assert(c.size() == 1);
assert(!cc->unchecked());
{
DisableAllocationGuard g;
Key k2(42);
Mapped m2(2);
It ret2 = c.emplace_hint(c.begin(), k2, m2);
assert(&(*ret2) == &(*ret));
assert(c.size() == 1);
}
}
{
CHECKPOINT("Testing C::emplace_hint(p, Key&&, Mapped&&)");
Container c;
Key k(42);
Mapped m(1);
cc->expect<Key&&, Mapped&&>();
It ret = c.emplace_hint(c.end(), std::move(k), std::move(m));
assert(ret != c.end());
assert(c.size() == 1);
assert(!cc->unchecked());
{
DisableAllocationGuard g;
Key k2(42);
Mapped m2(2);
It ret2 = c.emplace_hint(c.begin(), std::move(k2), std::move(m2));
assert(&(*ret2) == &(*ret));
assert(c.size() == 1);
}
}
{
CHECKPOINT("Testing C::emplace_hint(p, ConvertibleToKey&&, ConvertibleToMapped&&)");
Container c;
cc->expect<int&&, int&&>();
It ret = c.emplace_hint(c.end(), 42, 1);
assert(ret != c.end());
assert(c.size() == 1);
assert(!cc->unchecked());
{
cc->expect<int&&, int&&>();
It ret2 = c.emplace_hint(c.begin(), 42, 2);
assert(&(*ret2) == &(*ret));
assert(c.size() == 1);
assert(!cc->unchecked());
}
}
}
@ -414,6 +542,7 @@ void testMultimapInsert()
c.insert(std::begin(ValueList), std::end(ValueList));
assert(!cc->unchecked());
}
}
#endif
#endif

View File

@ -351,6 +351,4 @@ void testMultisetInsert()
}
}
#endif
#endif

View File

@ -26,4 +26,5 @@ int main()
{
testMapInsert<TCT::unordered_map<> >();
testMapEmplace<TCT::unordered_map<> >();
testMapEmplaceHint<TCT::unordered_map<> >();
}

View File

@ -158,6 +158,11 @@ struct AllocatorConstructController {
bool m_allow_unchecked;
int m_expected_count;
void clear() {
m_expected_args = nullptr;
m_expected_count = -1;
}
// Check for and consume an expected construction added by 'expect'.
// Return true if the construction was expected and false otherwise.
// This should only be called by 'Allocator.construct'.