【发布时间】:2016-04-04 21:40:00
【问题描述】:
我编写了一些代码来测试我的无序地图性能,其中以 2 分量向量为键。
std::unordered_map<Vector2i, int> m;
for(int i = 0; i < 1000; ++i)
for(int j = 0; j < 1000; ++j)
m[Vector2i(i,j)] = i*j+27*j;
clock.restart();
auto found = m.find(Vector2i(0,5));
std::cout << clock.getElapsedTime().asMicroseconds() << std::endl;
上述代码的输出:56(微秒) 当我将 for 循环中的 1000 替换为 100 时,输出为 2(微秒) 时间不应该是恒定的吗?
我的 Vector2i 的散列函数:
namespace std
{
template<>
struct hash<Vector2i>
{
std::size_t operator()(const Vector2i& k) const
{
using std::size_t;
using std::hash;
using std::string;
return (hash<int>()(k.x)) ^ (hash<int>()(k.y) << 1);
}
};
}
编辑: 我添加了这段代码来计算 for 循环后的冲突:
for (size_t bucket = 0; bucket != m.bucket_count(); ++bucket)
if (m.bucket_size(bucket) > 1)
++collisions;
使用 100*100 个元素:碰撞次数 = 256
1000*1000 个元素:碰撞次数 = 2048
【问题讨论】:
-
如果你有很多哈希冲突,你的哈希映射会退化成一个链表,也许就是这样。
-
尝试为您的类型添加一些填充,使其占据整个缓存行。在小尺寸下,缓存位置可能是主要因素。
-
@aqww 是的,这本身就是一门艺术;)This (and links within) 可能有用。
-
您使用的是哪个编译器? Visual C++ 使用 2 次幂的存储桶计数,使用如此弱的散列函数会更糟,而 GCC 使用素数。有碰撞的桶的数量并不是最有意义的计算——这些碰撞链有多深是非常重要的。而且,您只对单个查找进行计时,因此对于您当前的基准测试,只有
bucket_size与m.bucket(Vector2i(0,5))相关。不管怎样,试试boost::hash_combine或algo therefrom - 会有帮助的。 -
@TonyD 我使用 GCC 编译器。你是对的,我的碰撞计数不好,更好的方法是每个桶的碰撞 += bucket_size - 1。答案所建议的 hash_combine 解决了我的问题。 (现在我的时间几乎是固定的)
标签: c++ c++11 hashtable time-complexity unordered-map