为什么矢量在一个测试中比地图快,而在另一个测试则不然

Why is vector faster than map in one test, but not the other?

本文关键字:测试 地图 另一个 为什么 一个      更新时间:2023-10-16

我一直被告知矢量很快,在我多年的编程经验中,我从未见过任何与之相关的东西。我决定(提前优化和)编写一个关联类,它是一个围绕顺序容器(即::std::vector)的薄包装器,并提供与::std::map相同的接口

然而,在我对各种大小的POD类型(4到64字节)和std::strings(计数从8000到2000不等)的测试中,对于几乎所有的测试,::std::map::find都比我的::associative::find快,通常快15%左右。我做了一个简短、自包含、正确(可编译)的例子,在视频中清楚地显示了这一点。我检查了MSVC9对::std::map::find的实现,并确认它与我的vecfind::std::lower_bound代码非常匹配,无法解释为什么::std::map::find运行得更快,除了在Stack Overflow上的一次讨论,人们猜测二进制搜索方法在上次比较之前根本不会从矢量的位置中受益(不会使其更快),并且它需要::std::map节点不需要的指针算法,从而使其更慢。

今天有人向我提出了挑战,并在ideone上提供了这段代码,当我测试时,它显示矢量的速度是原来的两倍多。

StackOverflow的编码人员想让我了解这个明显的差异吗?我已经研究了这两组代码,它们对我来说似乎很有用,但也许我对它们玩得太多是盲目的。

(脚注:这与我之前的一个问题非常接近,但我的代码有几个错误得到了解决。由于新的信息/代码,我觉得这已经足够不同了,可以证明单独的问题是合理的。如果没有,我将努力合并它们。)

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

代码的视频输出报告mapfind()的滴答声比vecfind()的滴答声多50%。在这里运行代码(x86_64-linux,g++-4.5.1),mapfind()的时间大约是vecfind()的两倍。

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

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

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

尝试使用multimap进行苹果与苹果的比较。

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

首先,我移动了map变量,并用它初始化向量,以获得相同的元素排序和uniquing,然后我给lower_bound一个自定义比较器,它只比较first,因为这就是map要做的。在这些变化之后,Visual Studio 11显示,对于相同的100000个元素,矢量的速度更快(尽管视频时间没有显著变化)。http://ideone.com/b3OnH

随着test_size减少到8映射仍然更快。这并不奇怪,因为这就是算法复杂性的工作方式,函数中所有真正描述运行时物质的常数都是小N。我必须将test_size提高到2700左右,才能使向量在这个系统上达到偶数,然后领先于映射。

排序的std::vector比std::map:有两个优点
  • 更好的数据局部性:矢量将所有数据连续存储在内存中
  • 较小的内存占用:矢量不需要太多的记账数据(例如,没有树节点对象)

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

数据类型

如果元素是像整数这样的基元类型,那么std::向量是一个优势。在这种情况下,局部性确实有帮助,因为搜索所需的所有数据都在内存中的一个连续位置。

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

数据大小

如果std::矢量适合特定的缓存级别,但std::map不适合,那么std::向量将具有优势。这是,尤其是如果您一直对相同的数据重复测试。