C++ vector performance with predefined capacity - c++

There is 2 ways to define std::vector(that i know of):
std::vector<int> vectorOne;
and
std::vector<int> vectorTwo(300);
So if i don't define the first and fill it with 300 int's then it has to reallocate memory to store those int's. that would mean it would not for example be address 0x0 through 0x300 but there could be memory allocated inbetween because it has to be reallocated after, but the second vector will already have those addresses reserved for them so there would be no space inbetween.
Does this affect perfomance at all and how could I meassure this?

std::vector is guaranteed to always store its data in a continuous block of memory. That means that when you add items, it has to try and increase its range of memory in use. If something else is in the memory following the vector, it needs to find a free block of the right size somewhere else in memory and copy all the old data + the new data to it.
This is a fairly expensive operation in terms of time, so it tries to mitigate by allocating a slightly larger block than what you need. This allows you to add several items before the whole reallocate-and-move-operation takes place.
Vector has two properties: size and capacity. The former is how many elements it actually holds, the latter is how many places are reserved in total. For example, if you have a vector with size() == 10 and capacity() == 18, it means you can add 8 more elements before it needs to reallocate.
How and when the capacity increases exactly, is up to the implementer of your STL version. You can test what happens on your computer with the following test:
#include <iostream>
#include <vector>
int main() {
using std::cout;
using std::vector;
// Create a vector with values 1 .. 10
vector<int> v(10);
std::cout << "v has size " << v.size() << " and capacity " << v.capacity() << "\n";
// Now add 90 values, and print the size and capacity after each insert
for(int i = 11; i <= 100; ++i)
{
v.push_back(i);
std::cout << "v has size " << v.size() << " and capacity " << v.capacity()
<< ". Memory range: " << &v.front() << " -- " << &v.back() << "\n";
}
return 0;
}
I ran it on IDEOne and got the following output:
v has size 10 and capacity 10
v has size 11 and capacity 20. Memory range: 0x9899a40 -- 0x9899a68
v has size 12 and capacity 20. Memory range: 0x9899a40 -- 0x9899a6c
v has size 13 and capacity 20. Memory range: 0x9899a40 -- 0x9899a70
...
v has size 20 and capacity 20. Memory range: 0x9899a40 -- 0x9899a8c
v has size 21 and capacity 40. Memory range: 0x9899a98 -- 0x9899ae8
...
v has size 40 and capacity 40. Memory range: 0x9899a98 -- 0x9899b34
v has size 41 and capacity 80. Memory range: 0x9899b40 -- 0x9899be0
You see the capacity increase and re-allocations happening right there, and you also see that this particular compiler chooses to double the capacity every time you hit the limit.
On some systems the algorithm will be more subtle, growing faster as you insert more items (so if your vector is small, you waste little space, but if it notices you insert a lot of items into it, it allocates more to avoid having to increase the capacity too often).
PS: Note the difference between setting the size and the capacity of a vector.
vector<int> v(10);
will create a vector with capacity at least 10, and size() == 10. If you print the contents of v, you will see that it contains
0 0 0 0 0 0 0 0 0 0
i.e. 10 integers with their default values. The next element you push into it, may (and likely will) cause a re-allocation. On the other hand,
vector<int> v();
v.reserve(10);
will create an empty vector, but with its initial capacity set to 10 rather than the default (probably 1). You can be certain that the first 10 elements you push into it will not cause an allocation (and the one probably will but not necessarily, as reserve may actually set the capacity to more than what you requested).

You should use reserve() method:
std::vector<int> vec;
vec.reserve(300);
assert(vec.size() == 0); // But memory is allocated
This solves the problem.
In your example it affects the performance greatly. You can expect, that when you overflow the vector, it doubles the allocated memory. So, if you push_back() into vector N times (and you haven't called "reserve()"), you can expect O(logN) reallocations, each of them causing copying of all values. So the total complexity is expected to be O(N*logN), although it is not specified by C++ standard.

The differences can be dramatic because if data is not adjacent in memory, the data may have to be fetched from main memory which is 200 times slower than a l1 cache fetch. This will not happen in a vector because data in a vector is required to be adjacent.
see https://www.youtube.com/watch?v=YQs6IC-vgmo
Use std::vector::reserve, when you can to avoid realloc events. The C++ 'chrono' header has good time utilities, to measure the time difference, in high resolution ticks.

Related

Memory allocation function based on chunks

I have to create a memory allocation program that is supposed to allocate memory blocks to a memory that contains 8 contiguous memory positions. N is the size of the memory, M is the maximum number of positions a program can request and X is the number of memory positions requested. The memory is divided in M chunks, each equally made of N/M memory positions. N/M must be greater than M, otherwise a program will not have enough memory allocated to a chunk. The first element of POS stores the position (in the memory) where the first available block of 1 memory position is located, the second element stores the position (in memory) where the first available block of 2 memory positions is located and so on. If the ith element in the array POS stores number -1, it means that there are no blocks of (i+1) memory positions available in the ith chunk. If POS[X-1] stores a value different from -1, it means there is space to allocate a block of X. In that case, the value stored in POS[X-1] is returned. Otherwise, the value -1 is returned.
From what I understand of the question, it wants me to create a memory allocation program that separates 8 memory positions into chunks. Each chunk is required to be about as big as M, which is the largest number of memory positions a program can request. So M is 3 in the question so each chunk should be about 3 memory positions, meaning two chunks of 3 and one chunk of 2. Now the first chunk is allocated only for requests that only need 1 memory position, second chunk for requests that need 2 contiguous memory positions and third chunk for requests that need 3 memory positions. Example using 16 memory positions. Here's what I have so far.
#include <iostream>
using namespace std;
int search(int arr[], int n, int m, int x)
{
int POS[m];
if(POS[x-1]!=-1){
return POS[x-1];
} return -1;
}
int main(void)
{
int arr[] = { -1, -1, -1, -1, -1, -1, -1, -1 }; //example array, -1 represents available memory space
int x = 2; //example number of memory positions requested
int n = sizeof(arr) /sizeof(arr[0]); //size of array
int m = 3; //maximum number of positions a program can request
int result = search(arr, n, m, x);
if(result == -1) {
cout << "No slots available";
} else {
cout << x << " slots available at index " << result;;
}
}
To rephrase a little, it sounds like your question is essentially that there is an array of N ints where -1 represents an "available" element, and the goal is to find the position of X contiguous available element. I'm ignoring M and the POS array.
In other words, find a run of X or more -1 values. Here is an outline to do that with an O(N) search:
Initialize count to zero.
Make a loop, for i = 0, 1, ..., N - 1:
If arr[i] is available, increment the count. If the count is now equal to X, return (i - X) as the starting position of the X contiguous available elements.
Otherwise, reset the count to zero.
Edit: Based on your comment and example image, it sounds like the point of M-sized chunks is that each chunk of memory may contain only one allocation. This approach of course wastes some elements when allocations are smaller than M, but does make the allocation search easier.
Outline:
For each chunk...
Check whether the first arr element of the chunk is unused. If it so, then the whole chunk is available by the "one allocation per chunk" assumption.
To cover an edge case, if this is the last chunk, check that its size is at least X.
If both of these conditions are true, return the current chunk.

Is the size of a vector simply the size of the sum of its parts?

For example, a float is 4 bytes. Does this mean that a vector containing 10 floats is exactly 40 bytes?
One may interpret the "size" of a vector in different ways:
vector::size() gives the number of elements currently stored in
the vector, regardless of the memory they allocate. So
myVector.size() in your case would be 10
vector:capacity() gives you the number of the elements for which the vector has allocated memory so far. Note that a vector, when continuously appending elements, allocates memory in "chunks", i.e. it might reserve space for 50 new elements at once in order to avoid repeated memory allocs. So capacity() >= size() is always true.
sizeof(myVector) would give you the size of the data structure vector necessary for managing a dynamically increasing series of elements. It typically contains dynamically allocated memory, which is not reflected by sizeof, such that sizeof is of less use in most cases.
See the following code:
int main() {
vector<float> fv;
for (int i=0; i<10; i++) {
fv.push_back(1.0);
}
cout << "size(): " << fv.size() << endl;
cout << "capacity(): " << fv.capacity() << endl;
cout << "sizeof(fv): " << sizeof(fv) << endl;
return 0;
}
Output:
size(): 10
capacity(): 16
sizeof(fv): 24
Hope it helps.
A std::vector has 3 different sizes. There is the size of the vector object itself and you get that with size(std::vector<some_type>). This size isn't really useful for anything though. Typically this will be the size of three pointers as that is how vector is typically implemented.
The second size is what is returned from the size() member function. The value returned from this is the number of elements in the vector. This is the "size" most people use when talking about the size of the vector.
The last size a vector has is the total number of elements it has allocated currently. That is obtained from using the capacity member function.
So a vector holding 10 floats needs to use at least 10 floats worth of memory but it could be using more as the capacity() is allowed to be grater then its size(). But size of the vector object itself (sizeof(name_of_vector)) will be some value and that value will never change no matter how many elements you add into the vector.

2d std::vector Contiguous Memory?

Consider the following code, which allocates a 2d std::vector<std::vector<int> > of size 5 x 3 and prints the memory addresses of each element:
#include <iostream>
#include <vector>
int main() {
int n = 5, m = 3;
std::vector<std::vector<int> >vec (n, std::vector<int>(m));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
std::cout << &(vec[i][j]) << " ";
}
std::cout << "\n";
}
}
Output:
0x71ecc0 0x71ecc4 0x71ecc8
0x71ece0 0x71ece4 0x71ece8
0x71ed00 0x71ed04 0x71ed08
0x71ed20 0x71ed24 0x71ed28
0x71ed40 0x71ed44 0x71ed48
Of course, for a fixed row, each column is contiguous in memory, but the rows are not. In particular, each row is 32 bytes past the start of the previous row, but since each row is only 12 bytes, this leaves a gap of 20 bytes. For example, since I thought vectors allocate contiguous memory, I would have expected the first address of the second row to be 0x71eccc. Why is this not the case, and how does vector decide how much of a gap to give?
The overhead size of a vector is not 0. You have 24 bytes between the last element of your vector and the first element of the next vector. Try this:
cout << sizeof(std::vector<int>) << endl;
You will find the output to be 24 (Likely for your implementation of std::vector and compiler etc). Live example which happens to match.
You can imagine the vector layout like this:
If you want your elements to actually be contiguous then you need to do:
Use a std::array<std::array<int>> for no overhead (c++11 only). Note that this cannot be resized.
Use std::vector<int> and the formula row * numRows + col to access the element for row, col.
Agree with Mr. Fox's results and solutions1. Disagree with the logic used to get there.
In order to be resizable, a std::vector contains a dynamically allocated block of contiguous memory, so the outer vector has a pointer to a block of storage that contains N vectors that are contiguous. However, each of these inner vectors contains a pointer to its own block of storage. The likelihood (without some really groovy custom allocator voodoo) of all N blocks of storage being contiguous is astonishingly small. The overhead of the N vectors is contiguous. The data almost certainly not, but could be.
1 Mostly. The std::arrays in a std::vector will be contiguous, but nothing prevents the implementor of std::array from putting in additional state information that prevents the arrays in the std::arrays from being contiguous. An implementor who wrote the std::array in such a way is an occupant of an odd headspace or solving a very interesting problem.

C++ vector.erase() function bug

I have this vector:
list.push_back("one");
list.push_back("two");
list.push_back("three");
I use list.erase(list.begin() + 1) to delete the "two" and it works. But when I try to output the list again:
cout<<list[0]<<endl;
cout<<list[1]<<endl;
cout<<list[2]<<endl;
produces:
one
three
three
I tried targeting the last element for erasing with list.erase(list.begin() + 2), but the duplicate three's remain. I imagined index 2 should have been shifted and list[2] should have outputted nothing. list[3] outputs nothing, as it should.
I'm trying to erase the "two" and output the list as only:
one
three
When using cout<<list[2]<<endl; you asume that you still have three elements. But in fact you are accessing remaining data in a part of the memory that is no more used.
You should use list.size () to obtain the number of elements. So, something like:
for ( size_t i = 0; i < list.size (); i++ )
{
cout<<list[i]<<endl;
}
But you erased the element, thus the size of your container was decreased by one, i.e. from 3 to 2.
So, after the erase, you shouldn't do this:
cout<<list[0]<<endl;
cout<<list[1]<<endl;
cout<<list[2]<<endl; // Undefined Behaviour!!
but this:
cout<<list[0]<<endl;
cout<<list[1]<<endl;
In your case, the "three" is just copied to the index 1, which is expected. you is vector.size() == 2 now.
it is because vector will do pre-allocation, which help to improve the performance.
To keep from having to resize with every change, vector grabs a block of memory bigger than it needs and keeps it until forced to get bigger or instructed to get smaller.
To brutally simplify, think of it as
string * array = new string[100];
int capacity = 100
int size = 0;
In this case you can write all through that 100 element array without the program crashing because it is good and valid memory, but only values beneath size have been initialized and are meaningful. What happens when you read above size is undefined. Because reading out of bounds is a bad idea and preventing it has a performance cost that should not be paid by correct usage, the C++ standard didn't waste any time defining what the penalty for doing so is. Some debug or security critical versions will test and throw exceptions or mark unused portions with a canary value to assist in detecting faults, but most implementations are aiming for maximum speed and do nothing.
Now you push_back "one", "two", and "three". The array is still 100 elements, capacity is still 100, but size is now 3.
You erase array[1] and every element after 1 up to size will be copied up one element (note potentially huge performance cost here. vector is not the right data structure choice if you are adding and removing items from it at random locations) and size will be reduced by one resulting in "one", "three", and "three". The array is still 100 elements, capacity is still 100, but size is now 2.
Say you add another 99 strings. This pushes size each time a string is added and when size will exceed capacity, a new array will be made, the old array will be copied to the new, and the old will be freed. Something along the lines of:
capacity *= 1.5;
string * temp = new string[capacity];
for (int index = 0; index < size; index ++)
{
temp[index] = array[index];
}
delete array;
array = temp;
The array is now 150 elements, capacity is now 150, and size is now 101.
Result:
There is usually a bit of fluff around the end of a vector that will allow reading out of bounds without the program crashing, but do not confuse this with the program working.

vector memory allocation strategy

i wrote a little piece of code to determine, how memory allocating in a vector is done.
#include <iostream>
#include <vector>
using namespace std;
int main ()
{
vector<unsigned int> myvector;
unsigned int capacity = myvector.capacity();
for(unsigned int i = 0; i < 100000; ++i) {
myvector.push_back(i);
if(capacity != myvector.capacity())
{
capacity = myvector.capacity();
cout << myvector.capacity() << endl;
}
}
return 0;
}
I compiled this using Visual Studio 2008 and g++ 4.5.2 on Ubuntu and got these results:
Visual Studio:
1 2 3 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 12138 18207 27310 40965 61447 92170 138255
capacity = capacity * 1.5;
g++:
1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072
capacity = capacity * 2;
As you can see, these are two very different results.
Why is this like that? Is it only depending on the compiler or is it addicted to other factors?
Does it really make sense to keep on with doubling the capacity, even for large numbers of elements?
How the vector is growing is implementation defined. So different strategies can be used resulting in different capacity after inserting the same count of elements
If you need to rely on how many items are allocated you should use reserve and/or resize methods of vector
As you can see, VS is adding extra space with smaller chunks, while G++ i doing it by the powers of 2. This is just implementations of the same basic idea: the more elements you add, the more space will be allocated next time (because it is more likely that you will add additional data).
Imagine you've added 1 element to the vector, and I've added 1000. It's more likely that will add another 1000 and it is less likely that you will. This is the reasoning for such a strategy of allocating space.
The exact numbers sure depends on something, but that's the reasoning of the compiler makers, since they can implement it in any way they want.
The standard only defines a vector's behaviour. What really happens internally depends on the implementation.
Doubling the capacity results in an amortized O(n) cost for pushing/popping n elements, which is required for a vector, I guess.
Look here for more details.