的std ::矢量&lt; std :: vector <unsigned char =“”>&gt;或者std :: deque&lt; std :: vector <unsigned char =“”>&gt;?</unsigned> </unsigned>

时间:2012-02-15 19:22:42

标签: c++ optimization vector memory-management deque

我有一个现有的算法,如果可能的话我需要稍微优化它。目前,此算法中的大量更改不是一种选择。该算法适用于std::vector< std::vector<unsigned char> >的实例。它看起来像这样:

typedef std::vector<unsigned char> internal_vector_t;
std::vector< internal_vector_t > internal_vectors; 

while (fetching lots of records) {
   internal_vector_t tmp;
   // reads 1Mb of chars in tmp...
   internal_vectors.push_back(tmp);
   // some more work
}

// use this internal_vectors

该算法使用push_back()在internal_vector_t的internal_vectors个实例中插入了很多次。 internal_vector_t的大多数实例的大小为1 Mb 。由于internal_vectors的大小未知,因此事先没有预留()。

我不明白的第一件事是当internal_vectors达到其当前容量时发生的事情,需要分配一个新块并将其当前内容复制到更大的内存块中。由于大多数块的大小都是1Mb,因此复制操作很长。 我是否应该期望编译器(gcc 4.3,MS VC ++ 2008)能够设法优化它以避免复制

如果无法避免复制,会更改为std::deque帮助吗?我认为std :: deque因为我仍然需要通过像internal_vectors [10]这样的索引来访问。像这样:

typedef std::vector<unsigned char> internal_vector_t;
std::deque< internal_vector_t > internal_vectors; 
// the same while

据我所知,std::deque不需要重新定位曾经分配的内容。我是对的,std::deque在这种情况下会重新减少分配和复制push_backs吗?

<小时/> 的更新
1)根据DeadMG MSVC9进行这种类型的优化(Swaptimization - TR1 Fixes In VC9 SP1)。 gcc 4.3可能不会做这种类型的优化。

2)我已经分析了使用std::deque< std::vector<unsigned char> >的算法版本,我发现它的性能更好。

3)我还使用了Mark Ransom建议的swap。使用这改善了性能:

   internal_vector_t tmp;
   internal_vectors.push_back(empty);
   tmp.swap(internal_vectors.back());

5 个答案:

答案 0 :(得分:3)

MSVC9为其标准容器实现了称为“swaptimization”的东西。这是一个较弱的移动语义版本。当外部矢量调整大小时,复制内部矢量。

但是,您最好只需将编译器升级到MSVC10或GCC(4.5,我认为是),它将为您提供移动语义,从而使这些操作更加高效。当然,std::deque可能仍然是更智能的容器,但移动语义在许多地方都具有性能优势。

答案 1 :(得分:2)

每次向internal_vector_t插入internal_vectors时,都会复制internal_vector_t。无论您使用vector还是deque,都是如此。标准容器总是复制您要插入的对象。

您可以通过插入空internal_vector_t然后swap插入对象的内容以及您想要插入的内容来消除复制。

有时,矢量需要在插入过程中耗尽空间时自行调整大小,这会导致再次复制对象。只要你总是在开头或结尾插入,deque就会消除它。

编辑:我上面给出的建议可以通过这些代码更改进行汇总。此代码应避免复制大型矢量。

typedef std::vector<unsigned char> internal_vector_t;
std::deque< internal_vector_t > internal_vectors; 
internal_vector_t empty;

while (fetching lots of records) {
   internal_vector_t tmp;
   // reads 1Mb of chars in tmp...
   internal_vectors.push_back(empty);
   tmp.swap(internal_vectors.back());
   // some more work
}

答案 2 :(得分:1)

std::deque不会连续存储它的元素 - 它将存储区分为一系列常量大小的“块”。这意味着当std::deque用完容量时,它只需要分配一个常量大小的新块 - 它不需要重新分配它的整个内部缓冲区并移动它所有的现有元素。

另一方面,

std::vector确实保持连续存储,因此当它耗尽容量并重新分配时,它确实需要移动所有现有元素 - 这可能很昂贵。

std::vector对于其重新分配方案是“聪明的”,根据几何系列分配块(通常将容量加倍或增加1.5等)。这意味着不会经常重新分配。

在这种情况下,

std::deque可能更有效,因为在重新分配确实发生时它的工作量会减少。与往常一样,您必须进行基准测试以获得任何实数。

您的代码可能会在其他方面进一步改进。似乎在while循环的每次迭代中,您都在创建一个新的internal_vector_t tmp。在循环外声明这个并且在每次迭代时只是::clear()它的存储可能更有效。每次拨打tmp时,您也会复制整个internal_vectors.push_back(tmp)向量 - 您可以通过tmp移动internal_vectors.push_back(std::move(tmp))向量来改善这一点 - 这只会复制一些指示。

希望这有帮助。

答案 3 :(得分:0)

您是否正在索引外部向量?如果没有,std::list<std::vector<unsigned char> >怎么样?

答案 4 :(得分:0)

取决于实施,出队可能更有效。与向量不同,出队不能保证连续存储,因此可以分配几个单独的内存块。因此,它可以分配更多内存而无需移动已添加的元素。您应该尝试并测量影响。