push_back用于向量,双端队列和列表

时间:2009-03-27 22:52:07

标签: c++ stl

我正在尝试优化C ++例程。这个例程的主要瓶颈是对象向量的push_back()。我尝试使用deque而不是尝试了一个列表。但奇怪的是(与理论相反)deque和list实现的运行速度比矢量对应慢得多。

事实上,即使clear()对于deque和list实现的运行速度比向量对应运行慢得多。在这种情况下,Vector实现似乎是最快的,而列表实现是最慢的。

任何指针?

注意:vector reserve()可以加快实现速度但不能完成,因为它的大小未知。

感谢。

9 个答案:

答案 0 :(得分:6)

可以预期矢量比deque或list更快构建或清除;这是一个更简单的数据结构。

关于vector::push_back,它必须做两件事:

  1. 检查矢量是否足够大 抓住新项目。
  2. 插入新项目。
  3. 通常只需调整矢量大小并使用operator[]设置项目即可消除步骤1,从而加快速度。

    <强>更新 原始海报问了一个例子。 下面的代码乘以128兆插入和输出

    push_back           : 2.04s
    reserve & push_back : 1.73s
    resize & place      : 0.48s
    

    在旧的P4机器上使用g ++ -O3在Debian / Lenny上编译和运行时。

    #include <iostream>
    #include <time.h>
    #include <vector>
    
    int main(int,char**)
    {
      const size_t n=(128<<20);
    
      const clock_t t0=clock();
      {
        std::vector<unsigned char> a;
        for (size_t i=0;i<n;i++) a.push_back(i);
      }
      const clock_t t1=clock();
      {
        std::vector<unsigned char> a;
        a.reserve(n);
        for (size_t i=0;i<n;i++) a.push_back(i);
      }
      const clock_t t2=clock();
      {
        std::vector<unsigned char> a;
        a.resize(n);
        for (size_t i=0;i<n;i++) a[i]=i;
      }
      const clock_t t3=clock();
    
      std::cout << "push_back           : " << (t1-t0)/static_cast<float>(CLOCKS_PER_SEC) << "s" << std::endl;
      std::cout << "reserve & push_back : " << (t2-t1)/static_cast<float>(CLOCKS_PER_SEC) << "s" << std::endl;
      std::cout << "resize & place      : " << (t3-t2)/static_cast<float>(CLOCKS_PER_SEC) << "s" << std::endl;
    
      return 0;  
    }
    

答案 1 :(得分:3)

如果您不知道要添加多少个对象,则很难找到最佳解决方案。您所能做的就是尽量减少您所知道的成本 - 在这种情况下,您的矢量会不断调整大小。

你可以用两种方式做到这一点;

1)将您的操作拆分为构建和完成。在这里,您可以将列表构建为一个保证足够大的向量,并在完成后将其复制到另一个向量。

E.g。

std::vector<Foo> hugeVec;
hugeVec.reserve(1000);    // enough for 1000 foo's

// add stuff

std::vector<Foo> finalVec;
finalVec = hugeVec;

2)或者,当你的向量是完整的呼叫保留时,足够用于另一组对象;

if (vec.capacity() == vec.size())
  vec.reserve(vec.size() + 16);  // alloc space for 16 more objects

您可以选择一个不同的容器,该容器不会导致在调整大小时复制所有元素,但您的瓶颈可能会成为新元素的单独内存分配。

答案 2 :(得分:2)

你是在推回物体本身还是指向它们的指针?与任何大小的对象相比,指针通常要快得多,因为复制只需要4-8个字节。

答案 3 :(得分:2)

如果对象的副本很慢,“push_back()”可能会很慢。如果默认构造函数很快并且你有办法使用swap来避免复制,那么你可以有一个更快的程序。

void test_vector1()
{
    vector<vector<int> > vvi;
    for(size_t i=0; i<100; i++)
    {
        vector<int> vi(100000, 5);
        vvi.push_back(vi);    // copy of a large object
    }
}

void test_vector2()
{
    vector<int> vi0;
    vector<vector<int> > vvi;
    for(size_t i=0; i<100; i++)
    {
        vector<int> vi(100000, 5);
        vvi.push_back(vi0);  // copy of a small object
        vvi.back().swap(vi); // swap is fast
    }
}

结果:

VS2005-debug 
* test_vector1 -> 297
* test_vector2 -> 172

VS2005-release
* test_vector1 -> 203
* test_vector2 -> 94

gcc
* test_vector1 -> 343
* test_vector2 -> 188

gcc -O2
* test_vector1 -> 250
* test_vector2 -> 156

答案 4 :(得分:1)

如果你想要矢量快,你必须保留()足够的空间。它产生了巨大的差异,因为每次成长都是非常昂贵的。如果你不知道,那就做个好猜。

答案 5 :(得分:0)

您需要提供有关例程行为的更多信息。

在一个地方,你担心push_back()在另一个地方的速度{1}}。你在建造容器,做什么然后倾倒它吗?

您在clear()看到的结果是因为clear()只需释放单个内存块,vector<>必须释放几个内存,而deque<>必须释放一个对于每个元素。

答案 6 :(得分:0)

Deque的结构比矢量更复杂,两者之间的速度差异将在很大程度上取决于具体实现和推回的元素的实际数量,但对于大量数据,它应该更快。 clear()可能会更慢,因为它可能会选择摆脱更复杂的底层结构。列表大致相同。

答案 7 :(得分:0)

关于push_back()缓慢而且保留没有帮助,MSVC中使用的STL的实现是这样的:当你第一次创建一个向量时,它为我认为10个元素保留了空间。从那时起,每当它变满时,它就会保留1.5倍于向量中元素数量的空间。所以,像10,15,22,33,49,73,105,157 ......重新分配是昂贵的。

即使您不知道确切的大小,reserve()也很有用。如果需要,reserve()不会阻止向量增长。如果你保留()并且向量增长超过那个大小,你仍然因为保留而改进了东西。如果矢量变得更小,那么,也许这没关系,因为通常在较小尺寸下性能更好。

您需要在RELEASE模式下进行分析,以确定哪种策略最有效。

答案 8 :(得分:0)

你必须根据你将要做的事情来选择你的容器。

相关操作包括:扩展(使用push),插入(可能根本不需要),提取,删除。

cplusplus.com处,可以非常清楚地了解每种容器类型的操作。

如果操作是push - 绑定,则向量优先于所有其他操作。 deque的好处在于它分配了固定的块,因此可以更有效地使用碎片内存。