unordered_map:为什么范围操作效率低下,情况就是这样

unordered_map : why range operation are inefficient, and this is case?

本文关键字:情况 就是这样 效率 操作 map 为什么 范围 unordered      更新时间:2023-10-16

我得到了这个关于在C++中实现通用记忆的例子。但是,正如有人在此评论中注意到的那样,原始代码进行了 2 次查找,而下面的代码只进行了一次查找。

template <typename ReturnType, typename... Args>
std::function<ReturnType (Args...)> memoize(std::function<ReturnType (Args...)> func)
{
    std::map<std::tuple<Args...>, ReturnType> cache;
    return ([=](Args... args) mutable  {
            std::tuple<Args...> t(args...);
auto range = cache.equal_range(t);
if (range.first != range.second) return (*range.first).second;
return (*cache.insert(range.first, func(args...))).second;
    });
}

有人注意到使用unordered_map可能会有更好的性能。但我读过:

对于通过子集进行范围迭代,它们通常效率较低 他们的元素。我不明白为什么范围操作较少 高效,如果上述情况是这些情况之一(因为我们使用 范围(?

正如有人在此评论中注意到的那样,原始代码进行了 2 次查找,而下面的代码只进行了一次查找

好的,因此您尝试使用提示insert来避免对插入点进行冗余对数复杂性搜索。您仍然有与另一个问题相同的错误,您的代码可能如下所示:

cache.insert(range.first, std::make_pair(t, func(args...)));
//           ^hint        ^value_type

(这是链接文档中的重载 #4(。

你可以完全不用第一次查找:如果键存在,insert只是将迭代器返回到现有元素,因此您可以使用乐观插入来编写它 - 这是否更好取决于默认构造的成本,然后分配您的 ReturnType:

auto result = cache.insert(make_pair(t, ReturnType{}));
if (result.second) {
    // insertion succeeded so the value wasn't cached already
    result.first->second = func(args...);
}
return result.first->second;

。使用unordered_map可能会有更好的性能...

std::map查找/插入具有对数复杂度的比例,以及具有恒定复杂度的std::unordered_map比例。哪个实际上更好取决于您有多少条目,以及其他因素,您需要分析以确定。

。[unordered_map] 对于范围迭代的效率较低......

好吧,如果您避免equal_range,则根本不需要范围迭代.

查看您实际使用的操作,并了解哪种数据结构是自然适合的。对于简单的查找和插入,这是您在这里真正需要的,unordered_map可能没问题。 std::map如果你关心键的相对顺序会更好,而你不关心。

这两种结构对键也有不同的要求:map需要一个排序(operator<或自定义比较器(,而unordered_map需要一个定义良好的哈希函数和operator==或自定义谓词。

首先,我不确定std::hash是否支持std::tuple,因此您可能需要提供自定义哈希器才能使用unordered_map

相关文章: