【发布时间】:2017-07-24 02:49:14
【问题描述】:
我最近发现了一件奇怪的事情。似乎用no caching at all 计算 Collatz 序列长度比using std::unordered_map to cache all elements 快 2 倍以上。
请注意,我确实从问题 Is gcc std::unordered_map implementation slow? If so - why? 中得到了提示,并且我尝试利用这些知识使 std::unordered_map 表现得尽可能好(我使用了 g++ 4.6,它的性能确实比最新版本的 g++ 更好,我尝试了为了指定一个合理的初始桶数,我将其设置为完全等于地图必须包含的最大元素数)。
相比之下,using std::vector to cache a few elements 几乎比不使用缓存快 17 倍,比使用 std::unordered_map 快近 40 倍。
是我做错了什么还是这个容器太慢了,为什么?可以让它执行得更快吗?或者,hashmap 本质上是无效的,应该在高性能代码中尽可能避免使用?
有问题的基准是:
#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
【问题讨论】:
-
@WhiZTiM 我做错了什么还是这个容器很慢,为什么?
-
unordered_map只是是对于很多用途来说相当慢。大多数时候std::vector是最快的容器。数据位置 > 其他一切。 -
@WhiZTiM 编辑了问题,现在更好了吗?
-
如果您遇到复杂性问题,我为您感到难过,std::unordered_map 在 Big-O(N) 中返回。 (worst case)
-
你检查过实际值吗?鉴于 999999 的一半大于 65536,因此使用 uint_fast16_t 缓存值 IMO 是可疑的。
标签: c++ performance c++11 caching unordered-map