【问题标题】:Alternative to stdext::hash_map for performance reasons出于性能原因替代 stdext::hash_map
【发布时间】:2010-09-22 11:52:36
【问题描述】:

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

下面地图中的键是 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 驱动。

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

【问题讨论】:

  • 我非常怀疑如果将std::string 替换为char*,整体性能会大不相同。但是,这肯定会使代码的可维护性大大降低。
  • 哈希映射是 O(1),因此查找时间仅取决于计算哈希所需的时间。你调查过吗?
  • 我想知道,这是你代码中最大的瓶颈吗?闻起来过早的优化。
  • @skimobear:如果你不知道你的 CPU 时间花在了哪里,你建议如何找到唾手可得的果实?这是优化 101。不要只是猜测,也不要只是遍历整个代码库,试图盲目地优化所有内容:找出需要优化和有益的地方,然后对其进行优化。如果地图只占用应用程序总执行时间的 0.01%,那么优化它完全是浪费时间。
  • @skimobear:你感觉不对。 ;) 除非您有无限的时间进行优化,否则您花在优化非性能关键代码上的每一秒都是 秒,您可以花在重要的地方。因此,最终效果是您通过优化没有可衡量影响的地方来减慢代码

标签: c++ c std


【解决方案1】:

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

此外,如果您告诉我们为什么以及如何准确地映射查找会减慢您的代码,这将有助于理解您的问题。

【讨论】:

  • hash_map 的内容每天都在变化。每天早上它都会从数据库中提取出来。听起来很有趣,我去看看:)
  • gperf 生成用您的数据硬编码的 C++ 源文件。使用 gperf 从您每天早上卸载和加载的数据库中创建一个动态库。
【解决方案2】:

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

我不知道 stdext::hash_map&lt;std::string,T&gt; 是如何实现的,但 prefix tree 可能是一个更好的解决方案。相当于一个具有完美哈希函数的哈希表。

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

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

【讨论】:

    【解决方案3】:

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

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

    如果你用丑陋的 hack 弄乱代码以在一个算法成本为 O(n²) 的函数中赢得 1 个恒定微秒,而它可能是 O(n),那么你就是在错误的问题上浪费时间。

    如果没有额外的细节,我们真的无法判断。

    【讨论】:

    • 我添加了一些附加信息。希望它有所帮助,这就足够了:)
    【解决方案4】:

    如果您确实需要一个以字符串为键的 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】:

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

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

      【讨论】:

      • 在上面添加了一些附加信息。如果还不够,请告诉我。谢谢
      【解决方案6】:

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

      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 个)作为 int 一样简单。如果你不关心空间,你不需要为所有可能的数据做一个完美的散列,只需要一个适合你拥有的数据的快速散列。

      数组索引为simple_hash(string&amp; s)

      请记住,如果您更改了符号,您可能必须重写哈希,并且肯定需要重新生成表。

      编辑:基于@blaze 的回答——#5 中的代码是为您编写的,名为gperf

      【讨论】:

        【解决方案7】:

        这是一篇关于 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/

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-05-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多