编译器优化如何影响代码逻辑?

6
当相同的代码使用完全禁用优化(g++ -O0)编译,然后再次使用完全启用优化(g++ -O3)编译时,源代码本身的逻辑如何更改?
例如,编译器可以: 这两个优化使代码在不影响原始源代码完整性的情况下运行得更快。任何没有这些优化的代码都将在启用它们的情况下运行。
但是,编译器优化也可能会影响代码逻辑。以下是我知道的两个示例: 我很惊讶,并且很幸运地了解到这些问题,因为它们可能成为错误情况下的巨大潜在问题。
因此,我想知道,是否还有其他情况下C++编译器优化将影响代码逻辑?我特别寻找关于g ++编译器下的C ++11(没有任何未定义行为)的信息,但是欢迎其他编译器的提示。

3
别忘了提到未定义行为意味着任何事情都有可能发生。 - Mysticial
我相信编译器也可以“不计算”后续从未使用过的内容。虽然这不会改变代码的逻辑,但如果你正在衡量性能,这可能是不好的。不过,我不太确定。 - leo
@leo 很好的观点!尽管我总是期望使用优化编译会更快,因为那是优化器的工作。 - Ryan
3
简而言之,你的问题是:「作为if规则」有哪些例外情况? - GManNickG
@Dave,只有当“你编写的方式”在C++语言中被明确定义时才是正确的。编写一些预期行为未被语言规范定义的C++代码并不太困难,在这种情况下,编译器可以自由地执行任何操作,并将任何出现的问题归咎于你 ;) - Jeremy Friesner
显示剩余2条评论
2个回答

8
“似乎”规则:

只要程序的行为表现与需求被遵守时一样,实现可以忽略本国际标准的任何要求。 例如,如果实际实现可以推断出表达式中的某个部分不会被使用并且不会产生影响程序行为的副作用,则无需对其进行评估。

然而,标准提到了一种允许的优化,违反了“似乎”规则:

当满足某些条件时,即使对象的复制/移动构造函数和/或析构函数有副作用,实现也允许省略类对象的复制/移动构造。 在这种情况下,实现将省略复制/移动操作的源和目标视为仅是指向同一对象的两种不同方式,并且该对象的销毁发生在没有进行优化时的两个对象将被销毁的时间之后。称为复制省略的这种复制/移动操作省略适用于以下情况(可组合以消除多个副本):

— 在具有类返回类型的函数中的return语句中,当表达式是非易失自动对象(而不是函数或catch子句参数)的名称,并且该对象具有与函数返回类型相同的cv非限定类型时,则可以通过直接在函数的返回值中构造自动对象来省略复制/移动操作。

— 在throw表达式中,当运算符是不易失自动对象(而不是函数或catch子句参数)的名称,其作用域不延伸到最内层封闭try块的末尾(如果有的话),则可以通过直接将自动对象构造到异常对象中来省略从运算符到异常对象(15.1)的复制/移动操作。

— 当未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv非限定类型的类对象时,则可以通过直接在省略的复制/移动的目标中构造临时对象来省略复制/移动操作。

— 当异常处理程序(第15节)的异常声明声明与异常对象(15.1)具有相同类型(除了cv资格),并且除了执行异常声明所声明的对象的构造函数和析构函数之外,程序的含义不会改变时,可以通过将异常声明视为异常对象的别名来省略复制/移动操作。


1
“未指定行为”是指实现可以选择任何可能的行为。在这种情况下,优化器可能会影响所做的选择。
一个简单的例子是函数参数的求值顺序。非优化构建可以使用从左到右或从右到左,而优化构建可以以“混合”的顺序评估参数。一个很好的理由是最大化参数之间的公共子表达式优化机会。
如果其中任何一个参数具有明显的副作用,则代码逻辑将被改变,但这是否是错误因情况而异。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接