我正在尝试在无法预测容量时加速vector :: push_back
当保留可用时,矢量push_back将新元素写入容器的末尾,然后移动结束标记。使用所有保留后,push_back可能会触发重新分配,这是一个缓慢的过程 为了加快速度,可以为几个即将到来的push_back重新生成保留,而不会在空时重新分配。您如何看待此代码有助于实现该目标?
#ifndef __VECTOR_HPP
#define __VECTOR_HPP
#include <exception>
#include "Concept.hpp" //Concept::RESA constant
#include <vector>
template <typename T>
class Vector : public std::vector<T> {
public :
void push_back (T t) {
if (std::vector<T>::size () == std::vector<T>::capacity ()) {
std::vector<T>::reserve ((size_t) Concept::RESA);
}
std::vector<T>::push_back (t);
}
};
#endif
测试程序:
#include "Vector.hpp"
int main (int argc, char* argv []) {
{
std::vector<size_t> v0;
clock_t t (clock ());
size_t duration (0);
for (size_t i (0); i != 10000000; i++) {
v0.push_back (i);
}
duration = (size_t) (clock () -t);
std::cout << "duration old push_back == " << duration << " ticks" << std::endl;
}
{
size_t duration (0);
Vector<size_t> v1;
clock_t t (clock ());
for (size_t i (0); i != 10000000; i++) {
v1.push_back (i);
}
duration = (size_t) (clock () -t );
std::cout << "duration new push_back == " << duration << " ticks" << std::endl;
}
}
结果:
使用Concept :: RESA == 8192,并应用建议,以下是Lenovo ThinkCentre icore5(Linux Debian,g ++)上的结果:
持续时间旧push_back == 105317滴答
持续时间new push_back == 87156 ticks
答案 0 :(得分:3)
确实push_back
可能会触发重新分配,这是一个缓慢的过程
它不会在每个push_back
上执行此操作,而是每次都会保留指数级更多的内存,所以如果事先对结果矢量大小有一个很好的近似,那么显式reserve
才有意义。
换句话说,std::vector
已经在您的代码中处理了您的建议。
另一点:there is a reserve
method比插入和擦除元素更有效,最明显的是它不会创建和销毁实际对象。
具有讽刺意味的是,@Sopel提到用类中的reserve
替换插入/擦除将禁用向量的增长摊销,使您的代码成为几个错误(有些)相互抵消的好例子。
答案 1 :(得分:2)
您的代码存在一些问题。
std::vector
非常相似,std::vector
是其唯一成员。为什么不首先使用std::vector
?您的push_back()
功能非常糟糕。我先解释std::vector<>::push_back()
实际上做了什么。
size()<capacity()
:它只是复制块末尾的新元素并递增end
标记。 这是最常见的情况。 仅如果size()==capacity()
,则需要重新分配
现在让我们看看你的
void push_back (const T& t) {
if (val_.size () == val_.capacity ()) {
val_.insert (val_.end (), resa_.begin (), resa_.end ());
auto i = val_.end();
i -= (size_t) Concept::RESA;
val_.erase (i, val_.end ());
}
val_.push_back (t);
}
如果val_.size()==val_.capacity()
:
insert()
的{{1}}默认构造元素。为此,val_.end()
执行以下操作:
因此,您的函数也需要像普通std::vector::insert()
那样频繁地重新分配,并完全不必要的复制构造,然后销毁一大块元素。当你想要连续的内存布局时,无法避免重新分配(如std::push_back()
所承诺的那样)和不知道最终的大小预先。如果可以删除这些要求中的任何一个,则可以避免重新分配:std::vector
或使用具有非连续内存的容器,例如std::deque
。