std::vector< std::vector<unsigned char> > or std::d eque< std::vector<unsigned cha

std::vector< std::vector<unsigned char> > or std::deque< std::vector<unsigned char> >?

本文关键字:std lt vector gt unsigned cha eque or char      更新时间:2023-10-16

我有一个现有的算法,如果可能的话,我需要稍微优化它。目前还不能对这种算法进行大量更改。该算法适用于CCD_ 1实例。它看起来像这样:

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的大多数实例的大小为1Mb。由于internal_vectors的大小是未知的,因此事先不执行reserve()。

我不明白的第一件事是,当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进行了这种类型的优化(转换优化-TR1修复了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());

MSVC9为其标准容器实现了所谓的"交换优化"。这是一个较弱的移动语义版本。当调整外部向量的大小时,它将而不是复制内部向量。

然而,您最好简单地将编译器升级到MSVC10或GCC(我认为是4.5),这将为您提供移动语义,从而大大提高此类操作的效率。当然,std::vector< std::vector<unsigned char> >0可能仍然是更智能的容器,但移动语义在很多地方都对性能有益。

每次将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
}

std::deque不连续存储元素,而是将其存储分解为一系列大小不变的"块"。这意味着,当std::deque的容量用完时,它只需要分配一个大小不变的新块——它不需要重新分配整个内部缓冲区,也不需要移动所有现有元素。

另一方面,std::vector确实维护连续存储,因此当它耗尽容量并重新分配时,它确实需要移动所有现有元素——这可能会很昂贵。

std::vector对其重新分配方案很"聪明",根据几何级数分块分配(通常将容量增加一倍或1.5等)。这意味着重新分配并不经常发生。

CCD_ 22在这种情况下可能更有效,因为当重新分配确实发生时。和往常一样,你必须进行基准测试才能得到任何真实的数字。

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

希望这能有所帮助。

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

根据实现的不同,出列可能更高效。与向量不同,出列不能保证连续存储,因此可以分配几个单独的内存块。因此,它可以在不移动已经添加的元素的情况下分配更多的内存。你应该尝试一下并衡量一下影响。