speeding vector push_back

半世苍凉 提交于 2019-12-12 05:41:24

问题


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 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.



来源:https://stackoverflow.com/questions/44326712/speeding-vector-push-back

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!