【问题标题】:vector or map, which one to use?矢量或地图,使用哪一个?
【发布时间】:2010-10-02 01:41:34
【问题描述】:

我听过很多人说,如果容器中的预期元素数量相对较少,最好使用std::vector 而不是std::map,即使您只使用容器而不是用于查找迭代。

这背后的真正原因是什么?

显然std::map 的查找性能不能比std::vector 差(尽管可能以纳秒/微秒为单位不同),那么它与内存使用情况有关吗?

std::vector 在分割虚拟地址空间方面是否比std::map 更好/更差?

我正在使用 Visual Studio 附带的 STL 库(即 Microsoft 的实现)。与其他实现相比,这有什么不同吗?

【问题讨论】:

    标签: c++ performance stl


    【解决方案1】:

    我猜你是在比较 map<A, B>vector<pair<A, B> >

    首先,在一个非常小的向量中找到一个项目很容易比在地图中找到相同的项目更快,因为向量中的所有内存总是连续的(因此可以更好地与计算机的缓存和类似的东西一起使用),并且在向量中找到某些东西所需的比较次数可能与在地图中的相同。在非常大的容器的限制下,在地图中查找元素需要较少的操作。

    映射变得比向量快的点取决于实现、处理器、映射中的数据以及处理器缓存中的内存等微妙因素。通常,地图变得更快的点大约是 5-30 个元素。

    另一种方法是使用散列容器。它们通常被命名为hash_mapunordered_map。名为hash_map 的类不是官方标准的一部分(并且有一些变体); std::tr1::unordered_map 是。哈希映射通常比普通映射更快,无论其中有多少元素,但它是否真的更快取决于键是什么,它是如何散列的,你必须处理什么值,以及如何密钥在 std::map 中进行比较。它不会像 std::map 那样保持特定的顺序,但你说过你不关心这个。我会推荐哈希映射,特别是如果键是整数或指针,因为这些哈希非常快。

    【讨论】:

    • 奇怪的是,我发现 Java 的 HashMap 比 C++ Map 快得多。您帖子的最后一段可能描述了原因。
    • @wmac:对:将 Java 的 HashMap 与 C++ hash_mapunordered_map 以及 Java 的 SortedMap 与 C++ map 进行比较更准确。
    • 当我进行基准测试时,我发现 std::map 输出的速度比 std::vector 大约为 8000,但在某些硬件上低至 1000,我使用的代码可在:github.com/BlackToppStudios/DAGFrameScheduler/blob/…
    • “map 变得更快的点大约是 5-30 个元素”——您是假设对元素进行线性搜索还是二进制搜索?
    【解决方案2】:

    “默认情况下,在需要容器时使用矢量”- Bjarne Stroustrup。

    否则,我发现这个小流程图很有帮助(已编辑 - 可能是一个有效的实时新链接):

    https://ngoduyhoa.blogspot.com/2015/06/summary-of-different-containers.html

    【讨论】:

    • 根据 Herb Sutter (gotw.ca/gotw/054.htm) 的说法,如果要在 deque 和 vector 之间进行选择,通常最好选择 deque。
    • 双端队列很好,因为它几乎和向量一样快,但是因为双端队列的块是独立分配的,它不需要移动所有东西来增长。
    • 链接似乎已失效...您还有其他来源吗?
    • 确实死了。这个好像是原图(待确认):ngoduyhoa.blogspot.com/2015/06/…
    【解决方案3】:

    地图通常实现为二叉搜索树,遍历二叉树总是会带来一些开销(执行比较、遍历链接等)。向量基本上只是数组。对于非常少量的数据,可能是 8 或 12 个元素,有时对数组进行线性搜索比遍历二叉搜索树更快。

    您可以自己运行一些计时来查看盈亏平衡点在哪里——计时搜索四个元素,然后是八个,然后是十六个,依此类推,以找到适合您的 STL 特定实现的最佳点。

    映射确实倾向于在整个堆中分配一堆小的分配,而向量是连续的,因此在您从前到后迭代所有元素的情况下,向量的缓存命中率有时会好一些背部。

    【讨论】:

    • 您甚至不必进行线性搜索。 std::lower_bound 为您提供对任何已排序容器的二进制搜索。当有很多键插入,改变搜索树的结构时,映射很有用。如果它是一个相当静态的集合,那么经过排序的向量和 lower_bound 在性能上很容易匹配 map,而不仅仅是几个元素。当然在实践中还是值得比较的!
    【解决方案4】:

    如果您同时进行所有插入然后进行大量查找,则可以使用向量并在插入时对其进行排序;然后使用 lower_bound 进行快速查找。它可能比使用地图更快,即使对于大量项目也是如此。

    【讨论】:

      【解决方案5】:

      我认为您应该首先使用适合数据的容器。 std::vector 用于在 C 或前 STL C++ 中使用数组的情况:您需要一个连续的内存块来存储具有快速常数时间查找的值。 std::map 应该用于将键映射到值。这里的主要重叠是向量与以 size_t 为键的映射。在这种情况下,有两个问题:索引是否连续?如果没有,您可能会用向量浪费内存。第二,你想要什么查找时间?向量具有恒定时间查找,而 std::map 通常实现为 RB 树,其查找时间为 O(log n),甚至哈希映射(例如 TR1 unordered_map)通常具有更差的复杂性,因为索引(或其哈希)将映射到可以包含多个值的存储桶。

      如果针对的是带有对的向量:您可以使用向量的元素并使用 find 来查找元素。但这是一个二分搜索,几乎和 std::map 一样快。

      无论如何,尝试以显而易见的方式对数据进行建模。过早的优化通常没有多大帮助。

      【讨论】:

        【解决方案6】:

        另一种看待这个问题的方式是,如果我们谈论的是小容器,那么任何一个都不会花费很长时间来查找。除非您在非常紧凑的循环中搜索此容器,否则时间差异可能可以忽略不计。

        在这种情况下,我会寻找哪个容器更符合您的要求。如果您正在寻找一个特定的值,map 的内置 find() 方法将比创建 for 循环和遍历向量要容易得多(并且使用起来更简单)。

        你自己的时间可能比这里和那里的几纳秒更有价值。

        【讨论】:

        • 是的,我同意节省的 CPU 时间不值得付出努力。但是内存消耗呢?
        • 我大体上同意,但请注意,std::find() 算法在地图和向量上运行得非常愉快。
        • 如果我们谈论的是少量条目,那么内存消耗总体上会很低......几个字节是多少?我们在说什么……二十? Map 有一个内置的 find...比 std::find() 容易一点。
        【解决方案7】:

        基本上,地图用于查找。

        但是,有时std::vector 可以代替std::map 用于查找。

        如果您的键值对中的元素非常少,那么即使在 std::vector<std::pair<x,y>> 中,您也可以使用键进行迭代搜索。

        这是因为散列需要时间,尤其是散列字符串和 map 中的其他操作,例如在堆中存储数据。

        如果您有更多需要查找的元素,并且当您想要在您拥有的元素列表中进行频繁查找时,您只会在 std::map 中看到更好的差异。

        【讨论】:

        • std::map 不使用散列进行查找。默认情况下它使用 std::less 作为比较器
        猜你喜欢
        • 1970-01-01
        • 2022-01-13
        • 1970-01-01
        • 1970-01-01
        • 2012-12-07
        • 1970-01-01
        • 2011-06-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多