【问题标题】:Constant combining in optimizing compilers优化编译器中的常量组合
【发布时间】:2011-02-15 22:22:47
【问题描述】:

我有一个包含许多小内联函数的头文件。他们中的大多数碰巧都有恒定的数据。由于这些函数对性能至关重要,因此它们处理常量的方式变得很重要。 AFAIK 有两种方法可以引用常量:

1) 在一个单独的源文件中定义它们,该文件稍后会与应用程序链接。

2) 就地定义常量。

我会选择后一种方式,因为它更易于维护。但是,如果编译器不优化通过内联创建的数千个相等常量,它可能会更慢。

问题:

编译器会合并这些相等的常量吗?具体来说,将使用以下哪种方法?

1) 在编译单元中组合相等的常量。
2) 跨链接模块(整个程序或库)组合相等的常量
3) 将常量与 any 具有相同位模式并满足编译单元或整个程序的对齐要求的静态常量数据组合。

我使用现代编译器 (GCC4.5)。

我不是汇编专家,因此我无法通过几个简单的测试自己回答这个问题:)

编辑:

常量相当大(大部分至少 16 字节),因此编译器无法将它们设为立即值。

EDIT2:

代码示例

这个使用就地常量:

float_4 sign(float_4 a)
{
    const __attribute__((aligned(16))) float mask[4] = { //I use a macro for this line
        0x80000000, 0x80000000, 0x80000000, 0x80000000};
    const int128 mask = load(mask);
    return b_and(a, mask);
}

【问题讨论】:

  • “就地定义常量”是什么意思?喜欢const int i = 5;
  • 是的。我所说的常量是指任何独立于函数参数的数据。
  • 我不是 GCC 专家,但大多数编译器根本不组合 const 文字,除非它们是字符串文字。此外,const 文字通常作为立即值嵌入代码中。将值加载到内存变量中后,由编译器强制执行该值的 const-ness。
  • 我认为您对使用 const 数据如何影响性能感到困惑;例如在“由于这些函数对性能至关重要,因此它们处理常量的方式变得很重要。”并且在“但是,如果编译器不优化通过内联创建的数千个相等常量,它可能会更慢。”

标签: c++ c gcc constants inline


【解决方案1】:

根据GCC,以下选项可以满足您的需求:

-fmerge-constants

尝试跨编译单元合并相同的常量(字符串常量和浮点常量)。 如果汇编器和链接器支持,则此选项是优化编译的默认选项。使用 -fno-merge-constants 来禁止这种行为。
在 -O、-O2、-O3、-Os 级别启用。

【讨论】:

    【解决方案2】:

    如果你像这样在头文件中定义常量:

    int const TEN = 10;
    // or
    enum { ELEVEN = 11 };
    

    也就是说,在编译翻译单元(.cc 源文件)时,编译器不仅可以看到常量声明,而且定义也是可见的,那么编译器肯定会在生成的代码中用常量值替换它,即使是未启用优化。

    [max@truth test]$ cat test.cc
    int const TEN = 10; // definition available
    extern int const TWELVE; // only declaration
    
    int foo(int x) { return x + TEN; }
    int bar(int x) { return x + TWELVE; }
    
    [max@truth test]$ g++ -S -o - test.cc | c++filt | egrep -v " *\."
    foo(int):
        pushq   %rbp
        movq    %rsp, %rbp
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    $10, %eax
        leave
        ret
    bar(int):
        pushq   %rbp
        movq    %rsp, %rbp
        movl    %edi, -4(%rbp)
        movl    TWELVE(%rip), %eax
        addl    -4(%rbp), %eax
        leave
        ret
    TEN:
    

    注意在foo(int) 中它是如何以addl $10, %eax 进行加法运算的,即 TEN 常量被其值替换。另一方面,在bar(int) 中,它首先执行movl TWELVE(%rip), %eax 将TWELVE 的值从内存加载到eax 寄存器(地址将由链接器解析),然后添加addl -4(%rbp), %eax

    优化后的版本如下所示:

    [max@truth test]$ g++ -O3 -S -o - test.cc | c++filt | egrep -v " *\."
    foo(int):
        leal    10(%rdi), %eax
        ret
    bar(int):
        movl    TWELVE(%rip), %eax
        addl    %edi, %eax
        ret
    

    【讨论】:

      【解决方案3】:

      我认为您的问题没有一般性的答案。我只给 C 一个,C++ 的规则不同。

      这在很大程度上取决于常量的类型。一个重要的类是“整数常量表达式”。这些可以在编译时确定,特别是用作“整数枚举常量”的值。随时使用它

      enum { myFavoriteDimension = 55/2 };
      

      对于这样的常量,最好的事情通常应该发生:它们被实现为汇编立即数。它们甚至没有存储位置,直接写入汇编器,你的问题甚至没有意义。

      对于其他数据类型,问题更为微妙。尝试强制执行不占用“const 限定变量”的地址。这可以通过 register 关键字来完成。

      register double const something = 5.7;
      

      可能和上面的效果一样。

      对于组合类型(structunion、数组),没有通用的答案或方法。我已经看到 gcc 能够完全优化小型数组(10 个元素左右)。

      【讨论】:

        猜你喜欢
        • 2012-09-06
        • 2013-08-15
        • 1970-01-01
        • 2017-05-04
        • 2011-05-06
        • 1970-01-01
        • 1970-01-01
        • 2011-02-10
        • 2011-11-07
        相关资源
        最近更新 更多