为什么std :: vector的分配和释放比我的机器上的动态数组慢

时间:2013-06-27 15:28:11

标签: c++ performance stl benchmarking

我的印象是std :: vector只是动态数组的一个薄包装器,它们的性能应该是可比的。在Internet上搜索和stackoverflow本身也给出了相同的答案。但是,当我自己测试时,我发现了一个巨大的差异。代码如下。我尝试了VC ++ 2012(发布版本)和带有优化标志-O2的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就不行了,即使它有一个默认初始化所有元素的构造函数。

此外,这不仅仅是我脑海里浮现的东西。我对类的最终项目按照花费的时间进行评分,并且我使用了几个vector来分配一个巨大的内存缓冲区而不是new用于异常安全,因为我们的教科书鼓励使用STL。直到今天,我才意识到我因此而失去了一些积分。悲伤的一天。

编辑2

今天我在Ubuntu 13.04 x64上尝试过Clang 3.2,而std :: vector不再花时间进行初始化。事实上,矢量现在是最快的!也许这毕竟是编译器优化问题,而不是std :: vector的设计本身。

2 个答案:

答案 0 :(得分:4)

我相信这是由于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()

答案 1 :(得分:2)

printf("%d\n",d[len-1]);//prevent compiler from optimizing away 

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

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

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

这一行用值0初始化len个对象。另一行不是:

int *d=new int[len];

唯一导致len个对象的值为0的其他行就是这一行:

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

您可以从与分配和解除分配绩效相关的基准中得出的唯一结论是,基准不适合得出与分配和解除分配绩效相关的结论。