【发布时间】:2013-12-02 13:28:36
【问题描述】:
我在 gcc 中遇到了一个非常奇怪的行为,涉及标记为 __attribute((const)) 的运算符和函数。逻辑和算术运算符导致不同的优化,我不明白为什么。
这并不是一个真正的错误,因为__attribute((const)) 只是一个提示,并不能保证它的效果,但这仍然是非常令人惊讶的。有人解释一下吗?
这是代码。所以我定义了一个__attribute((const)) 函数:
int f(int & counter) __attribute((const));
int f(int & counter) {
++counter;
return 0;
}
然后我定义一个算子测试宏。这是通过宏而不是模板/仿函数来完成的,以便向编译器提供简单的代码并简化优化:
int global = 0; // forces results to be computed
#define TestOp(OP) \
{ \
int n = 0; \
global += (f(n) OP f(n)); \
std::cout << "op" #OP " calls f " << n << " times" << std::endl; \
}
最后,我测试不同的运算符如下。 cmets 在-O3 和-Ofast 处将输出与g++-4.8 -std=c++11 -O2 -Wall -pedantic 相同的输出匹配
int main() {
// all calls optimized away
TestOp(^) // 0
TestOp(-) // 0
// one call is optimized away
TestOp(|) // 1
TestOp(&) // 1
TestOp(||) // 1
TestOp(&&) // 1
// no optimization
TestOp(+) // 2
TestOp(*) // 2
return global;
}
我的问题是:为什么算术运算符会产生两个调用?为什么 f()+f() 不能优化为 2*f() ?有没有办法帮助/强制这种优化?
起初我认为乘法可能更昂贵,但我尝试了f()+....+f(),10 次加法仍然没有减少到10*f()。此外,由于它是int 算术,因此操作顺序无关紧要(与floats 相反)。
我还检查了 asm,但没有帮助:所有 int 似乎都是在编译时预先计算的。
【问题讨论】:
-
你可以试试 O3
-
@PlasmaHH:不会改变
-
文档警告说 const 函数不能检查指针参数,我认为对于引用来说这将是相同的(更不用说更改传入的任何参数 - const 应该是甚至比纯属性更严格)。
-
@nos 好吧,显然没有“合法”的方式来调试 const 函数,所以我坚持这一点。但这不是问题,因为没有检查这些属性,编译器应该相信我。
-
@Antonie 好吧,如果你创建你的函数
int f(int i) __attribute((const));,将实现放在不同的编译单元中(它会改变一个全局变量来跟踪计数),我会得到完全不同的结果 -只有 TestOp(|) 执行 1 次调用,其他都被优化掉,这将是调用 f(0) 时的预期情况。
标签: c++ gcc compiler-optimization function-attributes