4

I am using all the time the same std::vector<int> in order to try to avoid allocating an deallocating all the time. In a few lines, my code is as follows:

std::vector<int> myVector;
myVector.reserve(4);

for (int i = 0; i < 100; ++i) {
    fillVector(myVector);
    //use of myVector
    //....
    myVector.resize(0);
}

In each for iteration, myVector will be filled with up to 4 elements. In order to make efficient code, I want to use always myVector. However, in myVector.resize() the elements in myVector are being destroyed. I understand that myVector.clear() will have the same effect.

I think if I could just overwrite the existing elements in myVector I could save some time. However I think the std::vector is not capable of doing this.

Is there any way of doing this? Does it make sense to create a home-grown implementation which overwrites elements ?

Jarod42
  • 173,454
  • 13
  • 146
  • 250
Javi
  • 2,934
  • 4
  • 23
  • 38
  • What exactly do you mean by "destroyed"? Why is it problem? The space for these elements should remain allocated in the vector... – Messa Feb 14 '14 at 12:06
  • From std::vector reference: "If n is smaller than the current container size, the content is reduced to its first n elements, removing those beyond (and destroying them)." Therefore, resizing will keep the memory allocated in the vector but the destructor of all the elements in the vector will be called. I think it is enough to change the values in the elements of the vector so no need to call constructors in push_back() and destructors when rezising. – Javi Feb 14 '14 at 12:11
  • 2
    Make sure you only store POD types in your vector, and their destruction will be a no-op. – user4815162342 Feb 14 '14 at 12:11
  • @JaviV How do you deduce from the statement you quoted that *resizing will keep the memory allocated in the vector*? – qdii Feb 14 '14 at 12:15
  • @qdii I have been checking clear() vs resizing(0) and they are pretty much the same for this case. When resizing to a smaller siz (or clearing) elements in the vector are destroyed but capacity of the vector does not change. Therefore, push_back o emplace_back operations do not need to reallocate memory. – Javi Feb 14 '14 at 12:30
  • @user4815162342 you suggest that since I'm using int I should not care about this issue? – Javi Feb 14 '14 at 12:31
  • "up to 4 elements" == use array – NoSenseEtAl Feb 14 '14 at 12:49
  • Exactly — `int` is a [POD (plain-old-data) type](http://en.wikipedia.org/wiki/Plain_old_data_structure). – user4815162342 Feb 14 '14 at 14:15

5 Answers5

5

Your code is already valid (myVector.clear() has better style than myVector.resize(0) though).

'int destructor' does nothing.
So resize(0) just sets the size to 0, capacity is untouched.

Jarod42
  • 173,454
  • 13
  • 146
  • 250
  • "'int destructor' does nothing." Yes, he should have worried about this if he used `std::vector` where `~std::string` does something. – nodakai Feb 14 '14 at 12:27
4

Simply don't keep resizing myVector. Instead, initialise it with 4 elements (with std::vector<int> myVector(4)) and just assign to the elements instead (e.g. myVector[0] = 5).

However, if it's always going to be fixed size, then you might prefer to use a std::array<int, 4>.

Joseph Mansfield
  • 100,738
  • 18
  • 225
  • 303
  • The problem in my case is that, for instance, fillVector in iteration 1 will put 4 elements in the vector, and in iteration 2 could put only 2 elements. The fillVector function does not know in advance how many elements will be included into the vector. – Javi Feb 14 '14 at 12:14
  • 1
    @JaviV So resize to the number of elements you're putting in the vector. – Jim Balter Feb 14 '14 at 12:17
  • @JaviV As Jim said, use `myVector.resize(n)` at the beginning of each iteration, depending on how many elements you need. – Joseph Mansfield Feb 14 '14 at 12:26
  • @JosephMansfield I cannot do that because I do not know in advance how many elements will be added to fillVector. This is a simplified code. This fillVector function is actually a search for neighbours in a generic n-dimensional grid map in which some of the neighbours are not valid (closed cells), etc. – Javi Feb 14 '14 at 12:38
4

Resizing a vector to 0 will not reduce its capacity and, since your element type is int, there are no destructors to run:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{1,2,3};
    std::cout << v.capacity() << ' ';
    v.resize(0);
    std::cout << v.capacity() << '\n';
}

// Output: 3 3

Therefore, your code already performs mostly optimally; the only further optimisation you could make would be to avoid the resize entirely, thereby losing the internal "set size to 0" inside std::vector that likely comes down to an if statement and a data member value change.

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
0

In fact what you have at present is

for (int i = 0; i < 100; ++i) {
    myVector.reserve(4);
    //use of myVector
    //....
    myVector.resize(0);
}

I do not see any sense in that code.

Of course it would be better to use myVector.clear() instead of myVector.resize(0);

If you always overwrite exactly 4 elements of the vector inside the loop then you could use

std::vector<int> myVector( 4 );

instead of

std::vector<int> myVector;
myVector.reserve(4);

provided that function fillVector(myVector); uses the subscript operator to access these 4 elements of the vector instead of member function push_back

Otherwise use clear as it was early suggested.

Vlad from Moscow
  • 224,104
  • 15
  • 141
  • 268
  • I think there is some misunderstanding here. I do the reserve(4) out of the for since I now that fillVector will push up to 4 elements into myVector. The resize, or clear, is just in order to use the same vector through all the iterations. Lastly, the fillVector function should add a counter in order to know how many elements have been already modified in the vector in order to overwrite the proper elements. I guess this is even less efficient. – Javi Feb 14 '14 at 12:36
  • @Javi V Your question is inclear. We do not know how function fillVector works and what is the counter. When question is such unclear then it is obvious you will get irrelevant answers. For example I do not understand what you mean saying "resizing vector without destroying its elements" At least you could use either myVector.resize( myVector.size() ) or shrink_to_fit() – Vlad from Moscow Feb 14 '14 at 12:44
  • I'm sorry, I tried to be as brief as possible. In other words, what I was looking for was a way to overwrite elements of the vector every iteration instead of clearing and pushing back new elements. The complication comes when every iteration the number of elements in the vector are different. – Javi Feb 14 '14 at 12:57
  • @Javi V For example you could overwrite existent elements (you can always get the number of elements by using method size()) And in the end of the function erase the tail of unused elements using method erase. – Vlad from Moscow Feb 14 '14 at 13:00
  • But I guess that will be less efficient than the current accepted answer. – Javi Feb 14 '14 at 13:11
0

std::vector is not a solution in this case. You don't want to resize/clear/(de)allocate all over again? Don't.

  1. fillVector() fills 'vector' with number of elements known in each iteration.
  2. Vector is internally represented as continuous block of memory of type T*.
  3. You don't want to (de)allocate memory each time.

Ok. Use simple struct:

struct upTo4ElemVectorOfInts
{
  int data[4];
  size_t elems_num;
};

And modify fillVector() to save additional info:

void fillVector(upTo4ElemVectorOfInts& vec)
{
  //fill vec.data with values
  vec.elems_num = filled_num; //save how many values was filled in this iteration
}

Use it in the very same way:

upTo4ElemVectorOfInts myVector;

for (int i = 0; i < 100; ++i)
{
  fillVector(myVector);
  //use of myVector:
  //- myVector.data contains data (it's equivalent of std::vector<>::data())
  //- myVector.elems_num will tell you how many numbers you should care about
  //nothing needs to be resized/cleared
}

Additional Note:

If you want more general solution (to operate on any type or size), you can, of course, use templates:

template <class T, size_t Size>
struct upToSizeElemVectorOfTs
{
  T data[Size];
  size_t elems_num;
};

and adjust fillVector() to accept template instead of known type.

This solution is probably the fastest one. You can think: "Hey, and if I want to fill up to 100 elements? 1000? 10000? What then? 10000-elem array will consume a lot of storage!". It would consume anyway. Vector is resizing itself automatically and this reallocs are out of your control and thus can be very inefficient. If your array is reasonably small and you can predict max required size, always use fixed-size storage created on local stack. It's faster, more efficient and simpler. Of course this won't work for arrays of 1.000.000 elements (you would get Stack Overflow in this case).

Mateusz Grzejek
  • 10,635
  • 3
  • 30
  • 47
  • That is what I was looking for! Do you think this will be more efficient than my previous code? – Javi Feb 14 '14 at 12:40
  • Read update - yes, it will. Containers do some hard work to automate process of reallocation and this is always unwanted. Also, they allocate memory on heap (via malloc()). With solution presented above, memory will be 'allocated' on local stack, which is way faster. Read here: http://stackoverflow.com/questions/161053/c-which-is-faster-stack-allocation-or-heap-allocation. – Mateusz Grzejek Feb 14 '14 at 12:47
  • 1
    You have just reinvented `std::array` with your struct. Use the Standard Library instead. – Snps Feb 14 '14 at 12:57
  • -1: `You don't want to (de)allocate memory each time.` He doesn't. – Lightness Races in Orbit Feb 14 '14 at 13:28
  • @Snps std::array is a wrapper for a particular modus operandi. I wanted to present this behavior and also explain differences between heap vs stack allocations. I wanted to explain what is the best approach and why, not simply provide ready-to-use snippet. – Mateusz Grzejek Feb 14 '14 at 13:42
  • @Lightness Races in Orbit Read carefully before downvoting. I didn't say that he does. I listed 3 points that contains assumptions that my solution will take into account. I was referring: "in order to try to avoid allocating an deallocating", to explain that my solution will also avoid it - that was the main requirement. – Mateusz Grzejek Feb 14 '14 at 13:45
  • 1
    @MateuszGrzejek: But you don't _need_ any other solution. Your answer does not point out that the premise of the question is flawed. The answer to "how can I make my code do X" when it does "X" already is "your code already does X", not "use Y"! If you want another reason, the first sentence of your answer, `std::vector is not a solution in this case`, is flat out incorrect. – Lightness Races in Orbit Feb 14 '14 at 13:56
  • @LightnessRacesinOrbit I do not get the point of this but I have to say that after implementing this solution the execution time went down from 2.6 seconds to 1.8s which is pretty awesome. And actually, vector destroys elements always and the code he provides do not, so he is already answering my question (times are the prove). Thank you all guys! – Javi Feb 14 '14 at 15:55
  • 1
    `And actually, vector destroys elements always` Explain to me how you "destroy" an `int`, please. The time difference is because an internal "size" member is altered on each iteration. That's not vector's fault, and it is not a reason to stop using vector, contrary to what this answer asserts. – Lightness Races in Orbit Feb 14 '14 at 16:28
  • 1
    What. This is horrific. Have you considered learning how to use a vector *properly* instead? – jalf Feb 14 '14 at 16:38
  • @jalf are you aware that comments like that are not useful at all? It is better if you give us some light. In my implementation the fill Vector() function also increments the size counter. INT are not destroyed in this case but memory reallocation is happening all the time. Vectors are nice, but some books about efficient programming shows that for some specific cases it is better to create an own implementation. – Javi Feb 16 '14 at 13:21
  • @LightnessRacesInOrbit the previous comment is also for you. There should be something that I'm missing but I see that the simplicity of the solution proposed and its results match my objectives. – Javi Feb 16 '14 at 13:24
  • 1
    @JaviV: It's because your interpretation of the answer and the ways in which it benefits you begin at an incorrect premise. There are things you think `std::vector` is doing or must do (and therefore "I must make my own implementation that doesn't do those things) which are not true. Unfortunately, this Q&A is now such a mess of falsehoods and conflicting requirements that I can't even keep track of what it is that you want any more. – Lightness Races in Orbit Feb 16 '14 at 14:59