考虑下面的代码片段
#include <vector>
#include <cstdlib>
void __attribute__ ((noinline)) calculate1(double& a, int x) { a += x; };
void __attribute__ ((noinline)) calculate2(double& a, int x) { a *= x; };
void wrapper1(double& a, int x) { calculate1(a, x); }
void wrapper2(double& a, int x) { calculate2(a, x); }
typedef void (*Func)(double&, int);
int main()
{
std::vector<std::pair<double, Func>> pairs = {
std::make_pair(0, (rand() % 2 ? &wrapper1 : &wrapper2)),
std::make_pair(0, (rand() % 2 ? &wrapper1 : &wrapper2)),
};
for (auto& [a, wrapper] : pairs)
(*wrapper)(a, 5);
return pairs[0].first + pairs[1].first;
}
通过-O3优化,最新的gcc和clang版本没有优化指向包装器的指针、指向底层函数的指针。参见第22行的组装:
mov ebp, OFFSET FLAT:wrapper2(double&, int) # tmp118,
在后面的call
+JMP
中,编译器将指针放置到calculate1
,而不只是call
。
编辑2。同样模式的更简单的例子:
#include <vector>
#include <cstdlib>
typedef void (*Func)(double&, int);
static void __attribute__ ((noinline)) calculate(double& a, int x) { a += x; };
static void wrapper(double& a, int x) { calculate(a, x); }
int main() {
double a = 5.0;
Func f;
if (rand() % 2)
f = &wrapper; // f = &calculate;
else
f = &wrapper;
f(a, 0);
return 0;
}
gcc 8.2通过抛出指向包装器的指针并将&calculate
直接存储在其位置(https://gcc.godbolt.org/z/nmibeo)成功地优化了这段代码。然而,按照注释更改代码行(即手动执行相同优化的一部分)打破了这种魔力,并导致毫无意义的JMP
。
您似乎建议将&calculate1
而不是&wrapper1
存储在向量中。通常,这是不可能的:以后的代码可能会尝试将存储的指针与&calculate1
进行比较,结果必须比较为false。
我进一步假设您的建议是,编译器可能会尝试做一些静态分析,并确定向量中的函数指针值从未与其他函数指针进行相等性比较,事实上,对向量元素所做的其他操作都不会产生可观察行为的变化;因此,在这个精确的程序中,它可以存储&calculate1
。
通常,对于“为什么编译器不执行某些特定的优化”的回答是,没有人设想并实现过这个想法。另一个常见的原因是,在一般情况下,所涉及的静态分析相当困难,可能会导致编译速度放缓,而在无法保证分析成功的实际程序中没有任何好处。
我经常注意到gcc在可执行文件中将乘法转换为移位。当将与相乘时,可能会发生类似的情况。例如,可能只是将的指数增加1,从而节省一些周期。如果有人要求编译器这样做(例如,通过),编译器通常会这样做吗? 编译器通常是否足够聪明来执行此操作,还是我需要自己使用或函数系列来执行此操作?
(这个问题与此密切相关,但它是一个更具体的问题,我希望能就此得到答案)
Nicolai Josuttis在其著作《C标准库(第二版)》中指出,与普通函数相比,编译器可以更好地优化lambdas。 此外,C 编译器优化 lambda 的效果比普通函数更好。(第213页) 这是为什么呢? 我想当涉及到内联时,应该不会再有任何区别了。我能想到的唯一原因是编译器可能有更好的lambdas本地上下文,这样可以做出更多假设并执行更多优化。
问题内容: 假设我在C代码中有类似的内容。我知道您可以使用a 代替,以使编译器不对其进行编译,但是出于好奇,我问编译器是否也可以解决此问题。 我认为这对于Java编译器来说更为重要,因为它不支持。 问题答案: 在Java中,if内的代码甚至都不是已编译代码的一部分。它必须编译,但不会写入已编译的字节码。它实际上取决于编译器,但我不知道没有对它进行优化的编译器。规则在JLS中定义: 优化的编译器可能
如果关闭了编译器优化(gcc-o0...),那么说'volatile'关键字没有区别是可以的吗? 我制作了一些示例“C”程序,并且仅当打开编译器优化时,才在生成的汇编代码中看到易失性和非易失性之间的区别,即((gcc-o1....)。
今天我遇到了一些我不太理解的复制构造函数。 考虑下一个代码: 然后在将原点分配给复制时调用复制构造函数,这是有意义的。但是,如果我将复制的声明更改为 它不叫。即使当我使用< code>create()函数时,它也不会调用复制构造函数。但是,当将其更改为 它确实调用了复制构造函数。一些研究解释说,允许编译器优化复制构造函数,这听起来是件好事。直到复制构造函数是非默认的,因为那时它可能会,也可能不会做