__attribute((const))的gcc行为不一致

安托万

在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; \
    }

最后,我如下测试不同的运算符。注释使输出与g++-4.8 -std=c++11 -O2 -Wall -pedantic-O3的输出相同-Ofast

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似乎都在编译时预先计算了。

我们

编译器不信任您。由于您具有引用参数,因此编译器似乎并不信任您的const属性-const函数应该仅查看通过参数传递的值(而不是引用或取消引用指针)。

另一种测试方法是const在单独的编译单元中分解功能:

test1.cpp:

#include <stdio.h>
int global = 0; // forces results to be computed

int f(int i) __attribute((const));
void print_count(void);

#define TestOp(OP) \
    { \
        int n = 0; \
        global += (f(n) OP f(n)); \
        printf("op %s ", #OP);\
        print_count();\
    }

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;
}

counter.cpp:

#include <stdio.h>
static int counter = 0;

int f(int i) {
    ++counter;
    return 0;
}

void print_count(void)
{
   printf("counter %d\n", counter);
    counter = 0;
}

现在,编译器发现无需调用f(0)直到f(0) | f(0),并且该调用的结果将f(0)在其他情况下重复使用。

$ g++ -O2 -c counter.cpp && g++ -O2 -c test.cpp && g++ counter.o test.o && ./a.out
op ^ counter 0
op - counter 0
op | counter 1
op & counter 0
op || counter 0
op && counter 0
op + counter 0
op * counter 0

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章