0

What is the expected difference (if any) in memory taken by vvint1 and vvint2? Is vitest1 copied into a new memory position each time a push_back takes place? Is vitest2 copied into a new memory position each time a push_back takes place?

typedef vector<int> vint_t;
typedef vector<vint_t> vvint_t;
size_t nvec = 2;
size_t nvvec = 3;
vvint_t vvint1(nvvec), vvint2(nvvec);
vint_t vitest2(nvec, 1);
for ( j = 0; j < nvvec; j++ ) {
    vint_t vitest1(nvec, 2);
    vvint1.push_back(vitest1);
    vvint2.push_back(vitest2);
}
  • 2
    I think you might need to revise your question, it looks like a typo may have felled it; it currently asks for the difference between "vvint1 and vvint1". Which is the same as the difference between a banana and a banana. – Tommy Feb 26 '20 at 18:46
  • 1
    `vvint1` and `vvint2` are going to be the same. The both copy the vector every time you use `push_pack`. FWIW, a 2d-vector is not what you want. Like a `int**`, `std::vector>` lacks data locality between the rows. That lack of continuity can hamper performance. Instead, what you should do is emulate a sd structure using a class and storing all the data in a 1d vector using math to translate 2d indices into 1d indices. – NathanOliver Feb 26 '20 at 18:49
  • 2
    Unrelated: Sooner or later someone is going to mix up `vint_t` and `vvint_t` because of the similarity between the names and because the names are so similar it's going to be hard to spot. – user4581301 Feb 26 '20 at 19:13
  • 1
    @Tommy - Fixed (even though it was apparent what I meant to ask). Thanks. – sancho.s ReinstateMonicaCellio Feb 26 '20 at 19:16
  • @NathanOliver - I changed a bit the code to emphasize that I was interested in the space taken in memory, regardless of the contents. Now vvint1 and vvint2 won't be the same. My question was precisely related to the evaluation of overall memory taken in different cases, and I suspected what you wrote (still testing to get quantitative details). This will determine whether I would need 1D storage. – sancho.s ReinstateMonicaCellio Feb 26 '20 at 19:20
  • 2
    `vvint_t vvint1(nvvec), vvint2(nvvec);` aaaaaaaahhhh That is so unreadable code. – bolov Feb 26 '20 at 19:23
  • The only difference in memory usage is that the vectors appended to `vvint1` and `vvint2` have different sizes (and possibly capacities). In other words it is the dynamic memory managed by the various objects that may differ. The calculation is fairly trivial. And, BTW, your usage of `typedef`s has obfuscated the code, rather than making it clearer. – Peter Feb 26 '20 at 19:41
  • @Peter I may be misreading it, but don't `vvint1` and `vvint2` have the same sizes, just different initial values? – Tommy Feb 26 '20 at 19:47
  • @Tommy - `sizeof(vvint1) == sizeof(vvint2)` since they have the same type. `sizeof` also gives the same result for the size of individual elements of both vectors. The only difference in their memory usage is related to the dynamic memory allocation performed by them and their elements. – Peter Feb 26 '20 at 19:53
  • @Peter I meant, don't they have the same `.size()`s? They're both constructed as `(nvec, )` so they should both have `nvec` members? I didn't mean to invoke `sizeof` and the fact that vector contents are elsewhere. – Tommy Feb 26 '20 at 19:54
  • @Tommy - The different `.size()`s can affect how much dynamically allocated memory is used by each vector (and contained vector). But `.size()` doesn't specify the actual usage. Changes of `.capacity()` - which can be affected by changes in `.size()`, but it's not a one-to-one change - are also relevant. – Peter Feb 26 '20 at 20:00
  • @Peter I feel like we're talking at crossed purposes. To clarify, you said: "the vectors appended to vvint1 and vvint2 have different sizes". I think this is _not true_. That's the entirety of my point. – Tommy Feb 26 '20 at 20:08
  • @Tommy - this comes back to my comment that the coding had obfuscated the meaning of the code. I read the different values of `1` and `2` as being sizes, but see now I made a mistake in interpretation. Yes, the vectors have the same sizes. That does not (necessarily) mean they have the same capacities. When people are struggling to interpret nested typedefs, as in this code, it's easy to misinterpret which function argument is which. – Peter Feb 26 '20 at 20:24
  • @Peter that's not my coding, but hopefully sancho.s Reinstate Monica, whose question this is, will take note. – Tommy Feb 26 '20 at 20:27

2 Answers2

1

Both vvint1 and vvint2 are initially created with nvvec = 3 default-constructed members (i.e. empty vectors of int).

push_back always either copies or moves, but in this case you're not supplying rvalue references so you'll get copies. Look into std::move for more on that.

You're pushing the same number of things to both vectors. Therefore both vvint1 and vvint2 will end up being the same size.

Tommy
  • 99,986
  • 12
  • 185
  • 204
0

vvint1 and vvint2 memory requirements are:

  1. (on stack, in the example) sizeof(vector<vector<int>>) for the objects themselves, which is the same (vector is 2–3 pointers usually, regardless of the inner type);
  2. (on heap) 2 * nvvec * sizeof(vector<int>) for the contents (nvvec initially and nvvec push_back-ed in the loop); again, that’s the same for vvint1 and vvint2;
  3. (on heap) contents of each vector stored in these vectors. Since vectors don’t share memory, and you store them by value, nvec * nnvec * sizeof(int). Again, the same.

So the overall requirements are the same: sizeof(vector<vector<int>>) + nvvec * sizeof(vector<int>) + nvec * nnvec * sizeof(int)

Plain vector<int> would take less space ofc as item 2 wouldn’t apply. But what is more important is that in vvint_t, inner vectors may be of different lengths, and resizing any inner vector doesn’t affect others. But that adds complexity, so unless you really need that, it’s simpler to use flat vector and calculate index; imaging libraries do it that way.

Regarding second part, both vitests are copied on each push_back. But since C++11, you can write vvint1.push_back(std::move(vitest1)); (or vvint1.emplace_back(std::move(vitest1));) to move instead. For vectors that means the newly-constructed vector takes ownership of vitest1 contents without copying it (so vitest1 becomes empty). That doesn’t change memory requirements but reduces allocations as the space allocated by vitest (at construction) would be reused instead of being freed (at destruction, at end of each iteration).

numzero
  • 2,009
  • 6
  • 6