我编写了一个基本程序,它通过将字符串插入字符串 - >整数哈希映射来获取字符串并计算唯一字符的发生率。
我使用std :: tr1 :: unordered_map作为存储,模板化自定义散列函数和自定义相等函数。密钥类型实际上是char*
,而不是太慢std::string
。
然后我改变了相同的代码,使用一个非常非常简单的哈希表(实际上是一个由哈希索引的{key,value}结构数组),具有2的幂大小和线性探测冲突。该计划的速度提高了33%。
鉴于当我使用tr1 :: unordered_map时,我预先设定了哈希表,因此它永远不会增长,并且我使用完全相同的哈希和比较例程,tr1 :: unordered_map做什么会减慢它的速度与可以想象的最基本的哈希映射相比,有50%?
哈希地图类型的代码我在这里说的是“简单”:
typedef struct dataitem {
char* item;
size_t count;
} dataitem_t;
dataitem_t hashtable[HASHTABLE_SIZE] = {{NULL,0}}; // Start off with empty table
void insert(char* item) {
size_t hash = generate_hash(item);
size_t firsthash = hash;
while (true) {
hash &= HASHTABLE_SIZE_MASK; // Bitmasking effect is hash %= HASHTABLE_SIZE
if (hashtable[hash].item == NULL) { // Free bucket
hashtable[hash].item = item;
hashtable[hash].count = 1;
break;
}
if (strcmp(hashtable[hash].item, item) == 0) { // Not hash collision; same item
hashtable[hash].count += 1;
break;
}
hash++; // Hash collision. Move to next bucket (linear probing)
if (hash == firsthash) {
// Table is full. This does not happen because the presizing is correct.
exit(1);
}
}
}
答案 0 :(得分:11)
我希望延长@AProgrammer的答案。
您的哈希地图很简单,因为它是根据您的需求量身定制的。另一方面,std::tr1::unordered_map
必须完成许多不同的任务,并且在所有情况下都做得很好。这需要在所有情况下采用均值 - 性能方法,因此在任何特定领域都不会出色。
哈希容器非常特殊,因为有很多方法可以实现它们,你选择了Open-Addressing,而标准强制实现了一个bucket方法。两者都有不同的权衡,这就是为什么标准实际上强制执行特定实现的一个原因:因此当从一个库切换到另一个库时,性能不会发生显着变化。简单地指定Big-O复杂性/摊销的复杂性在这里是不够的。
你说你指示了unordered_map
关于决赛元素的数量,但是你是否更改了加载因子?在发生碰撞的情况下,链接是众所周知的“坏”(因为缺少内存局部性),并且使用较小的加载因子会有利于扩散元素。
最后,指出一个区别:调整哈希映射大小时会发生什么?通过使用链接,unordered_map
不会移动内存中的元素:
这与您的简单实现相反,后者会产生O(N)
份副本(除非您使用线性重组来分散工作,但这绝对不简单)。
因此,似乎unordered_map
的选择是平滑峰值,代价是平均插入速度较慢。
你可以做一些事情:提供自定义分配器。通过为您的用例编写一个特定的分配器,并一次性分配所有内存(因为您知道将插入多少个对象,并且可以让分配器报告节点有多少内存)。然后以类似堆栈的方式分配节点(简单指针增加)。它应该(稍微)改善性能。
答案 1 :(得分:6)
你的“自制的哈希映射”根本不是哈希映射,它是一个侵入式哈希集。 这就是它更快的原因。就这么简单。
嗯,实际上,侵入式哈希集也不准确,但它是最接近的匹配。
答案 2 :(得分:4)
一般来说,比较不构建相同规格的组件的速度是不公平的。
如果不确切知道您测量的是什么 - 哪种操作混合了哪些载荷因子与现有/缺失数据的混合 - 很难解释差异的来源。
g ++的TR1通过链接解决冲突。这意味着动态分配。但这也可以在高负载水平下提供更好的性能。
答案 3 :(得分:1)
你的“本土”哈希映射比std::tr1::unordered_map
更快 1 ,因为正如你自己所说,你自己开发的哈希映射是“simple”而它< em>不处理检查哈希表是否已满。可能还有许多你在操作之前没有检查过的东西。这可能是您的哈希映射比std::tr1::unordered_map
快的原因。
此外,std::tr1::unordered_map
的性能由实现定义,因此不同的实现将以不同的速度执行。您可以看到它的实现并与您的实施进行比较,因为这是您可以做的第一件事,我相信,这也将在一定程度上回答您的问题。
1。我只是假设你的说法是正确的,并在此基础上我说了上面的事情。