C++ 编译器会消除重复赋值吗?

问题描述 投票:0回答:3

如果我有一个示例函数,例如:

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

其中调用内联函数:

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; }

我应该关心“m_isValid = false”重复还是编译器通过优化消除它们?

c++ optimization compiler-optimization
3个回答
10
投票

是的,这通常称为“死存储消除”(用编译器的说法,读取 = 加载,写入 = 存储)。 一般来说,任何无用的操作都可以被编译器优化掉,只要它能证明你(用户)无法注意到它(在语言设置的范围内)。

特别是对于死店消除,通常仅限于:

单个函数的主体(但是,内联在这里有帮助)
  • 无需干预调用不透明函数
  • 一些例子:

struct Foo { int a; int b; }; void opaque(Foo& x); // opaque, aka unknown definition Foo foo() { Foo x{1, 2}; x.a = 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 x.a, so need to leave it at '1' for now x.a = 3; return x; } Foo baz() { Foo x{1, 2}; opaque(x); x.a = 1; // x.a may have been changed, cannot be optimized return x; }

注意,是否连续存储相同的值并不重要,只要编译器能够证明在两次存储操作之间没有读取变量,就可以安全地消除第一个存储操作。

特殊情况:根据 C++ 规范,无法优化对

volatile

的加载/存储。这是因为

volatile
被指定为允许与硬件交互,因此编译器无法先验地知道硬件是否会在程序背后读取或写入变量。

另一种特殊情况:出于优化的目的,多线程程序中使用的内存同步操作(栅栏、屏障等)也可以阻止这种优化。这是因为,与

volatile

的情况非常相似,同步意味着另一个执行线程可能已经修改了该线程背后的变量。


最后,与所有优化一样,其有效性很大程度上取决于对上下文的了解。如果证明

opaque

不读取或不写入

x.a
,那么某些存储可能会被优化(如果编译器可以检查
opaque
的定义即可证明),所以一般来说它实际上取决于内联和持续传播。
    


5
投票

完成一个完整的编译示例

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++ 将 
func1

编译为


_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

请注意,虽然如果性能是一个问题,您确实应该关注如何设计程序,但这种微观级别的优化最好在测量代码实际在哪里浪费时间之后在最后进行。


0
投票

© www.soinside.com 2019 - 2024. All rights reserved.