Optimizing storage of vectors of dynamically sized vectors - c++

I've encountered this problem pattern multiple times in some work I'm doing, and I'm wondering if a known solution exists.
It's simple: I have a vector of elements, which in turn are vectors of some dynamic size. I know the size of the inner vectors will be relatively small (i.e. in the order of 10s of items, in the average case), but there will be a lot of them.
I can solve this naively:
vector<vector<item>> vss;
Using this approach memory allocations in the inner vector will be all over the place. Iterating over all elements within vss will be a mess cache-wise, and this may cause me performance problems.
I'm thinking this could be solved using some sort of linked list-structure with multiple heads within the same block of memory.
Assuming that the size of the inner vectors can't be predetermined, is there a way to construct and fill vss such that iterating over the elements is not going to be a cache disaster?
Thank you.

I just wanted to add my current, but hopefully temporary, solution. Instead of filling up vss directly, I use a temporary vector of pairs:
vector<pair<size_t, item>> temporaries;
, which denotes that some item should be inserted at a specific index. From here I count up the number of entries per index, allocate a single block of memory to hold the items, and move the data. Some additional vectors are used for book-keeping (i.e. number of items per index, and their starting position).

Related

Efficiently storing a matrix with many zeros, dynamically

Background:
I'm working in c++.
I recall there being a method to efficiently (memory-wise) store "arrays" (where an array might be made of std::vector's, std::set's, etc... I don't care how, so long as it is memory efficient and I'm able to check the value of each element) of 0's and 1's (or, equivalently, truth/false, etc), wherein there is a disproportionate number of one or the other (e.g. mostly zeroes).
I've written an algorithm, which populates an "array" (currently, a vector<vector<size_t>>) with 0's and 1's according to some function. For these purposes, we can more-or-less consider it as being done randomly. The array is to be quite large (of variable size... on the order of 1000 columns, and 1E+8 or more rows), and always rectangular.
There need be this many data points. In the best of times, my machine becomes quickly resource constrained and slows to a crawl. At worst, I get std::bad_alloc.
Putting aside what I intend to do with this array, what is the most efficient (memory-wise) way to store a rectangular array of 1's and 0's (or T/F, etc), where there are mostly 1's or 0's (and I know which is most populous)?.
Note that the array need be created "dynamically" (i.e. one element at a time), elements must maintain their location, and I need only to check the value of individual elements after creation. I'm concerned about memory footprint, nothing else.
This is known as a sparse array or matrix.
std::set<std::pair<int,int>> bob;
If you want 7,100 to be 1, just bob.insert({7,100});. Missing elements are 0. You can use bob.count({3,7}) for a 0/1 value if you like.
Now looping over both columns are rows is tricky; easiest is to make 2 sets each backwards.
If you have no need to loop in order, use an unordered set instead.

Performance implications of using a list of vectors versus a vector of vectors when appending in parallel

It seems that in general, vectors are to be preferred over lists, see for example here, when appending simple types.
What if I want to fill a matrix with simple types? Every vector is a column, so I am going to go through the outer vector, and append 1 item to each vector, repeatedly.
Do the latter vectors of the outer vector always have to be moved when the previous vectors increase their reserved space? As in is the whole data in one continuous space? Or do the vectors all just hold a pointer to their individual memory regions, so the outer vector's memory size remains unchanged even as the individual vectors grow?
Taken from the comments, it appears vectors of vectors can happily be used.
For small to medium applications, the efficiency of the vectors will seldom be anything to worry about.
There a couple of cases where you might worry, but they will be uncommon.
class CData {}; // define this
typedef std::vector<CData> Column;
typedef std::vector<Column> Table;
Table tab;
To add a new row, you will append an item to every column. In a worst case, you might cause a reallocation of each column. That could be a problem if CData is extremely complex and the columns currently hold a very large number of CData cells (I'd say 10s of thousands, at least)
Similarly, if you add a new column and force the Table vector to reallocate, it might have to copy each column and again, for very large data sets, that might be a bit slow.
Note, however, that a new-ish compiler will probably be able to move the columns from the old table to the new (rather than copying them), making that trivially fast.
As #kkuryllo said in a comment, it generally isn't anything to worry about.
Work on making your code as clean, simple and correct as possible. Only if profiling reveals a performance problem should you worry about optimising for speed.

Why is it so slow to add or remove elements in the middle of a vector?

According to Accelerated C++:
To use this strategy, we need a way to remove an element from a vector. The good news is that such a facility exists; the bad news is that removing elements from vectors is slow enough to argue against using this approach for large amounts of input data. If the data we process get really big, performance degrades to an astonishing extent.
For example, if all of our students were to fail, the execution time of the function that we are about to see would grow proportionally to the square of the number of students. That means that for a class of 100 students, the program would take 10,000 times as long to run as it would for one student. The problem is that our input records are stored in a vector, which is optimized for fast random access. One price of that optimization is that it can be expensive to insert or delete elements other than at the end of the vector.
The authors do not explain why the vector would be so slow for 10,000+ students, and why in general it is slow to add or remove elements to the middle of a vector. Could somebody on Stack Overflow come up with a beautiful answer for me?
Take a row of houses: if you build them in a straight line, then finding No. 32 is really easy: just walk along the road about 32 houses' worth, and you're there. But it's not quite so fun to add house No. 31&half; in the middle — that's a big construction project with a lot of disruption to husband's/wife's and kids' lives. In the worst case, there is not enough space on the road for another house anyway, so you have to move all the houses to a different street before you even start.
Similarly, vectors store their data contiguously, i.e. in a continuous, sequential block in memory.
This is very good for quickly finding the nth element (as you simply have to trundle along n positions and dereference), but very bad for inserting into the middle as you have to move all the later elements along by one, one at a time.
Other containers are designed to be easy to insert elements, but the trade-off is that they are consequently not quite as easy to find things in. There is no container which is optimal for all operations.
When inserting elements into or removing elements from the middle of a std::vector<T> all elements after the modification point need to moved: when inserting they need to be moved further to the back, when removing they need to be moved forward to close the gap. The background is that std::vector<T> is basically just a contiguous sequence of elements.
Although this operation isn't too bad for certain types it can become comparatively slow. Note, however, that the size of the container needs to be of some sensible size or the cost of moving be significant: for small vectors, inserting into/removing from the middle is probably faster than using other data structures, e.g., lists. Eventually the cost of maintaining a more complex structure does pay off, however.
std::vector allocates memory as one extent. If you need to insert an element in the middle of the extend you have to shift right all elements of the vector that to make a free slot where you will nsert the new element. And moreover if the extend is already full of elements the vector need to allocate a new larger extend and copy all elements from the original extent to the new one.

Two Dimensional Vectors in C++

Hey I was curious about creating a 2 dimensional vector of chars so I know it's a vector inside a vector and I'd like to initialize all values to ','.
So i know this much I believe
vector<vector<char>> spaceStation();
I'm guessing the columns and rows go in the parenthesis but I'm not exactly sure.
Thanks!
If it is going to be a nxn or nxm 2D matrix that you are looking for, where n and m can be determined before the creation of the matrix (even if it at run time) then vector of vectors may not be the most efficient way to go about it. Internally the way vector grows is by allocating a bigger memory if the current allocated space gets filled up and moving all the existing data to the new location. Similarly inserting a new first row would require all the vectors to move down which would in turn result in copying all the other vectors and its elements down.
If you know n,m, even if it is at run time you could allocated a big block of memory using a single vector (of size n^2 or nxm) and access elements logically as a vector (a[i][j] would be vector[i*n + j]) thus efficiently managing a single memory block.

Why is my insertion into STL list running slow?

I'm trying to implement an undirected graph using adjacency list. I used the following code:
int v,e;
scanf("%d%d",&v,&e);
list<int> graph[3000];
for(int i=0;i<e;i++){
int a,b;
scanf("%d%d",&a,&b);
graph[a].push_back(b);
graph[b].push_back(a);
}
To test the running time of my code I created an input file with 3000 vertices and all possible edges. It took 2.2 seconds to run. I tried to optimise by changing it to a two dimensional array as follows
int graph[3000][3000];
for(int i=0;i<e;i++){
int a,b;
scanf("%d%d",&a,&b);
graph[a][p[a]]=b;
graph[b][p[b]]=a;
p[a]++;
p[b]++;
}
where 'p' is of size 3000 initalised with all zeros. This code ran in just 0.35 seconds for the same input file. I'm using gcc-4.3.2 compiler. I know insertion at the end of a list can be done in constant time then why is the first code running slow? Is there a chance of optimising the linked list implementation?
Thanks in advance
Avoid std::list. That's a doubly linked list, which is very cache unfriendly (the nodes are randomly distributed in memory) and involves a large overhead (2 pointers per element). So every time you append something, the list allocates 2*sizeof(void*)+sizeof(int) bytes and additionally some memory management overhead of operator new.
Later in the algorithm, when you will iterate over the values, you literally jump all over the whole memory, which is further slow.
The 2d array doesn't have this problem, but it does waste some memory.
I usually represent an adjacency list as a vector of vectors.
std::vector<std::vector<int> > graph;
Note that a vector can also push_back values in O(1) (as well as a std::deque, which can append even faster but is slower when traversing). If the graph is expected to be dense, then an adjacency matrix may be a better choice.
Insertion into a list requires allocating a new node. So when you're doing your 6000 push-backs, you have to do 6000 memory allocations. In the array case, you don't have to do any allocations at all, so that's a lot faster. That's the full difference.
To expand on the answers here, implement a linked list class yourself, and you will find out why it is slow.
There are things that can be done such as implementing a list containing a capacity value, a size value and a pointer that points to the first node in the actual list. That pointer is actually a dynamic array, and when size==capacity, the array is resized and the capacity increased by some factor (e.g. 10).
The drawback is that it is limited to 2^(sizeof capacity * CHAR_BIT) - 1 elements whereas just allocating nodes each time involves longer insertion times with the benefit of a theoretically unlimited amount of nodes. You'd most likely run out of memory before maxing out the capacity of the faster list implementation, but there is no guarantee of that, not to mention resizing the list usually involves making a copy of it, so that capacity maximum suddenly has a much smaller limit on it anyway.
Linked lists are generally slow. They have their uses, but if you need fast run times, find a better implementation, use a different container such as std::vector, or create a solution yourself, though honestly the standard containers do pretty well.