【问题标题】:std::vector faster than plain array?std::vector 比普通数组快吗?
【发布时间】:2011-09-16 09:25:01
【问题描述】:

我刚刚尝试在 std::vector<std::pair<float, unsigned int>>(填充 push_back 操作)和普通 std::pair<float, unsigned int>> * 数组(使用 new 分配,然后一一填充)上对 std::sort 进行基准测试。 compare 函数只是比较了对的浮点部分。

令人惊讶的是,当用于 16M 值时,在 std::vector 上只需要大约 1940 毫秒,但在数组上大约需要 2190 毫秒。谁能解释一下向量如何更快?是因为缓存,还是只是数组版本的 std::sort 实现不好?

gcc (GCC) 4.4.5 20110214 (Red Hat 4.4.5-6)

Intel(R) Core(TM) i7 CPU 870 @ 2.93GHz - cache size 8192 KB(计算机有两个四核 CPU,但我假设排序只是单线程)

编辑:现在你可以称我为笨蛋,但是当我试图重现我用于测量的代码时(我已经删除了原来的代码)我无法重现结果 - 现在是数组版本大约需要 1915 +- 5ms(在 32 次运行中测量)。我只能发誓我已经(手动)对 10 次测量进行了 3 次测试,结果相似,但这并不是一个严格的证明。

原始代码中可能存在一些错误,后台进程似乎不太可能,因为我已经交替测量向量和数组版本并且向量结果保持不变并且没有用户登录。

请将此问题视为已结束。感谢您的努力。

【问题讨论】:

  • 您运行了多少次测试?一次完全不准确。
  • 您应该提供您确实遵循的确切测试协议。例如,如果您对每个测试执行的次数不超过一次,或者如果您在同一执行中执行了两个测试,则 test 不相关。
  • 在我的i7 920 @ 2.67 GHzgcc 4.5.2 上,我得到770 - 780 ms 向量和630 - 650 ms 数组。您使用了哪些优化选项? (我用-march=native -O3,数组和向量包含相同的16000000对随机数)
  • @Konrad 鉴于他没有向我们展示他实际测量的任何代码,谁知道呢?但差异并不是很大(他也没有说他是如何测量的)。
  • 你在两个测试中使用相同的输入数据吗?

标签: c++ performance stl vector


【解决方案1】:

std::vector<std::pair<float, unsigned int>>(填入push_back操作)

这将所有数据连续存储,因此内存局部性非常好

std::pair<float, unsigned int>> *数组(使用new分配,然后一一填充)

这会将数据分散到整个内存中。

您在vector 和一个简单数组之间进行了非常不公平的比较。数组案例中涉及的额外间接性会受到伤害,缺乏局部性会降低缓存性能。我很惊讶你没有看到有利于连续存储的更大胜利。

【讨论】:

  • 我认为这是不对的。如果我理解正确,第二个使用数组 new 分配一个 std::pair<float, unsigned int>> 数组。地区应该完全相同。
  • @James:这不是问题的字面意思,尽管我理解你如何解释它(你的解释很可能是正确的)。这个问题说他有一个指向对的指针数组。这就是为什么没有代码的问题,尤其是性能问题,是一个糟糕的主意。
  • @Ben 我知道他在 1) 和 std::vector<std::pair<float, unsigned int> > 和 2) std::pair<float, unsigned int>* 上调用了 std::sort,但还不清楚。 (但如果他真的在第二种情况下使用了指针数组,我预计会有更大的不同。)
  • @James:如果分配是在良好的时间局部性下完成的,那么空间局部性将是平庸的,而不是完全糟糕的。这是我能提供的最好解释。
  • 快速排序的递归特性以及缓存将使局部性没有您想象的那么重要。一旦递归到某个级别,所有数据都在缓存中,只有间接导致惩罚,而不是局部性。我在这里假设快速排序。
【解决方案2】:

他们将使用相同版本的sort。这很可能是随机的 CPU 影响,例如缓存或线程上下文切换。

【讨论】:

  • 你怎么知道他们使用相同版本的排序?我记得在 ISO 规范中没有具体说明它们是如何实现的。他们可能只是使用相同的界面/模板。
  • @Yet - 如果他使用gcc (GCC) 4.4.5 20110214,我们就知道 std::sort 是什么样的。
  • @Yet Another Geek:因为向量迭代器和数组迭代器在发布版本中是一样的。
  • @DeadMG 我认为数组使用指针进行迭代,并通过某种基于类型安全类的引用进行向量化
  • @Yet Another Geek:在发布版本中,vector<T>::iterator 可以定义为T*。它们的规格是相同的。
【解决方案3】:

您是否使用-O3 编译您的代码?

如果没有,那就去做吧。所有其他基准测试结果都毫无意义,尤其是对于模板代码。

您是否多次运行测试?

这样做是为了防止诸如中断和/或缓存之类的事情对您的结果产生很大影响。

不要将浮点数比较或算术用于基准测试。结果很大程度上取决于编译器、平台、编译器选项等。

您的测试数据是如何创建的?

大多数排序算法所需的时间根据输入数据的排序而变化。

您使用了哪种测量时间的方法?时钟周期?计时器?

无论如何,编写可提供可靠结果的基准测试并不像起初看起来那么容易。不要使用基准来确定解决问题的正确代码。

【讨论】:

    猜你喜欢
    • 2014-11-29
    • 2012-04-18
    • 2021-05-26
    • 2011-06-01
    • 1970-01-01
    • 2017-05-10
    • 1970-01-01
    • 2011-05-08
    • 2014-10-24
    相关资源
    最近更新 更多