gpt4 book ai didi

c++ - 为什么编译器并不总是优化掉局部变量?

转载 作者:行者123 更新时间:2023-12-04 15:29:05 29 4
gpt4 key购买 nike

我试图了解删除局部中间变量是否会导致更好的优化代码。考虑下面的MWE,特别注意两个函数fg :

struct A {
double d;
};

struct B {
double s;
};

struct C {
A a;
B b;
};

A geta();
B getb();

C f() {
const A a = geta();
const B b = getb();

C c;
c.a = a;
c.b = b;
return c;
}

C g() {
C c;
c.a = geta();
c.b = getb();
return c;
}

两者 fg调用 geta()getb()填充类的实例 C然后返回,但是 f使用两个局部中间变量来存储 geta()的返回值和 getb() , 而 g将返回值直接赋值给 c的成员.

编译 gcc -O3 ,版本 9.2,两个函数的二进制文件 fg完全一样。但是,将另一个变量添加到 AB类导致 不同 二进制文件。特别是 f 的二进制文件还有一些说明。对于带有 -O3 的 clang v8.0.0 也是如此旗帜。

这里发生了什么?为什么编译器不能优化掉 f 的局部中间变量当 AB变得更复杂一点?不是 f的代码吗和 g相等的?

此外,MSVC v19.22 的行为与 /O2 不同。标志:微软的编译器在第一种情况下已经有不同的二进制文件,即两个类 AB由单个 double 组成.

我正在使用 Godbolt:你可以找到 here产生不同二进制文件的代码。

最佳答案

这是一个错过的优化

两个函数都不接受 C c 的地址所以逃逸分析应该很容易证明它是一个纯粹的本地,没有其他东西可以指向。 geta()getb()不能直接读取或写入该变量,因此存储 geta() 是安全的。直接返回值到c.a而不是堆栈上的临时。

令人惊讶的是 GCC、clang、ICC 和 MSVC 都错过了这个优化,大多数使用保留调用的寄存器来保存 geta()返回值直到之后 getb() . https://godbolt.org/z/WQ9MAF至少适用于 x86-64;我大多没有检查其他 ISA 或更旧的编译器版本。

有趣的事实:clang 3.5 甚至对 g() 也有这种错过的优化,打败了源代码试图提高效率的尝试。

有趣的事实 #2:使用 GCC9.2,编译为 C 而不是 C++ 会使 GCC 做得更糟,去优化 g() . (我不得不改为 typedef struct Atag {...} A; 但编译它作为 C++ 仍然优化 g() . https://godbolt.org/z/_Y95nj )

clang8.0 产生高效 g()有/无 -xc .和 ICC 产生效率低下的 g()无论哪种方式。

ICC的f()甚至比它的g()还要糟糕.

MSVC g()与您所希望的一样高效; Windows x64 调用约定通过隐藏指针返回结构,MSVC 从不优化它以将指针传递给它自己的返回值对象。 (无论如何它可能无法证明是安全的,如果它自己的调用者也有可能进行此类优化。)

显然如果 geta()getb()可以内联,这消除了优化器的任何疑问,它应该更容易/可靠地进行优化。

关于c++ - 为什么编译器并不总是优化掉局部变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57902236/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com