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