In C++ block scope, is re-using stack memory the area of optimization?

孤街醉人 提交于 2021-01-29 06:57:56

问题


I tested the following codes:

void f1() {
  int x = 1;
  cout << "f1  : " << &x << endl;
}

void f2() {
  int x = 2;
  cout << "f2  : " << &x << endl;
}

void f3() {
  {
    int x = 3;
    cout << "f3_1: " << &x << endl;
  }
  {
    int x = 4;
    cout << "f3_2: " << &x << endl;
  }
}

int main() {
  f1();
  f2();
  f3();
}

in release build, the output is...

f1  : 00FAF780
f2  : 00FAF780 
f3_1: 00FAF780
f3_2: 00FAF780  <-- I expected

but in debug build,

f1  : 012FF908
f2  : 012FF908
f3_1: 012FF908
f3_2: 012FF8FC  <-- what??

I thought the rule was to move the stack pointer to use the stack memory again when the block is ended.
Is this principle the area of optimization?


回答1:


A debug build is usually very unoptimized on purpose. It might be that each variable, regardless of scope, is given its own spot on the stack so that when your code crashes it can show you the state of each one. This wouldn't be possible if they all shared an address.




回答2:


The result is dependent on the compiler that you are using.

I tried in online compiler. I got the same addresses.

I tried this online compiler.

https://www.onlinegdb.com/online_c++_compiler




回答3:


I thought the rule was to move the stack pointer to use the stack memory again when the block is ended.

When it comes to optimizations you should not only think in terms of stack and heap, those are for sure an important part when doing optimizations, but the specification does not say anything about that, the specs only talk about lifetime, storage duration, and behavior. Stack and heap are just one way to implement them. So a compiler/optimizer is free to do whatever it wants as long as it fulfills the requirements of the specification.

For POD objects - that don't have any special behavior for construction or destruction - the compiler could completely optimize those objects (and their members) away and work with its value (or those of their members) directly. As @tadman already said in the comment, asking for the address, can break many of the possible optimizations. As you explicitly tell the compiler that you need to know something about the object.

It also heavily depends on the compiler, version, compiler flags, and architecture you compile for (arm, x64, haswell, sandy bridge, …) and on the surrounding code.

Because the compiler does assumptions about what generated code might perform best. E.g. allows the pipeline and branch predictor to do the best work.

If you e.g. use printf instead of std::cout the output could be:

f1  : 0x7ffc7781f56c
f2  : 0x7ffc7781f56c
f3_1: 0x7ffc7781f54c
f3_2: 0x7ffc7781f54c

Or if you place all of the code you show in one function:

void f1() {
  {
    int x = 1;
    cout << "f1  : " << &x << endl;
  }
  {
    int x = 2;
    cout << "f2  : " << &x << endl;
  }
  {
    int x = 3;
    cout << "f3_1: " << &x << endl;
  }
  {
    int x = 4;
    cout << "f3_2: " << &x << endl;
  }
}

The result for the same compiler could be:

f1  : 0x7ffc652aac34
f2  : 0x7ffc652aac34
f3_1: 0x7ffc652aac34
f3_2: 0x7ffc652aac34

So the concept of stack (visual representation), in terms of lifetime, is a way to visualize what is happening, but by no means what actually happens in terms of memory utilization on the stack (memory concept). The compiler could reserve a certain amount of memory on the stack (memory) and save the used values in it in which order it seems best. It often matches the visual representation of the stack but does not need to.

With optimizations turned off, the compiler will often use a distinct location on the stack for the variables of a function. But this is also not a guarantee. If the objects on the stack are too large the compiler might stop doing that even in a not optimized debug build:


struct Foo {
   int x1;
   int x2;
   int x3;
   int x4;
   int x5;
   int x6;
   int x7;
   int x8;
   int x9;
   int x10;
};

void f1() {
  Foo x;
  cout << "f1  : " << &x << endl;
}

void f2() {
  Foo x;
  cout << "f2  : " << &x << endl;
}

void f3() {
  {
    Foo x;
    cout << "f3_1: " << &x << endl;
  }
  {
    Foo x;
    cout << "f3_2: " << &x << endl;
  }
}

int main() {
  f1();
  f2();
  f3();
}

Would result in the same memory address for gcc x86-64 but for different addresses for clang x86-64.



来源:https://stackoverflow.com/questions/63388369/in-c-block-scope-is-re-using-stack-memory-the-area-of-optimization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!