emplace的性能比emplace的检查要差 [英] performance of emplace is worse than check followed by emplace

查看:393
本文介绍了emplace的性能比emplace的检查要差的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 value_type std :: unordered_map 没有默认构造函数,所以我不能做下面的

I have a std::unordered_map with a value_type that does not have a default constructor so I cannot do the following

auto k = get_key();
auto& v = my_map[k];

我最后写了一个辅助函数

I ended up writing a helper function

value_type& get_value(key_type& key)
{
    return std::get<0>(my_map.emplace(
                              std::piecewise_construct,
                              std::forward_as_tuple(key),
                              std::forward_as_tuple(args_to_construct_value)
                      ))->second;
}

但性能明显更糟糕(即value_type的构造函数出现在perf中)

but the performance was markedly worse (i.e. the value_type's constructor showed up in perf) than the following version.

value_type& get_value(key_type& key)
{
    auto it = my_map.find(key);
    if (it == my_map.end())
        return std::get<0>(my_map.emplace(
                                  std::piecewise_construct,
                                  std::forward_as_tuple(key),
                                  std::forward_as_tuple(args_to_construct_value)
                          ))->second;
    else
        return it->second;
}

我从 std :: unordered_map :: emplace对象创建,emplace需要构造对象以查看是否存在。但是emplace正在检查这个键值对是否在返回之前存在于地图中。

I read from std::unordered_map::emplace object creation that emplace needs to construct the object in order to see if exists. But emplace is checking to see if this key value pair exists in the map before it returns.

我使用emplace的方式错误吗?是否有更好的模式我应该遵循:

Am I using emplace the wrong way? Is there a better pattern I should follow that:


  1. 不会构建我的value_type每次查找(如我的第一个方法)

  2. 不会检查value_type是否存在于我的地图中两次(如我的第二个方法)

感谢

推荐答案

不幸的是,您的代码最适合标准库,因为它目前是最佳的。

Your code is unfortunately optimal for the standard library as it currently is.

问题是 emplace 操作旨在避免复制,而不是为了避免不必要的映射类型。实际上,发生的是实现分配和构造节点,其包含映射 value_type ie pair< const Key,T> ,然后散列密钥以确定所构造的节点是否可以链接到容器中;

The problem is that the emplace operation is designed to avoid copying, not to avoid unnecessary construction of the mapped type. In practical terms, what happens is that the implementation allocates and constructs a node, containing the map value_type i.e. pair<const Key, T>, and then hashes the key to determine whether the constructed node can be linked into the container; if this collides then the node is deleted.

只要 hash equal_to 不太贵,你的代码不应该做太多额外的工作。

As long as hash and equal_to are not too expensive, your code shouldn't do too much extra work.

另一种方法是使用一个自定义分配器拦截0参数构造您的映射类型;问题是检测这样的结构是非常fiddly:

An alternative is to use a custom allocator that intercepts 0-argument construction of your mapped type; the problem is that detecting such construction is pretty fiddly:

#include <unordered_map>
#include <iostream>

using Key = int;
struct D {
    D() = delete;
    D(D const&) = delete;
    D(D&&) = delete;
    D(std::string x, int y) { std::cout << "D(" << x << ", " << y << ")\n"; }
};
template<typename T>
struct A {
    using value_type = T;
    using pointer = T*;
    using const_pointer = T const*;
    using reference = T&;
    using const_reference = T const&;
    template<typename U> struct rebind { using other = A<U>; };
    value_type* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
    void deallocate(T* c, std::size_t n) { std::allocator<T>().deallocate(c, n); }
    template<class C, class...Args> void construct(C* c, Args&&... args) { std::allocator<T>().construct(c, std::forward<Args>(args)...); }
    template<class C> void destroy(C* c) { std::allocator<T>().destroy(c); }

    std::string x; int y;
    A(std::string x, int y): x(std::move(x)), y(y) {}
    template<typename U> A(A<U> const& other): x(other.x), y(other.y) {}
    template<class C, class...A> void construct(C* c, std::piecewise_construct_t pc, std::tuple<A...> a, std::tuple<>) {
        ::new((void*)c)C(pc, a, std::tie(x, y)); }
};

int main() {
    using UM = std::unordered_map<Key, D, std::hash<Key>, std::equal_to<Key>, A<std::pair<const Key, D>>>;
    UM um(0, UM::hasher(), UM::key_equal(), UM::allocator_type("hello", 42));
    um[5];
}

这篇关于emplace的性能比emplace的检查要差的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆