我怀疑缓存哈希代码是否值得努力。这意味着更改许多代码行以使用具有缓存哈希代码属性的新字符串类。鉴于当前实现在每次查找时执行log(n)个完整的字符串比较,我认为将其减少到基本上每次查找一个字符串遍历(通过哈希函数)是一个巨大的胜利。
有人有缓存字符串哈希代码的经验吗?它是否曾经证明过值得努力?
需要注意的一点。
虽然哈希表可以实现固定时间的查找,但有时也可能出现O(N)的查找情况。尽管这并不常见,但确实会发生。
因此,在使用哈希表时,你总是要付出O(log N)的时间代价,但同时也保证它不会更差。
我没有缓存哈希码的经验,但最近我做了一些工作,将std::map
转换为std::tr1::unordered_map
。有两个想法。首先,请尝试首先对这个相对简单的更改进行分析,因为它有时会使情况变得更糟,这取决于您的代码在做什么。在您尝试进一步优化之前,它可能已经为您自己提供了足够的加速。其次,您的性能分析器对初始化时间的其他90%有何看法?即使您将全局字典内容优化到0时间,您也最多只能将性能提高10%。
不幸的是,您可能会花费很多时间担心缓存友好性。在这方面,Trie类似于您已经拥有的树,而哈希映射可能比天真分配的树更好地行为良好。
此外,我对问题有点困惑。如果您多次查找相同的字符串对象,以使缓存其哈希值值得,那么您不应该只缓存查找结果吗?哈希表的整个重点在于,具有相等值的不同对象哈希到相同的值。如果您没有从包含相同字符的不同字符串多次计算相同的哈希值,则您的哈希表可能无法正常工作。
如果您的意思是缓存已经在哈希表中的键的值,则由哈希表决定。
size_t hash_value(const string_hash_pair& pPair)
{
return pPair.second; // don't actually hash
}
就是这样。请注意,在这个对中,string
和size_t
都是不可变的。这是因为如果string
改变了,你的哈希值就会变得不正确。所以我们将其设为const
,同时也可以将哈希值设为const
。
你需要一个辅助函数:
string_hash_pair make_string_hash(const std::string& pStr)
{
return std::make_pair(pStr, boost::hash_value(pStr));
}
测试集:在MSVC 2012更新4上进行基准测试。每个条目在4k和64k字典中查找10次:
除了10个检查在4k中全部未命中,64k中有500个命中(更多的土豚:)
set:1373毫秒/1938毫秒
multiset:1376毫秒/1913毫秒
unordered_set初始64k桶/0.5负载因子:168毫秒/362毫秒
unordered_set 4k / 1.0:331毫秒/452毫秒
c.f预缓存
unordered_set 64k / 0.5:1519毫秒/1881毫秒
FWIW相同的事情针对MinGW 4.9.1 -O3运行
set:2003毫秒/2490毫秒
multiset:1978毫秒/2306毫秒
unordered_set初始64k桶/0.5负载因子:140毫秒/605毫秒
unordered_set 4k / 1.0:318毫秒/683毫秒
c.f预缓存
unordered_set 64k/0.5: 1619毫秒 / 2455毫秒
hash_map
,那是一个老的扩展。相反,使用TR1或Boost中的unordered_map
。 - GManNickG