【问题标题】:Why is vector faster than map in one test, but not the other?为什么矢量在一项测试中比地图快,而在另一项测试中则不然?
【发布时间】:2012-03-09 07:47:07
【问题描述】:

一直有人告诉我矢量速度很快,而且在我多年的编程经验中,我从未见过任何可以收缩的东西。我决定(过早地优化和)编写一个关联类,它是一个围绕顺序容器(即::std::vector 并提供与::std::map 相同的接口。大部分代码非常简单,我可以使用它难度不大。

但是,在我对各种大小的 POD 类型(4 到 64 字节)和 std::strings(计数从 8 到 2000 不等)的测试中,::std::map::find 比我的 ::associative::find 快,通常在 15% 左右更快,几乎适用于所有测试。我做了一个Short, Self Contained, Correct (Compilable), Example 清楚地显示了这个at ideone 我检查了MSVC9 的::std::map::find 实现并确认它与我的vecfind::std::lower_bound 代码非常匹配,并且无法解释为什么::std::map::find 运行得更快,除了关于 Stack Overflow 的讨论之外,人们推测二进制搜索方法在最后一次比较之前根本不会从向量的局部性中受益(使其不更快),并且它需要 ::std::map 节点不使用的指针算法t 需要,使其变慢。

今天有人在这方面向我提出挑战,并提供了this code at ideone,当我测试时,它显示向量的速度是原来的两倍多。

StackOverflow 的编码人员是否想就这种明显的差异向我提供启发?我已经检查了这两组代码,它们对我来说似乎是平等的,但也许我对它们都玩这么多是盲目的。

(脚注:这 非常接近 one of my previous questions,但我的代码有几个已解决的错误。由于新的信息/代码,我觉得这足以证明一个单独的问题. 如果没有,我会努力合并它们。)

【问题讨论】:

  • 您是否尝试过分析这两种实现?老实说,仅仅经常看代码并没有真正的帮助。导航具体数据应该为您指明大方向。
  • 由于所有内容都被内联,我的分析器给我的信息比我从输出中得到的信息要少。
  • 过多的内联会减慢速度,因为您不断地从 CPU 缓存中弹出内容。由于您在 Windows 上编译代码大小而不强制任何内联和测量。
  • 这不应该过多地掩盖问题,具体取决于您的分析方式。您的分析方法是什么?样品还是跟踪?为了获得更准确的读数,我会更改您的测试。只是将 1 个值与另一个值进行简单比较是没有意义的。每个测试至少运行 100 次,然后将结果时间除以 100。它会给你一个更准确的时间。
  • 我发现您在 ideone.com 上发布的代码 (ideone.com/41iKt) 存在一些问题。 (ideone 实际上显示矢量更快,但使用 VS 2011 dp 的本地构建显示地图更快)首先我移动了地图变量并使用它来初始化矢量以获得相同的元素排序和唯一性,然后我给了lower_bound a只比较 first 的自定义比较器,因为这就是 map 要做的事情。在这些更改之后,VS2011 显示相同数量元素的向量更快(尽管 ideone 时间没有显着变化)。 ideone.com/b3OnH

标签: c++ map vector profiling


【解决方案1】:

是什么让您认为mapfind()vecfind() 快?

您的代码的 ideone 输出报告的 mapfind()vecfind() 多 50% 的滴答声。在此处运行代码(x86_64 linux,g++-4.5.1),mapfind() 大约是vecfind() 的两倍。

将地图/矢量放大 10 倍,差异增加到大约 3 倍。

但是请注意,第二个组件的总和是不同的。该地图仅包含一对具有任何给定的第一个组件(使用我的本地 PRNG,它创建了一个短两个元素的地图),而 vector 可以包含多个这样的对。

【讨论】:

  • 使用我的 WinXP 32 位,mapfind 需要 266 个滴答声,而 vecfind 需要 391 个滴答声。使用 WinXP 32 位上的 minGW,mapfind 需要 156 个滴答声,而 vecfind 需要 188 个滴答声。无论哪种情况,mapfind 都比 vecfind 快得多
  • 有趣。 vecfind 在我的盒子和 ideone 上明显更快。我认为ideone的机器也是32位的,所以它不会是架构。不过ideone的编译器也是gcc/g++ 4.5.1,所以可能是编译器。你用的是什么编译器?
  • 那些是MSVC++9和G++ 4.5.4 20111030,我也用MSVC++10测试过。
  • 啊哈。我不希望 gcc 4.5.4 与 4.5.1 对 mapvector 的相对性能产生巨大差异,所以我最好的猜测是它是 Windows 与 Linux 的事情。但是这里 map 对于小尺寸也更快,但是 vector 已经超过了 map 250 到 300 个元素。
【解决方案2】:

您放入测试容器的元素数量超过了 Microsoft 中 rand() 的可能输出数量,因此您得到了重复的数字。排序后的向量将包含所有这些,而 map 将丢弃重复项。填充后检查大小 - 向量将有 100000 个元素,地图 32768。由于地图要短得多,当然它会有更好的性能。

尝试multimap 进行比较。

【讨论】:

  • 呸,我之前的测试使用了rand()*RAND_MAX+rand(),但这次没有这样做,因为这个测试套件开始时比较每个容器中的 8 个对象。哎呀。好决定。 barmes53 首先发现了它,然后发现了另一个错误。
【解决方案3】:

我发现您在 ideone.com 上发布的代码 (http://ideone.com/41iKt) 存在一些问题。 (ideone 实际上显示 vector 更快,但使用 Visual Studio 11 开发人员预览版的本地构建显示 map 更快)。

首先我移动了 map 变量并使用它来初始化向量以获得相同的元素排序和唯一性,然后我给了lower_bound 一个只比较 first 的自定义比较器,因为这就是 map 要做的事情。在这些更改之后,Visual Studio 11 显示相同 100,000 个元素的向量更快(尽管 ideone 时间没有显着变化)。 http://ideone.com/b3OnH

test_size 减少到 8 个地图仍然更快。这并不奇怪,因为这是算法复杂性的工作方式,函数中真正描述运行时间的所有常量都与小 N 很重要。我必须将 test_size 提高到大约 2700 以使向量拉平然后领先在这个系统上映射。

【讨论】:

  • 根据要求迁移到评论中回答
【解决方案4】:

排序的 std::vector 比 std::map 有两个优点:

  • 更好的数据局部性:向量将所有数据连续存储在内存中
  • 更小的内存占用:向量不需要太多的簿记数据(例如,没有树节点对象)

这两种影响是否重要取决于场景。有两个因素可能会产生重大影响:

数据类型

如果元素是像整数这样的原始类型,则对 std::vector 来说是一个优势。在这种情况下,位置确实很有帮助,因为搜索所需的所有数据都位于内存中的连续位置。

如果元素是字符串,那么局部性并没有太大帮助。连续向量内存现在只存储可能遍布堆的指针对象。

数据大小

如果 std::vector 适合特定的缓存级别但 std::map 不适合,则 std::vector 将具有优势。 尤其是如果你不断重复测试相同的数据。

【讨论】:

  • 这并没有解释测试中的差异,也没有解释为什么在我的测试中,带有 8 pair<int,int> 的向量比带有 8 pair<int,int> 的地图慢 15%。你读过这个问题吗?
猜你喜欢
  • 2021-03-31
  • 2020-12-11
  • 1970-01-01
  • 2019-04-10
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 2011-04-14
  • 2014-01-25
相关资源
最近更新 更多