Do C ++ compilers compile duplication of duplication?

If I have an example function, for example:

void func1(float a, float b, float c) { setA(a); setB(b); setC(c); } 

What causes the built-in functions:

 inline void setA(float a){ m_a = a; m_isValid = false; } inline void setB(float b){ m_b = b; m_isValid = false; } inline void setC(float c){ m_c = c; m_isValid = false; } 

Should I worry about duplicates of "m_isValid = false" or does the compiler eliminate them by optimizing?

+4
source share
3 answers

A good compiler should remove them in this particular case.

Complete Compilation Example

 struct Foo { float m_a, m_b, m_c; bool m_isValid; void setA(float a){ m_a = a; m_isValid = false; } void setB(float b){ m_b = b; m_isValid = false; } void setC(float c){ m_c = c; m_isValid = false; } void func1(float a, float b, float c); }; Foo f; void func1(float a, float b, float c) { f.setA(a); f.setB(b); f.setC(c); } 

g ++ in this case compiles func1 in

 _Z5func1fff: .LFB3: .cfi_startproc movl 4(%esp), %eax ;; loads a movb $0, f+12 ;; clears m_isValid movl %eax, f ;; stores m_a movl 8(%esp), %eax ;; loads b movl %eax, f+4 ;; stores m_b movl 12(%esp), %eax ;; loads c movl %eax, f+8 ;; stores m_c ret .cfi_endproc 

Please note that although it is true that you should follow how to develop the program if performance is a problem, this kind of optimization at the micro level is best done at the end, after measuring where the code actually loses time.

+6
source

Yes, this is usually called Dead Store Elimination (read = load and write = stored in compiler language).

In general, any useless operation can be optimized by the compiler if it can prove that you (the user) cannot notice it (within the limits set by the language).

To destroy the Dead Store, in particular, it is usually limited to:

  • body of a single function (however inline is used here)
  • no intermediate calls to opaque functions

Some examples:

 struct Foo { int a; int b; }; void opaque(Foo& x); // opaque, aka unknown definition Foo foo() { Foo x{1, 2}; xa = 3; return x; // provably returns {3, 2} // thus equivalent to Foo foo() { return {3, 2}; } } Foo bar() { Foo x{1, 2}; opaque(x); // may use xa, so need to leave it at '1' for now xa = 3; return x; } Foo baz() { Foo x{1, 2}; opaque(x); xa = 1; // xa may have been changed, cannot be optimized return x; } 

Note that regardless of whether you keep the same value or not, as long as the compiler can prove that the variable is not read between the two operations of the repository, it can safely eliminate the first.

Special case: according to the specification in C ++, loading / saving in volatile cannot be optimized. This is due to the fact that volatile was specified in order to allow interaction with the hardware, and thus, the compiler cannot know a priori whether the hardware will read or write the variable behind the program back.

Another special case: in order to optimize memory synchronization operations (fences, barriers, etc.) used in multi-threaded programs can also prevent such optimizations. This is because, as with volatile , synchronization means that another thread of execution can change the variable behind that thread.

Finally, like all optimizations, its effectiveness is largely dependent on knowledge of the context. If it is proven that opaque either does not read or does not write to xa , then some stores can be optimized (it can be proved if the compiler can verify the definition of opaque ), so in general it really depends on embedding and constant distribution.

+8
source

Most modern compilers (with the optimization option turned on) should do this!

0
source

Source: https://habr.com/ru/post/1498845/


All Articles