gcc std::无序_映射实现慢吗?如果是,为什么? 我们正在开发一个性能很高的C++关键软件。在那里,我们需要一个并发哈希映射,并实现了一个。因此,我们编写了一个基准测试来计算,与std::unordered_map相比,我们的并发哈希映射要慢多少
但是,gcc std::无序_映射实现慢吗?如果是,为什么? 我们正在开发一个性能很高的C++关键软件。在那里,我们需要一个并发哈希映射,并实现了一个。因此,我们编写了一个基准测试来计算,与std::unordered_map相比,我们的并发哈希映射要慢多少,c++,stl,c++11,hashmap,concurrenthashmap,C++,Stl,C++11,Hashmap,Concurrenthashmap,但是,std::unordered_map似乎非常慢。。。这就是我们的微基准测试(对于并发映射,我们生成了一个新线程,以确保锁定不会被优化,并注意我从未插入0,因为我还使用google::dense_hash_map进行基准测试,它需要一个空值): 对于google::densite\u map: inserts: 3653 get : 816 inserts: 16462 get : 16978 对于我们的手动并发映射(虽然基准测试是单线程的,但在一个单独的派生线程中,它执行锁
std::unordered_map
似乎非常慢。。。这就是我们的微基准测试(对于并发映射,我们生成了一个新线程,以确保锁定不会被优化,并注意我从未插入0,因为我还使用google::dense_hash_map
进行基准测试,它需要一个空值):
对于google::densite\u map
:
inserts: 3653
get : 816
inserts: 16462
get : 16978
对于我们的手动并发映射(虽然基准测试是单线程的,但在一个单独的派生线程中,它执行锁定):
如果我在不支持pthread的情况下编译基准程序,并在主线程中运行所有程序,那么对于我们的手动并发映射,我会得到以下结果:
inserts: 4441
get : 1180
我使用以下命令编译:
g++-4.7 -O3 -DNDEBUG -I/tmp/benchmap/sparsehash-2.0.2/src/ -std=c++11 -pthread main.cc
因此,特别是在
std::unordered_map
上插入内容似乎非常昂贵-35秒,而其他贴图为3-5秒。而且查找时间似乎相当长
我的问题:为什么会这样?我读了另一个关于stackoverflow的问题,有人问,为什么
std::tr1::unordered_map
比他自己的实现慢。评分最高的回答指出,std::tr1::unordered_map
需要实现更复杂的接口。但我看不出这个论点:我们在并发映射中使用bucket方法,std::unordered\u映射
也使用bucket方法(google::densite\u hash\u映射
没有,但比std::unordered\u映射
至少应该和我们的手背并发安全版本一样快?)。除此之外,我在界面中看不到任何强制使用使哈希映射性能差的特性的东西
所以我的问题是:
std::unordered\u map
看起来非常慢,这是真的吗?如果没有:怎么了?如果是:原因是什么
我的主要问题是:为什么在
std::unordered_map
中插入一个值会非常昂贵(即使我们在一开始就预留了足够的空间,它的性能也不会好得多,所以重新格式化似乎不是问题)
编辑:
首先:是的,所展示的基准并非完美无瑕——这是因为我们对它进行了大量的研究,它只是一种黑客行为(例如,uint64
发行版来生成int实际上不是一个好主意,在循环中排除0有点愚蠢等等)
目前大多数评论解释说,我可以通过预先分配足够的空间使无序的_图更快。在我们的应用程序中,这是不可能的:我们正在开发一个数据库管理系统,需要一个哈希映射来存储事务期间的一些数据(例如锁定信息)。所以这个映射可以是从1(用户只进行一次插入和提交)到数十亿个条目(如果发生完整表扫描)的所有内容。在这里预先分配足够的空间是不可能的(而且一开始分配很多空间会消耗太多内存)
此外,我很抱歉,我没有把我的问题说得足够清楚:我对快速生成无序的_映射并不感兴趣(使用谷歌的密集散列映射对我们来说很好),我只是不太明白这种巨大的性能差异是从何而来的。它不能仅仅是预分配(即使有足够的预分配内存,密集映射也比无序映射快一个数量级,我们的手动并发映射从一个大小为64的数组开始,因此比无序映射小)
那么,
std::unordered\u map
性能不佳的原因是什么呢?或者另一个问题是:是否可以编写一个std::unordered_-map
接口的实现,该接口符合标准,并且(几乎)与谷歌的密集散列映射一样快?或者标准中是否有强制实施者选择低效的方式来实现它的内容
编辑2:
通过分析,我发现整数除法占用了很多时间
std::unordered_map
使用素数作为数组大小,而其他实现使用二的幂。为什么std::unordered\u map
使用素数?如果哈希值不好,是否执行得更好?对于好的散列,它没有什么区别
编辑3:
以下是std::map
的编号:
inserts: 3653
get : 816
inserts: 16462
get : 16978
SOOOOOO:为什么插入到
std::map
比插入到std::unordered\u map
更快。。。我是说什么std::map
的局部性较差(树与数组),需要进行更多的分配(每次插入与每次重新刷新+每次冲突加1),最重要的是:还有另一个算法复杂性(O(logn)与O(1)) 我猜你没有按照Ylisar的建议正确调整你的无序地图的大小。当链在无序映射中过长时,g++实现将自动重新缓存到更大的哈希表中,这将对性能造成很大的拖累。如果我没记错的话,unordered_map
默认值为(大于的最小素数)100
我的系统上没有chrono
,所以我用times()
计时
编辑:
inserts: 9280
get: 3302
inserts: 23946
get: 24824
inserts: 7289
get: 1908
inserts: 19222
get: 19711
我修改了代码,以便更容易地更改深度
#ifndef DEPTH
#define DEPTH 10000000
#endif
因此,默认情况下,选择哈希表的最坏大小
elapsed: 7.12 inserts, elapsed: 2.32 get, -DDEPTH=10000000
elapsed: 6.99 inserts, elapsed: 2.58 get, -DDEPTH=1000000
elapsed: 8.94 inserts, elapsed: 2.18 get, -DDEPTH=100000
elapsed: 5.23 inserts, elapsed: 2.41 get, -DDEPTH=10000
elapsed: 5.35 inserts, elapsed: 2.55 get, -DDEPTH=1000
elapsed: 6.29 inserts, elapsed: 2.05 get, -DDEPTH=100
elapsed: 6.76 inserts, elapsed: 2.03 get, -DDEPTH=10
elapsed: 2.86 inserts, elapsed: 2.29 get, -DDEPTH=1
我的结论是,对于任何初始哈希表大小,除了使其等于唯一插入的整个预期数量之外,没有太大的性能差异。此外,我没有看到您所观察到的数量级性能差异。我找到了原因:这是gcc-4.7的一个问题
使用gcc-4.7
inserts: 37728
get : 2985
使用gcc-4.6
inserts: 2531
get : 1565
所以std::无序地图inserts: 23946
get: 24824
inserts: 7289
get: 1908
inserts: 19222
get: 19711