【问题标题】:Cost of using std::map with std::string keys vs int keys?将 std::map 与 std::string 键与 int 键一起使用的成本?
【发布时间】:2010-12-23 00:20:01
【问题描述】:

我知道单个地图查询最多需要 log(N) 时间。但是我想知道,我已经看到很多使用字符串作为映射键的示例。例如,将 std::string 关联为映射的键而不是 int 的性能成本是多少?

std::map<std::string, aClass*> someMap;std::map<int, aClass*> someMap;

谢谢!

【问题讨论】:

  • 很容易自己写一个小测试我会想到的。然而,整数总是至少和字符串一样快,而且可能快得多。
  • 下一个问题:如果您将映射更改为采用整数而不是字符串,那么您自己进行转换会损失多少性能?
  • @David:这取决于很多事情,但也可能相当多。每次插入和搜索操作的附加成本为O(L)(L:字符串大小),但整个操作只执行一次:O(L)+O(log N),这将是O(L)O(log N),以较大者为准。如果key保存为字符串,则在所有节点中进行比较,代价为O(L)*O(log N)O(L*log N)

标签: c++ performance stl


【解决方案1】:

成本差异将与比较两个整数与比较两个字符串之间的成本差异相关联。

比较两个字符串时,您必须取消引用指针以获取第一个字符,然后比较它们。如果它们相同,则必须比较第二个字符,依此类推。如果你的字符串有一个长的公共前缀,这可能会减慢这个过程。不过,它不太可能像比较整数一样快。

【讨论】:

    【解决方案2】:

    成本当然是可以在实际 O(1) 时间内比较整数,而在 O(n) 时间内比较字符串(n 是最大共享前缀)。此外,字符串的存储比整数的存储消耗更多的空间。 除了这些明显的差异之外,没有重大的性能成本。

    【讨论】:

      【解决方案3】:

      除了已经提到的比较字符串的时间复杂度之外,每次将项目添加到容器中时,字符串键还会导致额外的内存分配。在某些情况下,例如高度并行的系统,全局分配器互斥锁可能是性能问题的根源。

      一般来说,您应该选择最适合您的情况的替代方案,并且仅根据实际性能测试进行优化。众所周知,很难判断什么是瓶颈。

      【讨论】:

        【解决方案4】:

        分析渐近性能的算法正在研究必须执行的操作以及它们添加到等式中的成本。为此,您需要首先了解执行的操作是什么,然后评估其成本。

        在平衡二叉树(恰好是映射)中搜索键需要O( log N ) 复杂的操作。这些操作中的每一个都意味着比较键是否匹配,如果键不匹配,则遵循适当的指针(子)。这意味着总成本与log N 乘以这两个操作的成本成正比。跟随指针是一个常数时间操作O(1),比较键依赖于键。对于整数键,比较很快O(1)。比较两个字符串是另一回事,它所花费的时间与所涉及的字符串的大小成正比 O(L)(我有意使用 L 作为 字符串长度 参数,而不是更常见的 @987654327 @。

        当您将所有成本加起来时,您会得到使用整数作为键的总成本是O( log N )*( O(1) + O(1) ),相当于O( log N )。 (O(1) 隐藏在 O 符号默默隐藏的常量中。

        如果使用字符串作为键,总成本为O( log N )*( O(L) + O(1) ),其中常数时间运算被成本更高的线性运算O(L) 隐藏,并且可以转换为O( L * log N )。也就是说,在以字符串为键的映射中定位元素的成本与映射中存储的元素数量的对数乘以用作键的字符串的平均长度成正比。

        请注意,大 O 表示法最适合用作分析工具,以确定当问题规模扩大时算法的行为方式,但它隐藏了许多对原始性能很重要的事实。

        作为最简单的示例,如果您将键从通用字符串更改为包含 1000 个字符的数组,则可以将该成本隐藏在从符号中删除的常量中。比较 1000 个字符的数组是一个恒定的操作,只是碰巧需要相当多的时间。使用渐近符号,这将只是一个 O( log N ) 操作,就像整数一样。

        许多其他隐藏成本也是如此,因为创建元素的成本通常被认为是一个常数时间操作,只是因为它不依赖于您的问题的参数(定位块的成本)每次分配的内存不取决于你的数据集,而是取决于算法分析范围之外的内存碎片,在 malloc 内部获取锁的成本,以保证不会有两个进程试图返回相同的块内存取决于锁的争用,而锁的争用取决于处理器的数量、进程以及它们执行多少内存请求……,同样超出了算法分析的范围)。在阅读大 O 符号中的成本时,您必须意识到它的真正含义。

        【讨论】:

        • 字符串比较可能是 O(N) 最坏的情况,但平均情况通常要好得多。事实上,对于 2 个随机字符串,它是 O(1) !
        【解决方案5】:

        首先,我怀疑在实际应用程序中,无论您使用的是字符串键还是整数键,都会产生明显的差异。分析您的应用程序会告诉您它是否重要。

        如果它确实重要,您可以将您的密钥更改为这样的东西(未经测试):

        class Key {
        public:
            unsigned hash;
            std::string s;
        
            int cmp(const Key& other) {
                int diff = hash - other.hash;
                if (diff == 0)
                    diff = strcmp(s, other.s);
                return diff;
        }
        

        现在您正在对两个字符串的哈希值进行 int 比较。如果哈希值不同,则字符串肯定不同。如果哈希相同,您仍然需要比较字符串,因为Pigeonhole Principle

        【讨论】:

          【解决方案6】:

          一个简单的例子,只使用相同数量的键访问两个映射中的值 - 一个 int 键另一个具有相同 int 值的字符串使用字符串需要 8 倍的时间。

          【讨论】:

          • 您能否为这些数字提供参考或示例?
          猜你喜欢
          • 2011-06-23
          • 1970-01-01
          • 2017-08-13
          • 1970-01-01
          • 2010-10-22
          • 2015-09-15
          • 2010-09-22
          • 1970-01-01
          • 2019-03-15
          相关资源
          最近更新 更多