【问题标题】:Vector vs Array Performance向量与阵列性能
【发布时间】:2013-05-08 17:25:06
【问题描述】:

在另一个线程中,我开始讨论向量和数组,其中我主要是在扮演魔鬼的拥护者,以按下按钮。然而,在此过程中,我偶然发现了一个让我有点困惑的测试用例,我想就它进行真正的讨论,关于我因扮演魔鬼代言人而受到的“虐待”,开始一个真正的现在不可能就该线程进行讨论。然而,这个特定的例子让我很感兴趣,我无法对自己做出令人满意的解释。

讨论的是向量与数组的一般性能,忽略动态元素。例如:显然在向量中不断使用 push_back() 会减慢它的速度。我们假设向量和数组预先填充了数据。我提出的示例,随后由线程中的个人修改,如下所示:

#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;

const int ARRAY_SIZE = 500000000;

// http://stackoverflow.com/a/15975738/500104
template <class T>
class no_init_allocator
{
public:
    typedef T value_type;

    no_init_allocator() noexcept {}
    template <class U>
        no_init_allocator(const no_init_allocator<U>&) noexcept {}
    T* allocate(std::size_t n)
        {return static_cast<T*>(::operator new(n * sizeof(T)));}
    void deallocate(T* p, std::size_t) noexcept
        {::operator delete(static_cast<void*>(p));}
    template <class U>
        void construct(U*) noexcept
        {
            // libstdc++ doesn't know 'is_trivially_default_constructible', still has the old names
            static_assert(is_trivially_default_constructible<U>::value,
            "This allocator can only be used with trivally default constructible types");
        }
    template <class U, class A0, class... Args>
        void construct(U* up, A0&& a0, Args&&... args) noexcept
        {
            ::new(up) U(std::forward<A0>(a0), std::forward<Args>(args)...);
        }
};

int main() {
    srand(5);  //I use the same seed, we just need the random distribution.
    vector<char, no_init_allocator<char>> charArray(ARRAY_SIZE);
    //char* charArray = new char[ARRAY_SIZE];
    for(int i = 0; i < ARRAY_SIZE; i++) {
        charArray[i] = (char)((i%26) + 48) ;
    }

    for(int i = 0; i < ARRAY_SIZE; i++) {
        charArray[i] = charArray[rand() % ARRAY_SIZE];
    }
}

当我在我的机器上运行它时,我得到以下终端输出。第一次运行未注释矢量线,第二次运行未注释数组线。我使用了最高级别的优化,为向量提供了最大的成功机会。下面是我的结果,前两次运行时未注释数组线,后两次运行时使用矢量线。

//Array run # 1
clang++ -std=c++11 -stdlib=libc++ -o3 some.cpp -o b.out && time ./b.out

real    0m20.287s
user    0m20.068s
sys 0m0.175s

//Array run # 2
clang++ -std=c++11 -stdlib=libc++ -o3 some.cpp -o b.out && time ./b.out

real    0m21.504s
user    0m21.267s
sys 0m0.192s

//Vector run # 1
clang++ -std=c++11 -stdlib=libc++ -o3 some.cpp -o b.out && time ./b.out

real    0m28.513s
user    0m28.292s
sys 0m0.178s

//Vector run # 2
clang++ -std=c++11 -stdlib=libc++ -o3 some.cpp -o b.out && time ./b.out

real    0m28.607s
user    0m28.391s
sys 0m0.178s

数组优于向量并不让我感到惊讶,但是,差异大约为 50% 让我非常惊讶,我希望它们可以忽略不计,我觉得这个测试用例的性质是掩盖结果的性质。当您在较小的数组大小上运行此测试时,性能差异会显着消失。

我的解释:

向量的附加实现指令导致向量指令在内存中对齐不佳,甚至在此示例中,可能在 2 个不同“块”上的一个非常糟糕的点上进行拆分。这导致内存在缓存级别、数据缓存和指令缓存之间来回跳转的频率比您预期的要高。我还怀疑 LLVM 编译器可能夸大了弱点,并且由于一些较新的 C++11 元素而优化不佳,尽管除了假设和猜想之外,我没有理由对这些解释中的任何一种进行解释。

如果 A:有人可以复制我的结果和 B:如果有人对计算机如何运行这个特定的基准测试以及为什么在这种情况下 vector 的性能大大低于数组有更好的解释,我很感兴趣。

我的设置:http://www.newegg.com/Product/Product.aspx?Item=N82E16834100226

【问题讨论】:

  • -o3 应该是 -O3,你没有优化。
  • 有趣的问题,很高兴看到源代码、编译器标志和结果,以及详细的解释。 +1 提出了一个很好的问题。 :)
  • 我看不出 VC++ 有什么不同
  • 使用-O3并将is_trivially_default_constructible更改为has_trivial_default_constructor(使用libstdc++),我得到数组0m26.250s和向量0m27.438s,所以这或多或少是预期的小区别。
  • @Markku:您概述的具体内容是优化应该解决的问题。

标签: c++ benchmarking


【解决方案1】:

更简单的解释:您正在构建禁用优化。你想要-O3,而不是-o3

我没有可用的 clang 来准确重现您的测试,但我的结果如下:

//Array run # 1
$ g++ -std=c++11 -O3 test.cpp -o b.out && time ./b.out

real    0m25.323s
user    0m25.162s
sys 0m0.148s

//Vector run #1
$ g++ -std=c++11 -O3 test.cpp -o b.out && time ./b.out

real    0m25.634s
user    0m25.486s
sys 0m0.136s

【讨论】:

  • 是的,我相信在这个答案和另一个答案之间,存在着正确的答案。通常 LLVM/CLang 会抱怨错误的标志。不知道我怎么错过了。但是我仍然得到在 10% 范围内不同的结果,因为你的结果仍然可疑。我想我会坚持使用 gcc,直到 Clang 弄清楚 c++11
  • @ChrisCM:在这种情况下,它不是错误标志。你告诉它调用输出3,然后用后面的-o b.out覆盖它。
  • LLVM 特定的错误优化很容易解释分数差异。#
  • 是的,我同意,这里的观点确实解决了大部分问题,但剩下的问题是 LLVM 的错误优化,很明显,使用 g++ 的结果是并且应该更依赖于系统噪声比说明或组装方面的任何差异。
【解决方案2】:

我可以保证 LLVM 确实错误优化了 std::vector (如果你实际上正在优化的话),至少现在是这样。它没有正确内联所涉及的许多函数调用。使用 GCC,您将获得更好的性能。

【讨论】:

  • 这也是我的怀疑。我会等着看别人怎么说,但这至少和它有一点关系。
  • 我会在这个上坚持使用小狗。
  • 我会接受这个答案。我的 -O3 错误是错误的主要来源,但是 g++ 仍然优化了所有向量问题,我相信我的 LLVM 版本是导致向量在适当优化后约 10% 弱点的罪魁祸首。
  • @ChrisCM:Google 报告 LLVM 损失了 12%,所以这完全在预期范围内。
猜你喜欢
  • 1970-01-01
  • 2016-12-03
  • 1970-01-01
  • 2020-11-09
  • 2013-05-20
  • 1970-01-01
  • 2016-04-11
  • 1970-01-01
  • 2018-03-24
相关资源
最近更新 更多