出于性能原因,替代stdext :: hash_map

时间:2010-09-22 11:52:36

标签: c++ c std

我正在开发一个高性能应用程序,其中所有调用都必须合理。我有一个地图,在每个事务开始时使用一次,以进行我想要改进的查找。地图在启动时加载,之后不会更改。

下面地图中的键是std :: string,但如果需要,它可以更改为char数组。 C或C ++作为解决方案很好。

  typedef stdext::hash_map<std:string, int> symbols_t;

是否有人知道任何其他可以消除查询或更快的解决方案?

提前感谢您的帮助。

编辑的其他信息:
1. hash_map目前有350,000个元素 2.每个键值的长度通常在4到10个字符之间 3.通过第三方API的回调接收信息。回调时会给出一个符号,该符号在执行地图查找时用作键值。软件的其余部分是从地图查找返回的int驱动的。

感谢:感谢大家的投入。你给了我一些探索的途径。我一定会尝试这些。我很感激帮助。

7 个答案:

答案 0 :(得分:2)

此地图是完全不变还是在程序调用之间发生变化? 对于常量哈希(在编译时已知),有一个gperf程序,它可以生成快速且有保证的O(1)查找表。

此外,如果您告诉我们为什么以及地图查找的确切速度如何减慢您的代码,它可能有助于您理解问题。

答案 1 :(得分:2)

哈希表通常足够快O(1),我们无法告诉您是否可以在不知道应用程序的整个结构的情况下摆脱哈希表。这可能是不可能的。

我不知道如何实施stdext::hash_map<std::string,T>,但prefix tree可能是更好的解决方案。它相当于具有完美哈希函数的哈希表。

      s
      |
      t
    /   \
   o     a
   |     |
(p,42)   r
         |
       (t,69)

它将为您提供与O(1)最多10次迭代(字符串的最大长度)的字符串对应的值,并将最小化存储密钥的空间成本。

答案 2 :(得分:1)

我想说我们在这里缺乏信息可靠地告诉你该怎么做。

您可能希望更具体地了解查找的内容以及函数的整体算法成本。

如果你用丑陋的黑客来破坏代码以在算法成本为O(n²)的函数中赢得1个恒定的微秒,它可能是O(n),那么你就是在浪费时间在错误的问题上。 / p>

如果没有其他细节,我们无法说出来。

答案 3 :(得分:1)

手动编码更适合您数据的哈希映射。

  1. 简单的哈希函数,足够好
  2. 使用足够大的稀疏C数组,以免数据发生冲突
  3. 确保所有来电均已内联
  4. 确保您永远不会复制或转换字符串
  5. 编写代码以生成此C数组的C源代码。它看起来像(没有条目使用0):

    int symbols[] = { 0,0,0,0,0,0,5,0,0,0,0,0,3,0,0,0,0,0,0,2 /* etc */ };
    

    您编写的代码可以搜索散列函数,其中您的数据不会发生冲突。也许它就像符号(或前4个)的前两个字符一样简单。如果你不关心空间,你不需要为所有可能的数据制作一个完美的哈希值,只需要一个非常适合你所拥有数据的数据。

  6. 数组索引为simple_hash(string& s)

    请记住,如果更改符号,则可能必须重写哈希,当然需要重新生成表。

    编辑:基于@ blaze的答案 - #5中的代码是为您编写的,称为gperf

答案 4 :(得分:1)

如果你确实需要键入字符串的hash_map,那么你可以尝试自定义哈希函数。如果你的字符串在(比如说)前四个字符中大多是唯一的,那么编写一个自定义哈希函数,只查看字符串中的前四个字符,并使hash_map使用它。这是一个例子:

struct CustomStringHash: std::unary_function<std::string, size_t>
{
    size_t operator()(const std::string & s) const
    {
         switch (s.size())
         {
              case 0:
                   return 0;
              case 1:
                   return s[0] + 1;
              case 2:
                   return (s[0] << 8) + s[1];
              default: //3 or more chars long, plus a terminating null
                   return *reinterpret_cast<const uint32_t *>(s.c_str());
         }
    }

如果您的字符串平均为8-12个字符,并且在前四个字符中大多是唯一的,那么自定义散列函数可以显着加快查找速度。

答案 5 :(得分:1)

我们如何建议您如何消除查询,因为您没有告诉我们您的查找内容或原因?我们需要更多的算法细节。

至于性能,是否使用hash_map取决于一些复杂性。 Hashmaps(如果你有一个很好的实现,实际上)O(1)查找,插入。但是持续的开销可能非常高。如果您的条目数量较少,您可能会受到影响,并且可能会受益于std :: map。如果经常访问地图的许多不同元素并且可以考虑使用某种排序数组,那么您也可能会遇到缓存一致性问题。

答案 6 :(得分:1)

这是一篇关于hash_map性能的文章,其中提供了一个应该执行得更好的插入式替换:

http://www.codeproject.com/KB/cross-platform/BenchmarkCppVsDotNet.aspx

以下是更多性能测试的列表:

http://attractivechaos.wordpress.com/2008/10/07/another-look-at-my-old-benchmark/ http://attractivechaos.wordpress.com/2008/08/28/comparison-of-hash-table-libraries/
http://tinodidriksen.com/2009/10/04/cpp-map-speeds-msvc-edition/

经验证明,当超过25.000个元素时,std_ext :: hash_map表现不佳,其中随着元素数量的增加,查找变得更慢。更改为boost :: unordered_map解决了问题。