是的,这通常称为死存储消除(用编译器的说法读取 = 加载和写入 = 存储)。
通常,编译器可以优化任何无用的操作,前提是它可以证明您(用户)无法注意到它(在语言设置的范围内)。
特别是对于 Dead Store Elimination,它通常仅限于:
- 单个函数的主体(但是,内联在这里有帮助)
- 不干预对不透明函数的调用
一些例子:
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
无法优化对 a 的加载/存储。这是因为volatile
被指定为允许与硬件交互,因此编译器无法先验地知道硬件是否会在程序背后读取或写入变量。
另一种特殊情况:出于优化的目的,多线程程序中使用的内存同步操作(栅栏、屏障等)也可以防止这种优化。这是因为,与这种情况非常相似volatile
,同步意味着另一个执行线程可能已经修改了该线程背后的变量。
最后,像所有优化一样,它的有效性在很大程度上取决于对上下文的了解。如果证明opaque
不读取或不写入x.a
,则可能会优化某些存储(如果编译器可以检查 的定义,则可以证明opaque
),因此通常它实际上取决于内联和常量传播。