问题
I am trying to speed vector::push_back when capacity cant be predicted
When reserve is available a vector push_back writes the new element at the end of the container, and then the end marker is moved. After all reserve is used, push_back may trigger reallocation which is a slow process.
To speed this up, reserve is regenerated for several coming push_back without reallocation when empty. How do you think this code assist in achieving that goal ?
#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
Test program :
#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;
}
}
Results :
with a Concept::RESA == 8192, and applying suggestions, here are the results on a Lenovo ThinkCentre icore5 (Linux Debian, g++) :
duration old push_back == 105317 ticks
duration new push_back == 87156 ticks
回答1:
Indeed push_back
may trigger reallocation, which is a slow process.
It will not do so on every single push_back
though, instead it will reserve exponentially more memory each time, so explicit reserve
only makes sense if you have a good approximation of resulting vector size beforehand.
In other words, std::vector
already takes care of what you are suggesting in your code.
Another point: there is a reserve method that serves the purpose much better than inserting and erasing elements, most notably it does not create and destroy actual objects.
Ironically as @Sopel mentioned replacing insert/erase with reserve
in your class would disable vector's growth amortization, making your code a nice example of several mistakes (somewhat) cancelling each other.
回答2:
There are several issues with your code.
- You're essentially defining a type very similar to
std::vector
withstd::vector
as its only member. Why not using astd::vector
in the first place? Your
push_back()
function is just terrible. Let me first explain whatstd::vector<>::push_back()
actually does.- if
size()<capacity()
: it just copies the new element at the end of the block and increments theend
marker. This is the most common situation. only if
size()==capacity()
, re-allocation is needed and- it allocates a new block of memory, typically twice the current capacity
- it moves all data to the begin of the new block
- it de-allocates the old block of memory
- it finally constructs a new element at the end of the data
Let's now see what your
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); }
does if
val_.size()==val_.capacity()
:- it
insert()
s default-constructed element atval_.end()
. To this end,std::vector::insert()
does the following:- it allocates a new block of memory, sufficiently large to hold the old data plus the data to be inserted, but possibly larger.
- it moves all data to the begin of the new block
- it de-allocates the old block of memory
- it copies the elements to be inserted at the end of the old data
- it the destructs all the newly inserted elements.
- if finally constructs a new element at the end of the data (which needs no re-allocation).
Thus, your function too requires re-allocation about as frequent as plain
std::push_back()
and completely unnecessarily copy-constructs and then destroys a whole chunk of elements. There is no way of avoiding re-allocation when you want contiguous memory layout (as promised bystd::vector
) and don't know the final size in advance. If either of these requirements can be dropped, you can avoid re-allocation: either bystd::vector<>::reserve()
or by using a container with non-contiguous memory, such as std::deque.- if
来源:https://stackoverflow.com/questions/44326712/speeding-vector-push-back