简体   繁体   中英

speeding vector push_back

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

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.

There are several issues with your code.

  • You're essentially defining a type very similar to std::vector with std::vector as its only member. Why not using a std::vector in the first place?
  • Your push_back() function is just terrible. Let me first explain what std::vector<>::push_back() actually does.

    1. if size()<capacity() : it just copies the new element at the end of the block and increments the end marker. This is the most common situation .
    2. only if size()==capacity() , re-allocation is needed and

      1. it allocates a new block of memory, typically twice the current capacity
      2. it moves all data to the begin of the new block
      3. it de-allocates the old block of memory
      4. 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() :

    1. it insert() s default-constructed element at val_.end() . To this end, std::vector::insert() does the following:
      1. it allocates a new block of memory, sufficiently large to hold the old data plus the data to be inserted, but possibly larger.
      2. it moves all data to the begin of the new block
      3. it de-allocates the old block of memory
      4. it copies the elements to be inserted at the end of the old data
    2. it the destructs all the newly inserted elements.
    3. 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 by std::vector ) and don't know the final size in advance. If either of these requirements can be dropped, you can avoid re-allocation: either by std::vector<>::reserve() or by using a container with non-contiguous memory, such as std::deque .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM