关于 C++ 优化的问题

Question about optimization in C++

提问人:Luchian Grigore 提问时间:9/22/2011 更新时间:9/24/2011 访问量:335

问:

我读到C++标准允许优化到实际上会阻碍预期功能的程度。当我这么说时,我说的是返回值优化,其中复制构造函数中实际上可能有一些逻辑,但编译器会优化调用。

我发现这有点糟糕,因为不知道这一点的人可能会花相当多的时间来修复由此导致的错误。

我想知道的是,是否有任何其他情况,编译器的过度优化会改变功能。

例如,类似以下内容:

int x = 1;
x = 1;
x = 1;
x = 1;

可以优化为单个 x=1;

假设我有:

class A;

A a = b;
a = b;
a = b;

这是否也可以优化?可能不是最好的例子,但我希望你明白我的意思......

C++ 优化

评论

4赞 Oliver Charlesworth 9/22/2011
我不同意接近投票。这是一个真实、可以回答的问题。
4赞 PlasmaHH 9/22/2011
当省略复制 ctor 导致代码中出现错误时,您一开始就错误地设计了复制 ctor。您的代码不应依赖于周围有多少对象,也不应依赖于复制/分配事物的频率。
1赞 Cat Plus Plus 9/22/2011
复制 ctor 中的逻辑应该是复制对象的逻辑。如果它没有被复制,那么为什么复制 ctor 逻辑需要运行?
0赞 9/22/2011
你的两个例子都可以优化为无操作,因为它们没有任何意义。
0赞 Matthieu M. 9/22/2011
与编译器不完全相关:浮点数的计算可能取决于中间结果是存储在处理器寄存器中还是弹回堆栈,因为在后一种情况下,值是四舍五入的......

答:

12赞 sbi 9/22/2011 #1

省略复制操作是允许编译器优化到副作用明显变化的唯一情况。不要依赖被调用的复制构造函数,编译器可能会优化这些调用。

对于其他所有内容,“假设”规则都适用:编译器可以随心所欲地优化,只要可见的副作用与编译器根本没有优化相同。

(例如,“可见的副作用”包括写入控制台或文件系统的内容,但不包括运行时和 CPU 风扇速度。

评论

0赞 Robᵩ 9/22/2011
+1:“副作用明显改变。可能值得扩展一下可见的副作用到底是什么。
1赞 Matthieu M. 9/22/2011
我要指出的是,这是在引入 r 值之前。这允许优化。现在 r 值已经存在了,这些优化可能不那么相关了(复制省略仍然比调用移动构造函数更快,但不是以相同的幅度),但保留了该行为,不是为了向后兼容,而是因为它仍然提供了好处,并且人们已经学会了无论如何都不要使用复制构造函数来解决问题。
0赞 sbi 9/23/2011
@Rob:这足以满足你的口味吗?
0赞 sbi 9/23/2011
@Matthieu:这确实是一个非常有价值的补充。
0赞 Luchian Grigore 9/23/2011
谢谢,这正是我一直在寻找的答案。我知道复制构造函数可以跳过,我只是想知道是否存在其他情况。
1赞 sharptooth 9/22/2011 #2

这将取决于如何实现,编译器是否能看到实现以及它是否足够智能。例如,如果 in 有一些副作用,例如优化 out 会改变程序行为,这是不可能的。class Aoperator=()class A

3赞 Roman R. 9/22/2011 #3

它可能是优化的,是的。但是你仍然对这个过程有一定的控制权,例如,假设代码:

int x = 1;
x = 1;
x = 1;
x = 1;
volatile int y = 1;
y = 1;
y = 1;
y = 1;

如果在此片段下既不使用 x 也不使用 y,VS 2010 将生成代码:

    int x = 1;
    x = 1;
    x = 1;
    x = 1;
    volatile int y = 1;
010B1004  xor         eax,eax  
010B1006  inc         eax  
010B1007  mov         dword ptr [y],eax  
    y = 1;
010B100A  mov         dword ptr [y],eax  
    y = 1;
010B100D  mov         dword ptr [y],eax  
    y = 1;
010B1010  mov         dword ptr [y],eax  

也就是说,优化用“x”去除所有行,用“y”保留所有四行。这就是 volatile 的工作方式,但关键是你仍然可以控制编译器为你做什么。

无论是类还是基元类型,都取决于编译器,它的优化上限有多复杂。

另一个用于研究的代码片段:

class A
{
private:
    int c;

public:
    A(int b)
    {
        *this = b;
    }
    A& operator = (int b)
    {
        c = b;
        return *this;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    int b = 0;
    A a = b;
    a = b;
    a = b;
    return 0;
}

Visual Studio 2010 优化将所有代码剥离为零,在具有“完全优化”的发布版本中_tmain不执行任何操作并立即返回零。

-1赞 owen gerig 9/22/2011 #4

我对 c++ 了解不多,但目前正在阅读编译器原理、技术和工具

以下是他们关于代码优化的部分的片段:

独立于机器的代码优化阶段尝试改进 中间代码,以便生成更好的目标代码。通常 更好意味着更快,但可能需要其他目标,例如 较短的代码,或消耗较少功率的目标代码。例如 简单的算法使用 树表示中每个运算符的指令 来自语义分析器。简单的中间代码生成 算法后跟代码优化是一种合理的方法 生成良好的目标代码。Optimizar 可以推断 可以将 60 从整数转换为浮点数一次,并且 在编译时,可以消除 inttofloat 操作 通过将整数 6- 替换为浮点数 60.0。 此外,T3 仅使用一次将其值转换为 ID1,因此 优化器可以将 1.3 转换为更短的序列 (1.4)

1.3
t1 - intoffloat(60
t2 -- id3 * id1
ts -- id2 + t2
id1 t3

1.4
t1=id3 * 60.0
id1 = id2 + t1

总而言之,我的意思是说代码优化应该在更深层次上进行,因为代码处于如此简单的状态,所以不会影响代码的功能

0赞 Emilio Garavaglia 9/23/2011 #5

优化不会(用正确的术语)“删除对复制或分配的调用”。 它将有限状态机转换为另一个有限状态,即具有相同外部行为的机器。

现在,如果你废除调用

a=b; a=b; a=b; 

编译器执行的操作取决于实际执行的操作。 如果编译器发现调用没有机会更改程序的状态(并且“程序状态”是“所有内容都比作用域可以访问的作用域长”),它将将其剥离。 如果无法“证明”这一点,则调用将保持不变。operator=

无论编译器会做什么,都不要太担心:编译器不能(通过合同)改变程序或程序的一部分的外部逻辑。

-1赞 Frigo 9/24/2011 #6

我在 const 变量和 .编译器在用于计算其他内容时会产生不正确的结果。const 变量被优化掉了,它的旧值被做成一个编译时常量。真正的“意外行为”。好吧,也许不是;)const_cast

例:

const int x = 2;
const_cast<int&>(x) = 3;
int y = x * 2;
cout << y << endl;

评论

0赞 underscore_d 4/18/2016
这没有什么出乎意料的。 仅用于丢弃引用或指针,指向未声明的引用对象(例如,在处理错误的 API 或采用定义一对调用的快捷方式时)。相比之下,该标准非常刻意地指出,抛弃最初声明的变量的性是未定义的行为。const_castconstconstoperatorconstconst