Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why this program reserves and keeps 2 GB of memory instead of 1 GB?

Tags:

c++

memory

vector

#include <vector>
typedef std::vector<char> vc;
typedef std::vector<vc> vvc;

vvc f() {
  const int N = (1 << 15);
  return vvc(N, vc(N)); // 1 GB
}

int main () {
  vvc v; 
  v = f();
  while(true);  // Why 2GB allocated ?
  return 0;
}

Compiled with both clang -O2 and g++ -O2. Same behavior.

Edit: There are multiple ways to fix this code pointed in some of the answers. But my point is to understand this code. Of course there is a temporary object, but it should disappear at the semicolon and 1GB of memory should be returned to the system. The question intends to ask why it does not happen.

Edit 2: The destructor of temporary object is indeed called before semicolon.

like image 513
Łukasz Lew Avatar asked Jun 09 '13 09:06

Łukasz Lew


3 Answers

My guess is that you're looking at the operating system display of how much memory is allocated to the process, and that your compiler doesn't yet support C++11's move assignment.

Therefore what happens is probably the following:

  • In your function, you allocate the vector (consuming 1GB of memory).
  • Then, in your assignment, that vector is copied (consuming anmother 1GB of memory, which is adjacent to the first block).
  • After that, the original vector is destroyed. That frees the memory for future allocations in your program, but since that memory is before the memory of the retained vector, and for most operating systems the memory allocated to a process must be contiguous, the process can not give it back to the operating system.

So the result is that the operating system allocates 2GB to your process, of which 1GB is allocated to the vector, and 1GB is free for future allocation within your program (but not to allocations in another process).

like image 167
celtschk Avatar answered Nov 15 '22 15:11

celtschk


You have assignment in your code that we preach against in vain for good reason ;-)

If you used initialization, that allows RVO optimization, and as others experimented it actually seem working too.

With assignment the function return an object, then it must be copied to target and only then nuke the temporary. While with data flow anal it could be possibly optimized, it's a case that hard to catch and considered rare to use. Thus you suffer the performance penalty on top of all the rest.

EDIT: to see that the temporary is indeed nuked at the proper point I suggest using debugger and single-step into functions. Alternatively look at assy listing. For the abstract machine the dtor call must happen before the next instruction.

However, optimized code uses the 'as if' rule more liberally. If the code has no way to tell the difference, it may postpone it a little, and you may just experience that effect.

C++11 note: in C++11 vector gained another op= that moves from the rvalue. That would cure this particular case, but it will take time till all compilers close the gap and especially all classes gain moving stuff. Pre-11 code looking for moving would use swap() in place of =.

like image 37
Balog Pal Avatar answered Nov 15 '22 15:11

Balog Pal


I've just tested on win32, vc7.

Your code allocates 2Gb. If change to this:

int main () {
  vvc v = f();
  while(true);

It needs only 1Gb.

I suppose the reason - is a copy operation between vectors.

v = f() - invokes constructor. Your case - default c'tor and copy (operator =) into empty object. Coping needs 2 Gb.

Internal actions of f() (creating vector and return) can use RVO, and there are no any coping and extra allocating.

like image 41
Boris Avatar answered Nov 15 '22 14:11

Boris