【问题标题】:Under what circumstances will std::unordered_map behave very slow?在什么情况下 std::unordered_map 会表现得很慢?
【发布时间】:2016-06-23 02:32:32
【问题描述】:

我做了一些随机测试,但我无法得出结论。

如果将 1000000 个整数插入到 map 和 unordered_map 中,map 使用的时间是 3 倍。

如果插入 1000000 个字符串,那么 map 使用的时间是 2 倍。

std::unordered_map 在什么情况下会表现得很慢?

提前致谢。

UPD:: gcc 版本 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3)。所有测试都没有 -O2。

代码:

a.cpp:std::map<int, int> M; b.cpp:std::unordered_map<int, int> M;

g(i, 1, 1000000) {
    M[i] = rand() % i;
}

我的测试结果:

yyhs@yyhs-Pro:~/Documents$ g++ a.cpp -o a -g --std=c++11 && time ./a

real    0m0.659s
user    0m0.653s
sys 0m0.004s
yyhs@yyhs-Pro:~/Documents$ g++ b.cpp -o b -g --std=c++11 && time ./b

real    0m0.260s
user    0m0.251s
sys 0m0.008s

yyhs@yyhs-Pro:~/Documents$ g++ a.cpp -o a -g --std=c++11 -O2 && time ./a

real    0m0.290s
user    0m0.282s
sys 0m0.008s
yyhs@yyhs-Pro:~/Documents$ g++ b.cpp -o b -g --std=c++11 -O2 && time ./b

real    0m0.081s
user    0m0.081s
sys 0m0.000s

我的问题是什么情况可能导致 std::unordered_map 变慢。

【问题讨论】:

  • 您没有发布您使用的编译器以及用于构建测试的编译器选项。如果您正在计时“调试”或未优化的构建,那么结果将毫无意义。
  • 非常感谢@PaulMcKenzie。我已经添加了详细信息。
  • 当您说“没有 -O2”时,您是说您正在计时未优化的构建?
  • 然后阅读我的第一条评论。结果毫无意义。时间优化的构建。
  • 当你遇到很多冲突时,哈希映射很慢,和/或哈希函数的评估很慢。你可以用长字符串得到后者。哈希需要考虑整个字符串,而根据树中的节点检查字符串可能只考虑第一个小部分。

标签: c++


【解决方案1】:

像往常一样,这将取决于特定的实现,但这并不完全正确,标准保证std::unordered_map 将渐近优于std::map。只有不变的因素会因实施而异。 std::map 的插入时间为 O(log N),std::unordered_map 的平均插入时间为 O(1)。有关详细信息,请参阅 n3690 中的 §23.4.4.1 和 §23.5.4。

一般来说,std::unordered_map 的性能将大大优于std::map(正如您所观察到的),除非您有很多冲突。您可以通过选择放置在同一存储桶中的键来创建冲突。这需要了解您的散列函数以及从散列值到存储桶的映射,但是如果攻击者可以控制散列表中的键,则攻击者可以利用这些知识使您的程序变慢。出于这个原因,在暴露的应用程序中使用随机散列函数是很常见的。

在病态的情况下,如果您的哈希函数选择不当(评估速度非常慢或产生许多冲突),std::map 的性能可能优于 std::unordered_map。这是非常不典型的。

作为一个小提示,标准库std::unordered_map 往往是一个开放的哈希表,以满足 C++ 标准对迭代器行为的要求。众所周知,这对于许多应用程序来说远非最佳,并且有许多替代的哈希表库,它们的性能甚至更好。

【讨论】:

    猜你喜欢
    • 2011-03-22
    • 1970-01-01
    • 2017-07-24
    • 2016-07-16
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多