【问题标题】:Why is allocation and deallocation of std::vector slower than dynamic array on my machine为什么 std::vector 的分配和释放比我机器上的动态数组慢
【发布时间】:2013-06-27 15:28:10
【问题描述】:

我的印象是 std::vector 只是动态数组的一个薄包装器,它们的性能应该是可比的。在 Internet 上的搜索和 stackoverflow 本身也给出了相同的答案。但是,当我自己测试时,我发现了巨大的差异。代码如下。我尝试使用优化标志 -O2 的 VC++ 2012(发布版本)和 MinGW。

new、malloc 和 calloc 的时间大约是 0.005 秒,而 std::vector 在两个编译器上都需要 0.9 秒。是 std::vector 本身就很慢还是我的代码有一些严重的缺陷?

#define _SCL_SECURE 0
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <time.h>

struct timer
{
    clock_t start;
    timer()
    {
        start=clock();
    }
    ~timer()
    {
        double t=static_cast<double>(clock()-start)/CLOCKS_PER_SEC;
        printf("%lf\n", t);
    }
};

int main()
{
    const size_t len=(1<<28);   
    {
        timer t;
        int *d=new int[len];
        printf("%d\n",d[len-1]);//prevent compiler from optimizing away 
                                //useless allocation and deallocation
        delete[] d;
    }
    {
        timer t;
        int *d=(int *)malloc(len*sizeof(int));
        printf("%d\n", d[len-1]);
        free(d);
    }

    {
        timer t;
        std::vector<int> d(len);
        printf("%d\n", d[len-1]);
    }
    {
        timer t;
        int *d=(int *)calloc(len, sizeof(int));
        printf("%d\n", d[len-1]);
        free(d);
    }

    return 0;
}

编辑 1

根据建议,我测试了创建动态数组的其他方法

  • new: 0.005
  • malloc: 0.005
  • calloc: 0.005
  • malloc+memset: 1.244
  • vector(len): 1.231
  • vector(len, 0): 1.234
  • vector.reserve(len): 0.005

所以确实违规者是零初始化而不是分配或释放。这意味着如果需要一个零初始化数组,vector 不是可行的方法,即使它有一个默认初始化所有元素的构造函数。

此外,这不仅仅是从我脑海中蹦出来的东西。我的最后一个课程项目是根据花费的时间来评分的,我使用了几个vectors 来分配一个巨大的内存缓冲区而不是new,以确保异常安全,并且因为我们的教科书鼓励使用 STL。直到今天我才意识到我因此而失去了一些分数。悲伤的一天。

编辑 2

今天我在 Ubuntu 13.04 x64 上尝试了 Clang 3.2,std::vector 不再需要花时间来初始化。事实上,vector现在是最快的!也许这毕竟是一个编译器优化问题,而不是在 std::vector 的设计中固有的。

【问题讨论】:

  • 您不应该通过大约 1000 次迭代(如果不是更多)来运行测试吗?
  • 它是 GCC 4.8.1 上的 even worse 和 -O3。 0.000000 表示除向量之外的所有内容,即 4.09(第二次运行的时间要短得多,即链接)。
  • 您应该比较具有相同功能的代码(即您是否阅读过矢量文档?)。您应该比较没有未定义行为的代码。
  • vector 和其他代码之间的重要区别在于,vector 对内存进行零初始化,而其他代码则不是(calloc 除外)。如果将new 替换为new int[len](),则它需要与向量一样长的时间。我假设 calloc 以某种方式进行了优化(例如,它可以请求归零内存,而不是显式地将内存归零)。
  • 数组的分配一般是使用时最近最少使用的操作。

标签: c++ performance stl benchmarking


【解决方案1】:

我相信这是由于分配了 std::vector 在每个元素上调用了一个复制构造函数,其中 malloc 只是返回未初始化的内存。

std::vector<int> x(100); 

实际上等同于:

std::vector<int> y(100, int()); 

查看 std::vector 构造函数的文档 http://en.cppreference.com/w/cpp/container/vector/vector

我经常会做这样的事情:

std::vector<int> x; 
x.reserve(100);
// Insert items into x via x.push_back()

【讨论】:

  • 在 C++11(和 Visual C++ 2012)中,std::vector&lt;T&gt; x(100);std::vector&lt;T&gt; y(100, T()); 相同。前者默认构造 100 个元素;后一个副本从给定的 T() 参数构造 100 个元素。 (int效果 相同,但并非所有类型。)
  • newmalloccalloc 相同的原始类型?
  • @JamesMcNellis:现在我对 C++ 的怪癖有了更多的了解,我发现您的措辞有误。在 C++11 中,std::vector value 初始化其内容,而不是 default 初始化。
【解决方案2】:
printf("%d\n",d[len-1]);//prevent compiler from optimizing away 

这一行从一个 未初始化的 对象中读取。它不是阻止编译器优化事物,而是让它有余地做它想做的任何事情(即程序的行为是未定义的)。

假设我们以某种方式解决了这个问题,并且程序的行为现在已经明确定义(也许我们添加了一行初始化 d[len-1])。

std::vector<int> d(len);

这一行初始化 len 对象,值为 0。另一行没有:

int *d=new int[len];

导致len 对象值为0 的唯一另一行是这一行:

int *d=(int *)calloc(len, sizeof(int));

您可以从与分配和释放性能相关的基准中得出的唯一结论是,该基准不适用于得出与分配和释放性能相关的结论。

【讨论】:

  • 我知道它是从一个未初始化的内存中读取的。但我实际上并不需要该值(可能是剩下的任何字节)。除了打印出一个非零的随机值之外,undefined 的行为可能是什么?
  • @C.R.谁知道呢。你认为“未定义”是什么意思?这并不意味着“定义为剩余的任何字节”。它的意思是“未定义”。
  • @R.MartinhoFernandes 据我所知,从未初始化的内存中读取是 not 未定义的行为。如果我错了,请纠正我,但我一直认为从内存中未初始化的块读取被定义为返回任何值(如果你愿意的话,一个随机的位序列)。
  • 在这个例子中它是不是 UB,因为这个变量不能被优化为register:stackoverflow.com/q/11962457/207791
  • @Victor 这是 UB,因为标准说它是 UB。该变量可以“优化为register”,因为它是UB。这就是 UB 的意思:任何事情都可能发生,包括变量“优化为register”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-28
  • 2016-06-08
  • 2023-01-20
  • 1970-01-01
  • 2017-12-16
相关资源
最近更新 更多