为什么 std::unordered_map 很慢,我可以更有效地使用它来缓解这种情况吗?
Why is std::unordered_map slow, and can I use it more effectively to alleviate that?
我最近发现了一件奇怪的事情。似乎在完全没有缓存的情况下计算 Collatz 序列长度比使用std::unordered_map
缓存所有元素快 2 倍以上。
注意我确实从问题中得到了提示 gcc std::unordered_map 实施速度慢吗?如果是这样 - 为什么?我试图利用这些知识使std::unordered_map
表现得尽可能好(我使用了 g++ 4.6,它确实比最新版本的 g++ 表现得更好,我试图指定一个合理的初始桶计数,我让它完全等于地图必须容纳的最大元素数)。
相比之下,使用std::vector
缓存一些元素几乎比完全不缓存快 17 倍,比使用std::unordered_map
快近 40 倍。
我做错了什么还是这个容器那么慢,为什么?它可以更快地执行吗?或者,也许哈希图本质上是无效的,应该在高性能代码中尽可能避免?
有问题的基准是:
#include <iostream>
#include <unordered_map>
#include <cstdint>
#include <ctime>
std::uint_fast16_t getCollatzLength(std::uint_fast64_t val) {
static std::unordered_map <std::uint_fast64_t, std::uint_fast16_t> cache ({{1,1}}, 2168611);
if(cache.count(val) == 0) {
if(val%2 == 0)
cache[val] = getCollatzLength(val/2) + 1;
else
cache[val] = getCollatzLength(3*val+1) + 1;
}
return cache[val];
}
int main()
{
std::clock_t tStart = std::clock();
std::uint_fast16_t largest = 0;
for(int i = 1; i <= 999999; ++i) {
auto cmax = getCollatzLength(i);
if(cmax > largest)
largest = cmax;
}
std::cout << largest << 'n';
std::cout << "Time taken: " << (double)(std::clock() - tStart)/CLOCKS_PER_SEC << 'n';
}
它输出:Time taken: 0.761717
而完全没有缓存的基准测试:
#include <iostream>
#include <unordered_map>
#include <cstdint>
#include <ctime>
std::uint_fast16_t getCollatzLength(std::uint_fast64_t val) {
std::uint_fast16_t length = 1;
while(val != 1) {
if(val%2 == 0)
val /= 2;
else
val = 3*val + 1;
++length;
}
return length;
}
int main()
{
std::clock_t tStart = std::clock();
std::uint_fast16_t largest = 0;
for(int i = 1; i <= 999999; ++i) {
auto cmax = getCollatzLength(i);
if(cmax > largest)
largest = cmax;
}
std::cout << largest << 'n';
std::cout << "Time taken: " << (double)(std::clock() - tStart)/CLOCKS_PER_SEC << 'n';
}
输出Time taken: 0.324586
标准图书馆的地图确实天生很慢(std::map
特别是,但也std::unoredered_map
)。谷歌的钱德勒·卡鲁斯(Chandler Carruth)在他的CppCon 2014演讲中解释了这一点;简而言之:std::unordered_map
缓存不友好,因为它使用链表作为存储桶。
这个SO问题提到了一些有效的哈希映射实现 - 改用其中一个。
相关文章:
- 有效地使用std::unordered_map来插入或增加键的值
- 如何有效地在 std::vector 中插入一对?
- 有效地计算多维数组的累积和?
- 如何有效地计算将单位立方体映射到自身的反射和旋转?
- 有效地将大数存储为 2 的幂用于路径问题
- 如何在C++中写入 1000 个文件时有效地缓冲
- 如何有效地找到数组中三元组和的最小差异?
- 如何在C++中有效地将数字值重新分配给字符数组
- C++有效地找到向量中第一个最接近的匹配值?
- 如何有效地操作满足给定谓词的向量中的所有项目?
- 有效地将数据加载到 std::vector 中<char>
- 为什么 std::unordered_map 很慢,我可以更有效地使用它来缓解这种情况吗?
- 是否存在简单的指向布尔值作为线程取消标志的现实情况,无法有效地取消线程?
- RAII 是否可以在不同步的情况下有效地在线程之间共享不可变对象
- 在不使用密钥的情况下有效地搜索哈希表
- 即使在文件描述符不可用的情况下,也能有效地侦听多个套接字
- 在不使用“protected”的情况下,子类如何有效地使用基类中定义的变量
- 如何在这种情况下有效地使用intrusive_ptr
- 如何在不重新建立堆不变量的情况下有效地替换堆的顶部元素
- 如何在没有库的情况下有效地对双精度数组进行排序