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> >?
我有一个现有的算法,如果可能的话,我需要稍微优化它。目前还不能对这种算法进行大量更改。该算法适用于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> >
怎么样?
根据实现的不同,出列可能更高效。与向量不同,出列不能保证连续存储,因此可以分配几个单独的内存块。因此,它可以在不移动已经添加的元素的情况下分配更多的内存。你应该尝试一下并衡量一下影响。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 错误:调用"std::vector<:vector<int>>::p ush_back(std::vector<std::__cxx11::basic_string<
- C 建造者Clang STD :: Sill,找不到超载的操作员&lt;
- 为什么STD :: MAP需要操作员&lt;以及我如何写一个
- std::vector::reserve(未知m),我知道m<<;N(通常)并且知道N
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy
- C++运算符<<调用::ostream而不是std::osttream
- 是std :: set&lt; std :: future&gt;不可能存在
- 在修改列表后,std :: list&lt; t&gt; :: end()的值是否会更改
- BOOST ::变体无法解决运算符&lt;&lt;对于STD :: Ostream
- C++重载<<具有typedef'd std::vector
- 以x的倍数填充前导零,使用std::cout<<std::十六进制
- 错误:没有匹配'运算符<<"在'std::cout
- std::pair的默认构造函数<>将基本类型(int等)设置为零
- std::ostream&operator< & lt; (std:: ostream&压力,压力& &;val)
- 将std::endl传递给std::operator<<
- std::映射<>或std::vector<>在处理大型标志集时
- 重载& lt; & lt;使用命名空间std